Refactoring: new multi app structure
@ -3,4 +3,5 @@ from django.apps import AppConfig
|
||||
|
||||
class AccountConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'account'
|
||||
name = 'apps.account'
|
||||
verbose_name = 'Auth and account management'
|
@ -14,7 +14,7 @@ app_name = 'account'
|
||||
urlpatterns = [
|
||||
# WEB LOGOUT:
|
||||
path(
|
||||
'accounts/logout/',
|
||||
'account/logout/',
|
||||
LogoutView.as_view(next_page=settings.LOGOUT_REDIRECT_URL),
|
||||
name='logout'
|
||||
)
|
@ -5,7 +5,7 @@ from django_filters.rest_framework import (
|
||||
)
|
||||
from django_filters import widgets
|
||||
|
||||
from .models import Archive, Ticket
|
||||
from apps.collector.models import Archive, Ticket
|
||||
from .utils import DateTimeFilterMixin
|
||||
|
||||
|
@ -3,7 +3,7 @@ from rest_framework import serializers
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from drf_spectacular.openapi import OpenApiTypes
|
||||
|
||||
from .models import Archive, Platform, Ticket
|
||||
from apps.collector.models import Archive, Platform, Ticket
|
||||
|
||||
|
||||
@extend_schema_field(OpenApiTypes.NUMBER)
|
21
logs_collector/apps/collector/api/urls.py
Normal file
@ -0,0 +1,21 @@
|
||||
from django.urls import path, include
|
||||
|
||||
from rest_framework import routers
|
||||
|
||||
from . import views
|
||||
|
||||
# ▄▀█ █▀█ █
|
||||
# █▀█ █▀▀ █
|
||||
# -- -- --
|
||||
|
||||
app_name = 'collector_api'
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'archives', views.ArchiveViewSet)
|
||||
router.register(r'platforms', views.PlatformViewSet)
|
||||
router.register(r'tickets', views.TicketViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
# CRUD:
|
||||
path('v1/', include(router.urls)),
|
||||
]
|
20
logs_collector/apps/collector/api/utils.py
Normal file
@ -0,0 +1,20 @@
|
||||
from django_filters import NumberFilter
|
||||
|
||||
|
||||
class DateTimeFilterMixin:
|
||||
year__gte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='year__gte'
|
||||
)
|
||||
year__lte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='year__lte'
|
||||
)
|
||||
month__gte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='month__gte'
|
||||
)
|
||||
month__lte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='month__lte'
|
||||
)
|
@ -1,9 +1,4 @@
|
||||
from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import FileResponse
|
||||
from django.views import generic
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.db.models import Q
|
||||
|
||||
from rest_framework import status
|
||||
# from rest_framework.decorators import action
|
||||
@ -15,14 +10,10 @@ from rest_framework import filters
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
||||
from two_factor.views import OTPRequiredMixin
|
||||
from apps.collector.models import Archive, Ticket, Platform # ???????
|
||||
|
||||
from .models import Archive, Ticket, Platform
|
||||
from .forms import TicketForm
|
||||
from .filters import ArchiveFilter, TicketFilter
|
||||
from .utils import PageTitleViewMixin
|
||||
from .permissions import IsGuestUpload
|
||||
|
||||
from .serializers import (
|
||||
PublicArchiveUploadSerializer,
|
||||
ArchiveSerializer,
|
||||
@ -31,104 +22,6 @@ from .serializers import (
|
||||
)
|
||||
|
||||
|
||||
class ArchiveHandlerView(
|
||||
OTPRequiredMixin,
|
||||
LoginRequiredMixin,
|
||||
SingleObjectMixin,
|
||||
generic.View):
|
||||
model = Archive
|
||||
slug_field = 'file'
|
||||
slug_url_kwarg = 'path'
|
||||
|
||||
def get(self, request, path):
|
||||
self.object = self.get_object()
|
||||
return FileResponse(self.object.file)
|
||||
|
||||
|
||||
class CreateTicket(LoginRequiredMixin, PageTitleViewMixin, generic.CreateView):
|
||||
model = Ticket
|
||||
form_class = TicketForm
|
||||
template_name = 'collector/ticket_create.html'
|
||||
|
||||
def get_title(self):
|
||||
return f'{self.title} - create'
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = self.request.user
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class UpdateTicket(LoginRequiredMixin, PageTitleViewMixin, generic.UpdateView):
|
||||
model = Ticket
|
||||
form_class = TicketForm
|
||||
template_name = 'collector/ticket_create.html'
|
||||
slug_field = 'number'
|
||||
slug_url_kwarg = 'ticket'
|
||||
|
||||
def get_title(self, **kwargs):
|
||||
return f'{self.title} - {self.kwargs.get("ticket", "update")}'
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = self.request.user
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ListAllTickets(LoginRequiredMixin, PageTitleViewMixin, generic.ListView):
|
||||
model = Ticket
|
||||
template_name = 'collector/tickets.html'
|
||||
context_object_name = 'tickets'
|
||||
paginate_by = 5
|
||||
title = 'Collector - tickets'
|
||||
|
||||
def get_queryset(self):
|
||||
search_query = self.request.GET.get('search', '')
|
||||
if search_query:
|
||||
query_list = []
|
||||
try:
|
||||
for item in search_query.split(','):
|
||||
query_list.append(int(item))
|
||||
except ValueError:
|
||||
return super().get_queryset()
|
||||
queryset = self.model.objects.filter(
|
||||
Q(number__in=query_list) | Q(number__icontains=query_list[0])
|
||||
)
|
||||
self.paginate_by = 100 # ? fake disable pagination)
|
||||
return queryset
|
||||
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class ListPlatformTickets(
|
||||
LoginRequiredMixin,
|
||||
PageTitleViewMixin,
|
||||
generic.ListView
|
||||
):
|
||||
model = Ticket
|
||||
template_name = 'collector/tickets.html'
|
||||
context_object_name = 'tickets'
|
||||
# allow_empty = False
|
||||
paginate_by = 5
|
||||
|
||||
def get_title(self, **kwargs):
|
||||
return f'{self.title} - {self.kwargs.get("platform", "tickets")}'
|
||||
|
||||
def get_queryset(self):
|
||||
return Ticket.objects.filter(
|
||||
platform__name=self.kwargs.get('platform')
|
||||
)
|
||||
|
||||
|
||||
class DetailTicket(LoginRequiredMixin, PageTitleViewMixin, generic.DetailView):
|
||||
model = Ticket
|
||||
template_name = 'collector/ticket.html'
|
||||
context_object_name = 'ticket'
|
||||
slug_field = 'number'
|
||||
slug_url_kwarg = 'ticket'
|
||||
|
||||
def get_title(self, **kwargs):
|
||||
return f'{self.title} - {self.kwargs.get("ticket", "show")}'
|
||||
|
||||
|
||||
class ArchiveViewSet(viewsets.ModelViewSet):
|
||||
queryset = Archive.objects.order_by('-time_create')
|
||||
serializer_class = ArchiveSerializer
|
@ -3,4 +3,5 @@ from django.apps import AppConfig
|
||||
|
||||
class CollectorConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'collector'
|
||||
name = 'apps.collector'
|
||||
verbose_name = 'Collector archives for analyse'
|
@ -1,11 +1,13 @@
|
||||
# Generated by Django 4.2 on 2023-07-28 14:40
|
||||
# Generated by Django 4.2 on 2023-08-14 09:07
|
||||
|
||||
import collector.utils
|
||||
import apps.collector.utils
|
||||
from django.conf import settings
|
||||
import django.core.files.storage
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import pathlib
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@ -21,7 +23,7 @@ class Migration(migrations.Migration):
|
||||
name='Platform',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=20)),
|
||||
('name', models.CharField(max_length=20, unique=True)),
|
||||
('pretty_name', models.CharField(max_length=20)),
|
||||
],
|
||||
),
|
||||
@ -29,26 +31,29 @@ class Migration(migrations.Migration):
|
||||
name='Ticket',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('number', models.IntegerField()),
|
||||
('number', models.IntegerField(db_index=True, unique=True)),
|
||||
('resolved', models.BooleanField(default=False)),
|
||||
('note', models.TextField(blank=True)),
|
||||
('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
|
||||
('attempts', models.IntegerField(default=5, validators=[django.core.validators.MaxValueValidator(10), django.core.validators.MinValueValidator(0)])),
|
||||
('time_create', models.DateTimeField(auto_now_add=True)),
|
||||
('time_update', models.DateTimeField(auto_now=True)),
|
||||
('platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='collector.platform')),
|
||||
('platform', models.ForeignKey(db_column='platform_name', on_delete=django.db.models.deletion.CASCADE, to='collector.platform', to_field='name')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-time_create'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Archive',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('file', models.FileField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(base_url='/archives/', location=pathlib.PurePosixPath('/home/stepan/Documents/Dev/ISPsystem/logs-collector/logs_collector/archives')), upload_to=collector.utils.logs_dir_path)),
|
||||
('size', models.CharField(blank=True, max_length=50)),
|
||||
('sha1', models.CharField(editable=False, max_length=1024)),
|
||||
('file', models.FileField(blank=True, null=True, storage=django.core.files.storage.FileSystemStorage(base_url='/archives/', location=pathlib.PurePosixPath('/home/stepan/Documents/Dev/ISPsystem/logs-collector/logs_collector/archives')), upload_to=apps.collector.utils.logs_dir_path)),
|
||||
('md5', models.CharField(editable=False, max_length=1024)),
|
||||
('time_create', models.DateTimeField(auto_now_add=True)),
|
||||
('time_update', models.DateTimeField(auto_now=True)),
|
||||
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='collector.ticket')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('ticket', models.ForeignKey(db_column='ticket_number', on_delete=django.db.models.deletion.CASCADE, to='collector.ticket', to_field='number')),
|
||||
],
|
||||
),
|
||||
]
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 495 B |
Before Width: | Height: | Size: 930 B After Width: | Height: | Size: 930 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
30
logs_collector/apps/collector/templates/collector/base.html
Normal file
@ -0,0 +1,30 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block collector_head %}
|
||||
<title>{% block title %}{% endblock title %}</title>
|
||||
{% endblock collector_head %}
|
||||
|
||||
{% block collector_content %}
|
||||
<header>
|
||||
<section>
|
||||
{% include 'includes/navigation.html' %}
|
||||
</section>
|
||||
</header>
|
||||
<main>
|
||||
<section>
|
||||
{% block main %}{% endblock main %}
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<section>
|
||||
{% block footer %}{% endblock footer %}
|
||||
</section>
|
||||
</footer>
|
||||
{% endblock collector_content %}
|
||||
|
||||
{% block collector_scripts %}
|
||||
<script src="{% static 'collector/js/jquery-3.7.0.min.js' %}"></script>
|
||||
{% block bs %}{% endblock bs %}
|
||||
{% block jquery %}{% endblock jquery %}
|
||||
{% endblock collector_scripts %}
|
@ -30,7 +30,7 @@
|
||||
>Cancel
|
||||
</button>
|
||||
<a
|
||||
href="{% url 'collector:archive-detail' archive.id %}"
|
||||
href="{% url 'collector_api:archive-detail' archive.id %}"
|
||||
type="button"
|
||||
class="btn btn-danger btn-archive-eraser"
|
||||
data-bs-dismiss="modal"
|
@ -27,7 +27,7 @@
|
||||
</button>
|
||||
<a
|
||||
type="button"
|
||||
href="{% url 'collector:ticket-detail' ticket.number %}"
|
||||
href="{% url 'collector_api:ticket-detail' ticket.number %}"
|
||||
class="btn btn-danger btn-ticket-del"
|
||||
data-bs-dismiss="modal"
|
||||
data-jq-ticket-del-target="#div-ticket-{{ ticket.number }}"
|
@ -9,7 +9,7 @@
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
name="ticket-state"
|
||||
ticket-state-url="{% url 'collector:ticket-detail' ticket.number %}"
|
||||
ticket-state-url="{% url 'collector_api:ticket-detail' ticket.number %}"
|
||||
{% if ticket.resolved %} ticket-state-switch="1" {% endif %}
|
||||
{% if ticket.resolved %} checked {% endif %}>
|
||||
</div>
|
@ -2,7 +2,7 @@ import markdown as md
|
||||
from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
|
||||
from collector.models import Platform
|
||||
from apps.collector.models import Platform
|
||||
|
||||
|
||||
register = template.Library()
|
@ -1,6 +1,4 @@
|
||||
from django.urls import path, include
|
||||
|
||||
from rest_framework import routers
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
@ -50,17 +48,3 @@ urlpatterns = [
|
||||
name='update'
|
||||
),
|
||||
]
|
||||
|
||||
# ▄▀█ █▀█ █
|
||||
# █▀█ █▀▀ █
|
||||
# -- -- --
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'archives', views.ArchiveViewSet)
|
||||
router.register(r'platforms', views.PlatformViewSet)
|
||||
router.register(r'tickets', views.TicketViewSet)
|
||||
|
||||
urlpatterns += [
|
||||
# CRUD:
|
||||
path('api/v1/', include(router.urls)),
|
||||
]
|
@ -1,5 +1,4 @@
|
||||
import os
|
||||
from django_filters import NumberFilter
|
||||
|
||||
|
||||
def logs_dir_path(instance, filename):
|
||||
@ -10,6 +9,7 @@ def logs_dir_path(instance, filename):
|
||||
return f'{instance.ticket.number}/{filename}'
|
||||
|
||||
|
||||
# deprecated
|
||||
def get_file_size(file_path, unit='bytes'):
|
||||
file_size = os.path.getsize(file_path)
|
||||
exponents_map = {'bytes': 0, 'kb': 1, 'mb': 2, 'gb': 3}
|
||||
@ -21,6 +21,7 @@ def get_file_size(file_path, unit='bytes'):
|
||||
return round(size, 3)
|
||||
|
||||
|
||||
# deprecated
|
||||
def is_ajax(request):
|
||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||
return True
|
||||
@ -40,22 +41,3 @@ class PageTitleViewMixin:
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = self.get_title()
|
||||
return context
|
||||
|
||||
|
||||
class DateTimeFilterMixin:
|
||||
year__gte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='year__gte'
|
||||
)
|
||||
year__lte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='year__lte'
|
||||
)
|
||||
month__gte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='month__gte'
|
||||
)
|
||||
month__lte = NumberFilter(
|
||||
field_name='time_create',
|
||||
lookup_expr='month__lte'
|
||||
)
|
109
logs_collector/apps/collector/views.py
Normal file
@ -0,0 +1,109 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import FileResponse
|
||||
from django.views import generic
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.db.models import Q
|
||||
|
||||
from two_factor.views import OTPRequiredMixin
|
||||
|
||||
from .forms import TicketForm
|
||||
from .models import Archive, Ticket
|
||||
from .utils import PageTitleViewMixin
|
||||
|
||||
|
||||
class ArchiveHandlerView(
|
||||
OTPRequiredMixin,
|
||||
LoginRequiredMixin,
|
||||
SingleObjectMixin,
|
||||
generic.View):
|
||||
model = Archive
|
||||
slug_field = 'file'
|
||||
slug_url_kwarg = 'path'
|
||||
|
||||
def get(self, request, path):
|
||||
self.object = self.get_object()
|
||||
return FileResponse(self.object.file)
|
||||
|
||||
|
||||
class CreateTicket(LoginRequiredMixin, PageTitleViewMixin, generic.CreateView):
|
||||
model = Ticket
|
||||
form_class = TicketForm
|
||||
template_name = 'collector/ticket_create.html'
|
||||
|
||||
def get_title(self):
|
||||
return f'{self.title} - create'
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = self.request.user
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class UpdateTicket(LoginRequiredMixin, PageTitleViewMixin, generic.UpdateView):
|
||||
model = Ticket
|
||||
form_class = TicketForm
|
||||
template_name = 'collector/ticket_create.html'
|
||||
slug_field = 'number'
|
||||
slug_url_kwarg = 'ticket'
|
||||
|
||||
def get_title(self, **kwargs):
|
||||
return f'{self.title} - {self.kwargs.get("ticket", "update")}'
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = self.request.user
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ListAllTickets(LoginRequiredMixin, PageTitleViewMixin, generic.ListView):
|
||||
model = Ticket
|
||||
template_name = 'collector/tickets.html'
|
||||
context_object_name = 'tickets'
|
||||
paginate_by = 5
|
||||
title = 'Collector - tickets'
|
||||
|
||||
def get_queryset(self):
|
||||
search_query = self.request.GET.get('search', '')
|
||||
if search_query:
|
||||
query_list = []
|
||||
try:
|
||||
for item in search_query.split(','):
|
||||
query_list.append(int(item))
|
||||
except ValueError:
|
||||
return super().get_queryset()
|
||||
queryset = self.model.objects.filter(
|
||||
Q(number__in=query_list) | Q(number__icontains=query_list[0])
|
||||
)
|
||||
self.paginate_by = 100 # ? fake disable pagination)
|
||||
return queryset
|
||||
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class ListPlatformTickets(
|
||||
LoginRequiredMixin,
|
||||
PageTitleViewMixin,
|
||||
generic.ListView
|
||||
):
|
||||
model = Ticket
|
||||
template_name = 'collector/tickets.html'
|
||||
context_object_name = 'tickets'
|
||||
# allow_empty = False
|
||||
paginate_by = 5
|
||||
|
||||
def get_title(self, **kwargs):
|
||||
return f'{self.title} - {self.kwargs.get("platform", "tickets")}'
|
||||
|
||||
def get_queryset(self):
|
||||
return Ticket.objects.filter(
|
||||
platform__name=self.kwargs.get('platform')
|
||||
)
|
||||
|
||||
|
||||
class DetailTicket(LoginRequiredMixin, PageTitleViewMixin, generic.DetailView):
|
||||
model = Ticket
|
||||
template_name = 'collector/ticket.html'
|
||||
context_object_name = 'ticket'
|
||||
slug_field = 'number'
|
||||
slug_url_kwarg = 'ticket'
|
||||
|
||||
def get_title(self, **kwargs):
|
||||
return f'{self.title} - {self.kwargs.get("ticket", "show")}'
|
@ -1,27 +0,0 @@
|
||||
# Generated by Django 4.2 on 2023-08-05 11:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collector', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='ticket',
|
||||
options={'ordering': ['-time_create']},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archive',
|
||||
name='size',
|
||||
field=models.CharField(blank=True, editable=False, max_length=50),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='number',
|
||||
field=models.IntegerField(db_index=True, unique=True),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# Generated by Django 4.2 on 2023-08-08 05:52
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collector', '0002_alter_ticket_options_alter_archive_size_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='archive',
|
||||
name='ticket',
|
||||
field=models.ForeignKey(db_column='ticket_number', on_delete=django.db.models.deletion.CASCADE, to='collector.ticket', to_field='number'),
|
||||
),
|
||||
]
|
@ -1,22 +0,0 @@
|
||||
# Generated by Django 4.2 on 2023-08-08 09:17
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collector', '0003_alter_archive_ticket'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='archive',
|
||||
old_name='sha1',
|
||||
new_name='md5',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='archive',
|
||||
name='size',
|
||||
),
|
||||
]
|
@ -1,33 +0,0 @@
|
||||
# Generated by Django 4.2 on 2023-08-08 11:16
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('collector', '0004_rename_sha1_archive_md5_remove_archive_size'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Token',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('expires', models.IntegerField(default=5, validators=[django.core.validators.MaxValueValidator(100), django.core.validators.MinValueValidator(1)])),
|
||||
('blocked', models.BooleanField(default=False)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='archive',
|
||||
name='token',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='collector.token'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,47 +0,0 @@
|
||||
# Generated by Django 4.2 on 2023-08-08 16:52
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collector', '0005_token_archive_token'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='archive',
|
||||
name='token',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='archive',
|
||||
name='user',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='token',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='upload',
|
||||
field=models.IntegerField(default=5, validators=[django.core.validators.MaxValueValidator(10), django.core.validators.MinValueValidator(1)]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='platform',
|
||||
name='name',
|
||||
field=models.CharField(max_length=20, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='platform',
|
||||
field=models.ForeignKey(db_column='platform_name', on_delete=django.db.models.deletion.CASCADE, to='collector.platform', to_field='name'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Token',
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.2 on 2023-08-08 17:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collector', '0006_remove_archive_token_remove_archive_user_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='ticket',
|
||||
old_name='upload',
|
||||
new_name='attempts',
|
||||
),
|
||||
]
|
@ -1,25 +0,0 @@
|
||||
# Generated by Django 4.2 on 2023-08-10 03:24
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('collector', '0007_rename_upload_ticket_attempts'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='attempts',
|
||||
field=models.IntegerField(default=5, validators=[django.core.validators.MaxValueValidator(10), django.core.validators.MinValueValidator(0)]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='token',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
|
||||
),
|
||||
]
|
@ -1,30 +0,0 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<head>
|
||||
{% include 'collector/includes/metalinks.html' %}
|
||||
<title>{% block title %}{% endblock title %}</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<section>
|
||||
{% include 'collector/includes/navigation.html' %}
|
||||
</section>
|
||||
</header>
|
||||
<main>
|
||||
<section>
|
||||
{% block main %}{% endblock main %}
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<section>
|
||||
{% block footer %}{% endblock footer %}
|
||||
</section>
|
||||
</footer>
|
||||
<script src="{% static 'collector/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'collector/js/bs.theme.mode.js' %}"></script>
|
||||
<script src="{% static 'collector/js/jquery-3.7.0.min.js' %}"></script>
|
||||
{% block bs %}{% endblock bs %}
|
||||
{% block jquery %}{% endblock jquery %}
|
||||
</body>
|
||||
</html>
|
@ -1,33 +0,0 @@
|
||||
{% load static %}
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{% static 'collector/css/bootstrap.min.css' %}"
|
||||
rel="stylesheet"
|
||||
>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="{% static 'collector/img/apple-touch-icon.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="{% static 'collector/img/favicon-32x32.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="{% static 'collector/img/favicon-16x16.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="manifest"
|
||||
href="{% static 'collector/img/site.webmanifest' %}"
|
||||
>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
|
||||
>
|
@ -35,8 +35,8 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'collector.apps.CollectorConfig', # main app
|
||||
'account.apps.AccountConfig', # account app
|
||||
'apps.collector.apps.CollectorConfig', # main app
|
||||
'apps.account.apps.AccountConfig', # account app
|
||||
'rest_framework',
|
||||
'rest_framework_simplejwt',
|
||||
'django_filters',
|
||||
@ -129,6 +129,9 @@ USE_TZ = True
|
||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / "static",
|
||||
]
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||
|
@ -11,7 +11,7 @@ from drf_spectacular.views import (
|
||||
from two_factor.urls import urlpatterns as tf_urls
|
||||
|
||||
from logs_collector import settings
|
||||
from account.utils import AdminSiteOTPRequiredMixinRedirectSetup
|
||||
from apps.account.utils import AdminSiteOTPRequiredMixinRedirectSetup
|
||||
|
||||
|
||||
# ? 2FA patch (Admin site protection)
|
||||
@ -19,12 +19,29 @@ admin.site.__class__ = AdminSiteOTPRequiredMixinRedirectSetup
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('collector.urls', namespace='collector')),
|
||||
path('', include(tf_urls)),
|
||||
path('', include('account.urls', namespace='account'))
|
||||
path(
|
||||
'admin/',
|
||||
admin.site.urls
|
||||
),
|
||||
path(
|
||||
'',
|
||||
include('apps.collector.urls', namespace='collector')
|
||||
),
|
||||
path(
|
||||
'',
|
||||
include(tf_urls)
|
||||
),
|
||||
path(
|
||||
'',
|
||||
include('apps.account.urls', namespace='account')
|
||||
),
|
||||
path(
|
||||
'api/',
|
||||
include('apps.collector.api.urls', namespace='collector_api')
|
||||
),
|
||||
]
|
||||
|
||||
# SWAGGER URLS:
|
||||
urlpatterns += [
|
||||
# API PATTERNS
|
||||
path('api/v1/schema/', SpectacularAPIView.as_view(), name='schema'),
|
||||
|
@ -1,836 +0,0 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Logs collector API
|
||||
version: 0.1.0
|
||||
description: Collector of archives with log files for further analysis
|
||||
paths:
|
||||
/api/schema/:
|
||||
get:
|
||||
operationId: schema_retrieve
|
||||
description: |-
|
||||
OpenApi3 schema for this API. Format can be selected via content negotiation.
|
||||
|
||||
- YAML: application/vnd.oai.openapi
|
||||
- JSON: application/vnd.oai.openapi+json
|
||||
parameters:
|
||||
- in: query
|
||||
name: format
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- json
|
||||
- yaml
|
||||
- in: query
|
||||
name: lang
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- af
|
||||
- ar
|
||||
- ar-dz
|
||||
- ast
|
||||
- az
|
||||
- be
|
||||
- bg
|
||||
- bn
|
||||
- br
|
||||
- bs
|
||||
- ca
|
||||
- ckb
|
||||
- cs
|
||||
- cy
|
||||
- da
|
||||
- de
|
||||
- dsb
|
||||
- el
|
||||
- en
|
||||
- en-au
|
||||
- en-gb
|
||||
- eo
|
||||
- es
|
||||
- es-ar
|
||||
- es-co
|
||||
- es-mx
|
||||
- es-ni
|
||||
- es-ve
|
||||
- et
|
||||
- eu
|
||||
- fa
|
||||
- fi
|
||||
- fr
|
||||
- fy
|
||||
- ga
|
||||
- gd
|
||||
- gl
|
||||
- he
|
||||
- hi
|
||||
- hr
|
||||
- hsb
|
||||
- hu
|
||||
- hy
|
||||
- ia
|
||||
- id
|
||||
- ig
|
||||
- io
|
||||
- is
|
||||
- it
|
||||
- ja
|
||||
- ka
|
||||
- kab
|
||||
- kk
|
||||
- km
|
||||
- kn
|
||||
- ko
|
||||
- ky
|
||||
- lb
|
||||
- lt
|
||||
- lv
|
||||
- mk
|
||||
- ml
|
||||
- mn
|
||||
- mr
|
||||
- ms
|
||||
- my
|
||||
- nb
|
||||
- ne
|
||||
- nl
|
||||
- nn
|
||||
- os
|
||||
- pa
|
||||
- pl
|
||||
- pt
|
||||
- pt-br
|
||||
- ro
|
||||
- ru
|
||||
- sk
|
||||
- sl
|
||||
- sq
|
||||
- sr
|
||||
- sr-latn
|
||||
- sv
|
||||
- sw
|
||||
- ta
|
||||
- te
|
||||
- tg
|
||||
- th
|
||||
- tk
|
||||
- tr
|
||||
- tt
|
||||
- udm
|
||||
- uk
|
||||
- ur
|
||||
- uz
|
||||
- vi
|
||||
- zh-hans
|
||||
- zh-hant
|
||||
tags:
|
||||
- schema
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
- {}
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/vnd.oai.openapi:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
application/yaml:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
application/vnd.oai.openapi+json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
description: ''
|
||||
/api/v1/archives/:
|
||||
get:
|
||||
operationId: v1_archives_list
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: id__gte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: id__in
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
description: Multiple values may be separated by commas.
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
name: id__lte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: ticket
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: ticket__gte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: ticket__in
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
description: Multiple values may be separated by commas.
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
name: ticket__lte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: time_create
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- in: query
|
||||
name: time_create__gte
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- in: query
|
||||
name: time_create__lte
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
description: ''
|
||||
post:
|
||||
operationId: v1_archives_create
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
description: ''
|
||||
/api/v1/archives/{id}/:
|
||||
get:
|
||||
operationId: v1_archives_retrieve
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this archive.
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
description: ''
|
||||
put:
|
||||
operationId: v1_archives_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this archive.
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: v1_archives_partial_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this archive.
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedArchive'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedArchive'
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Archive'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: v1_archives_destroy
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this archive.
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
/api/v1/platforms/:
|
||||
get:
|
||||
operationId: v1_platforms_list
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
description: ''
|
||||
post:
|
||||
operationId: v1_platforms_create
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
text/html:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
description: ''
|
||||
/api/v1/platforms/{name}/:
|
||||
get:
|
||||
operationId: v1_platforms_retrieve
|
||||
parameters:
|
||||
- in: path
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
description: ''
|
||||
put:
|
||||
operationId: v1_platforms_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
text/html:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: v1_platforms_partial_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedPlatform'
|
||||
text/html:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedPlatform'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedPlatform'
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Platform'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: v1_platforms_destroy
|
||||
parameters:
|
||||
- in: path
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
/api/v1/tickets/:
|
||||
get:
|
||||
operationId: v1_tickets_list
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: id__gte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: id__in
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
description: Multiple values may be separated by commas.
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
name: id__lte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: number
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: number__contains
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: number__gte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: number__in
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
description: Multiple values may be separated by commas.
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
name: number__lte
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: resolved
|
||||
schema:
|
||||
type: boolean
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: user
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
description: ''
|
||||
post:
|
||||
operationId: v1_tickets_create
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
text/html:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
description: ''
|
||||
/api/v1/tickets/{number}/:
|
||||
get:
|
||||
operationId: v1_tickets_retrieve
|
||||
parameters:
|
||||
- in: path
|
||||
name: number
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
description: ''
|
||||
put:
|
||||
operationId: v1_tickets_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: number
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
text/html:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: v1_tickets_partial_update
|
||||
parameters:
|
||||
- in: path
|
||||
name: number
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedTicket'
|
||||
text/html:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedTicket'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedTicket'
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Ticket'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: v1_tickets_destroy
|
||||
parameters:
|
||||
- in: path
|
||||
name: number
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
tags:
|
||||
- v1
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
components:
|
||||
schemas:
|
||||
Archive:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
file:
|
||||
type: string
|
||||
format: uri
|
||||
nullable: true
|
||||
ticket:
|
||||
type: integer
|
||||
time_create:
|
||||
type: number
|
||||
readOnly: true
|
||||
required:
|
||||
- id
|
||||
- ticket
|
||||
- time_create
|
||||
PatchedArchive:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
file:
|
||||
type: string
|
||||
format: uri
|
||||
nullable: true
|
||||
ticket:
|
||||
type: integer
|
||||
time_create:
|
||||
type: number
|
||||
readOnly: true
|
||||
PatchedPlatform:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
name:
|
||||
type: string
|
||||
maxLength: 20
|
||||
pretty_name:
|
||||
type: string
|
||||
maxLength: 20
|
||||
PatchedTicket:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
number:
|
||||
type: integer
|
||||
resolved:
|
||||
type: boolean
|
||||
token:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
attempts:
|
||||
type: integer
|
||||
maximum: 10
|
||||
minimum: 0
|
||||
platform:
|
||||
type: string
|
||||
time_create:
|
||||
type: number
|
||||
readOnly: true
|
||||
time_update:
|
||||
type: number
|
||||
readOnly: true
|
||||
user:
|
||||
type: string
|
||||
description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
|
||||
only.
|
||||
readOnly: true
|
||||
Platform:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
name:
|
||||
type: string
|
||||
maxLength: 20
|
||||
pretty_name:
|
||||
type: string
|
||||
maxLength: 20
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- pretty_name
|
||||
Ticket:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
number:
|
||||
type: integer
|
||||
resolved:
|
||||
type: boolean
|
||||
token:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
attempts:
|
||||
type: integer
|
||||
maximum: 10
|
||||
minimum: 0
|
||||
platform:
|
||||
type: string
|
||||
time_create:
|
||||
type: number
|
||||
readOnly: true
|
||||
time_update:
|
||||
type: number
|
||||
readOnly: true
|
||||
user:
|
||||
type: string
|
||||
description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
|
||||
only.
|
||||
readOnly: true
|
||||
required:
|
||||
- id
|
||||
- number
|
||||
- platform
|
||||
- time_create
|
||||
- time_update
|
||||
- token
|
||||
- user
|
||||
securitySchemes:
|
||||
basicAuth:
|
||||
type: http
|
||||
scheme: basic
|
||||
cookieAuth:
|
||||
type: apiKey
|
||||
in: cookie
|
||||
name: sessionid
|
6
logs_collector/static/css/bootstrap.min.css
vendored
Normal file
1
logs_collector/static/css/bootstrap.min.css.map
Normal file
BIN
logs_collector/static/img/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
logs_collector/static/img/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
logs_collector/static/img/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
logs_collector/static/img/favicon-16x16.png
Normal file
After Width: | Height: | Size: 495 B |
BIN
logs_collector/static/img/favicon-32x32.png
Normal file
After Width: | Height: | Size: 930 B |
BIN
logs_collector/static/img/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
1
logs_collector/static/img/site.webmanifest
Normal file
@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
7
logs_collector/static/js/bootstrap.bundle.min.js
vendored
Normal file
1
logs_collector/static/js/bootstrap.bundle.min.js.map
Normal file
90
logs_collector/static/js/bs.theme.mode.js
Normal file
@ -0,0 +1,90 @@
|
||||
/*!
|
||||
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
|
||||
* Copyright 2011-2023 The Bootstrap Authors
|
||||
* Licensed under the Creative Commons Attribution 3.0 Unported License.
|
||||
*/
|
||||
|
||||
;(() => {
|
||||
"use strict"
|
||||
|
||||
const getStoredTheme = () => localStorage.getItem("theme")
|
||||
const setStoredTheme = (theme) => localStorage.setItem("theme", theme)
|
||||
|
||||
const getPreferredTheme = () => {
|
||||
const storedTheme = getStoredTheme()
|
||||
if (storedTheme) {
|
||||
return storedTheme
|
||||
}
|
||||
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light"
|
||||
}
|
||||
|
||||
const setTheme = (theme) => {
|
||||
if (
|
||||
theme === "auto" &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
) {
|
||||
document.documentElement.setAttribute("data-bs-theme", "dark")
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-bs-theme", theme)
|
||||
}
|
||||
}
|
||||
|
||||
setTheme(getPreferredTheme())
|
||||
|
||||
const showActiveTheme = (theme, focus = false) => {
|
||||
const themeSwitcher = document.querySelector("#bd-theme")
|
||||
|
||||
if (!themeSwitcher) {
|
||||
return
|
||||
}
|
||||
|
||||
const themeSwitcherText = document.querySelector("#bd-theme-text")
|
||||
const activeThemeIcon = document.querySelector(".theme-icon-active use")
|
||||
const btnToActive = document.querySelector(
|
||||
`[data-bs-theme-value="${theme}"]`
|
||||
)
|
||||
const svgOfActiveBtn = btnToActive
|
||||
.querySelector("svg use")
|
||||
.getAttribute("href")
|
||||
|
||||
document.querySelectorAll("[data-bs-theme-value]").forEach((element) => {
|
||||
element.classList.remove("active")
|
||||
element.setAttribute("aria-pressed", "false")
|
||||
})
|
||||
|
||||
btnToActive.classList.add("active")
|
||||
btnToActive.setAttribute("aria-pressed", "true")
|
||||
activeThemeIcon.setAttribute("href", svgOfActiveBtn)
|
||||
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
|
||||
themeSwitcher.setAttribute("aria-label", themeSwitcherLabel)
|
||||
|
||||
if (focus) {
|
||||
themeSwitcher.focus()
|
||||
}
|
||||
}
|
||||
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", () => {
|
||||
const storedTheme = getStoredTheme()
|
||||
if (storedTheme !== "light" && storedTheme !== "dark") {
|
||||
setTheme(getPreferredTheme())
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
showActiveTheme(getPreferredTheme())
|
||||
|
||||
document.querySelectorAll("[data-bs-theme-value]").forEach((toggle) => {
|
||||
toggle.addEventListener("click", () => {
|
||||
const theme = toggle.getAttribute("data-bs-theme-value")
|
||||
setStoredTheme(theme)
|
||||
setTheme(theme)
|
||||
showActiveTheme(theme, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})()
|
6
logs_collector/static/js/bs.tooltip.js
Normal file
@ -0,0 +1,6 @@
|
||||
const tooltipTriggerList = document.querySelectorAll(
|
||||
'[data-bs-toggle="tooltip"]'
|
||||
)
|
||||
const tooltipList = [...tooltipTriggerList].map(
|
||||
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
)
|
50
logs_collector/templates/base.html
Normal file
@ -0,0 +1,50 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{% static '/css/bootstrap.min.css' %}"
|
||||
rel="stylesheet"
|
||||
>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="{% static '/img/apple-touch-icon.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="{% static '/img/favicon-32x32.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="{% static '/img/favicon-16x16.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="manifest"
|
||||
href="{% static '/img/site.webmanifest' %}"
|
||||
>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
|
||||
>
|
||||
{% block collector_head %}{% endblock collector_head %}
|
||||
{% block account_head %}{% endblock account_head %}
|
||||
</head>
|
||||
<body>
|
||||
{% block collector_content %}{% endblock collector_content %}
|
||||
{% block account_content %}{% endblock account_content %}
|
||||
|
||||
<script src="{% static '/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static '/js/bs.theme.mode.js' %}"></script>
|
||||
|
||||
{% block collector_scripts %}{% endblock collector_scripts %}
|
||||
{% block account_scripts %}{% endblock account_scripts %}
|
||||
</body>
|
||||
</html>
|
@ -97,7 +97,14 @@
|
||||
<a class="dropdown-item" type="button" href="{% url 'swagger-ui' %}"
|
||||
><i class="bi bi-braces-asterisk"></i> Swagger</a>
|
||||
</li>
|
||||
<li><button class="dropdown-item" type="button"><i class="bi bi-gear"></i> Settings</button></li>
|
||||
<li>
|
||||
<a
|
||||
href="{% url 'two_factor:profile' %}"
|
||||
class="dropdown-item"
|
||||
type="button">
|
||||
<i class="bi bi-gear"></i> Settings
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider" /></li>
|
||||
<li>
|
||||
<a
|
||||
@ -130,7 +137,7 @@
|
||||
<!-- Theme switcher-->
|
||||
<li class="nav-item dropdown">
|
||||
<div class="dropdown bd-mode-toggle">
|
||||
{% include 'collector/includes/theme_swither.html' %}
|
||||
{% include 'includes/theme_switcher.html' %}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
@ -22,7 +22,7 @@
|
||||
/>
|
||||
</symbol>
|
||||
</svg>
|
||||
<!--Theme switcher buttons-->
|
||||
<!-- Theme switcher dropdown buttons -->
|
||||
<button
|
||||
class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
||||
id="bd-theme"
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
@ -1,91 +0,0 @@
|
||||
<!--Theme switcher icons-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
|
||||
<symbol id="check2" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="circle-half" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
|
||||
</symbol>
|
||||
<symbol id="moon-stars-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"
|
||||
/>
|
||||
<path
|
||||
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="sun-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"
|
||||
/>
|
||||
</symbol>
|
||||
</svg>
|
||||
<!--Theme switcher buttons-->
|
||||
<button
|
||||
class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
||||
id="bd-theme"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-label="Toggle theme (auto)"
|
||||
>
|
||||
<svg class="bi my-1 theme-icon-active" width="1em" height="1em">
|
||||
<use href="#circle-half"></use>
|
||||
</svg>
|
||||
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-menu-end shadow"
|
||||
aria-labelledby="bd-theme-text"
|
||||
>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="light"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#sun-fill"></use>
|
||||
</svg>
|
||||
Light
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="dark"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#moon-stars-fill"></use>
|
||||
</svg>
|
||||
Dark
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center active"
|
||||
data-bs-theme-value="auto"
|
||||
aria-pressed="true"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#circle-half"></use>
|
||||
</svg>
|
||||
Auto
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
Before Width: | Height: | Size: 3.9 KiB |
@ -1,42 +1,18 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<head>
|
||||
{% include 'collector/includes/metalinks.html' %}
|
||||
<title>Collector - {% block title %}{% endblock %}</title>
|
||||
{% block extra_media %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<!-- Two factor auth-->
|
||||
<section>
|
||||
<header>
|
||||
{% block 'navigation' %}{% endblock 'navigation' %}
|
||||
</header>
|
||||
</section>
|
||||
<section>
|
||||
<main>
|
||||
<div
|
||||
class="d-flex min-vh-100 align-items-center py-4 bg-body-tertiary"
|
||||
cz-shortcut-listen="true" >
|
||||
{% block content_wrapper %}
|
||||
<div class="container">
|
||||
<div class="row">{% block content %}{% endblock %}</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
<!-- Theme switcher-->
|
||||
<section>
|
||||
<footer>
|
||||
<div
|
||||
class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle"
|
||||
>
|
||||
{% include 'collector/includes/theme_swither.html' %}
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
<script src="{% static 'collector/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'collector/js/bs.theme.mode.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{% block account_head %}
|
||||
<title>Collector - {% block title %}{% endblock %}</title>
|
||||
{% block extra_media %}{% endblock %}
|
||||
{% endblock account_head %}
|
||||
|
||||
{% block account_content %}
|
||||
{% block content_wrapper %}
|
||||
{% block content %}{% endblock %}
|
||||
{% endblock %}
|
||||
<section>
|
||||
<footer>
|
||||
{% block theme_switcher %}{% endblock %}
|
||||
</footer>
|
||||
</section>
|
||||
{% endblock account_content %}
|
||||
|
@ -1,11 +1,22 @@
|
||||
{% extends "two_factor/_base.html" %}
|
||||
|
||||
{% block content_wrapper %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4 offset-md-4">
|
||||
{% block content %}{% endblock %}
|
||||
<section>
|
||||
<header>
|
||||
{% block nav %}{% endblock nav %}
|
||||
</header>
|
||||
</section>
|
||||
<section>
|
||||
<main>
|
||||
<div class="container mt-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
@ -1,13 +1,15 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% if cancel_url %}
|
||||
<a href="{{ cancel_url }}"
|
||||
class="float-right btn btn-link">{% trans "Cancel" %}</a>
|
||||
<a href="{{ cancel_url }}" class="float-right btn btn-link">{% trans "Cancel" %}</a>
|
||||
{% endif %}
|
||||
{% if wizard.steps.prev %}
|
||||
<button name="wizard_goto_step" type="submit"
|
||||
value="{{ wizard.steps.prev }}"
|
||||
class="btn btn-secondary">{% trans "Back" %}</button>
|
||||
<button
|
||||
name="wizard_goto_step"
|
||||
type="submit"
|
||||
value="{{ wizard.steps.prev }}"
|
||||
class="btn btn-secondary">{% trans "Back" %}
|
||||
</button>
|
||||
{% else %}
|
||||
<button disabled name="" type="button" class="btn">{% trans "Back" %}</button>
|
||||
{% endif %}
|
||||
|
@ -1,18 +1,23 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Backup Tokens" %}{% endblock %}</h1>
|
||||
<p>{% blocktrans trimmed %}Backup tokens can be used when your primary and backup
|
||||
phone numbers aren't available. The backup tokens below can be used
|
||||
for login verification. If you've used up all your backup tokens, you
|
||||
can generate a new set of backup tokens. Only the backup tokens shown
|
||||
below will be valid.{% endblocktrans %}</p>
|
||||
|
||||
<h1>{% block title %}{% trans "Backup Tokens" %}{% endblock %}</h1>
|
||||
<div class="mb-3 d-flex justify-content-center">
|
||||
<p>{% blocktrans trimmed %}Backup tokens can be used when your primary and backup
|
||||
phone numbers aren't available. The backup tokens below can be used
|
||||
for login verification. If you've used up all your backup tokens, you
|
||||
can generate a new set of backup tokens. Only the backup tokens shown
|
||||
below will be valid.{% endblocktrans %}</p>
|
||||
</div>
|
||||
{% if device.token_set.count %}
|
||||
<ul>
|
||||
<ul class="list-group">
|
||||
{% for token in device.token_set.all %}
|
||||
<li>{{ token.token }}</li>
|
||||
<li class="list-group-item list-group-item-action">{{ token.token }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p>{% blocktrans %}Print these tokens and keep them somewhere safe.{% endblocktrans %}</p>
|
||||
@ -20,9 +25,11 @@
|
||||
<p>{% trans "You don't have any backup codes yet." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">{% csrf_token %}{{ form.as_p }}
|
||||
<a href="{% url 'two_factor:profile'%}"
|
||||
class="float-right btn btn-link">{% trans "Back to Account Security" %}</a>
|
||||
<button class="btn btn-primary" type="submit">{% trans "Generate Tokens" %}</button>
|
||||
<form method="post">{% csrf_token %}{{ form|crispy }}
|
||||
<a
|
||||
href="{% url 'two_factor:profile'%}"
|
||||
class="float-right btn btn-link">{% trans "Back to Account Security" %}
|
||||
</a>
|
||||
<button class="btn btn-outline-primary" type="submit">{% trans "Generate Tokens" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% extends "two_factor/_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load two_factor_tags %}
|
||||
|
||||
@ -7,51 +7,65 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Login" %}{% endblock %}</h1>
|
||||
|
||||
{% if wizard.steps.current == 'auth' %}
|
||||
<p>{% blocktrans %}Enter your credentials.{% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'token' %}
|
||||
<p>{{ device|as_verbose_action }}</p>
|
||||
{% elif wizard.steps.current == 'backup' %}
|
||||
<p>{% blocktrans trimmed %}Use this form for entering backup tokens for logging in.
|
||||
These tokens have been generated for you to print and keep safe. Please
|
||||
enter one of these backup tokens to login to your account.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% include "two_factor/_wizard_forms.html" %}
|
||||
|
||||
{# hidden submit button to enable [enter] key #}
|
||||
<input type="submit" value="" hidden />
|
||||
|
||||
{% if other_devices %}
|
||||
<p>{% trans "Or, alternatively, use one of your other authentication methods:" %}</p>
|
||||
<p>
|
||||
{% for other in other_devices %}
|
||||
<button name="challenge_device" value="{{ other.persistent_id }}"
|
||||
class="btn btn-secondary btn-block" type="submit">
|
||||
{{ other|as_action }}
|
||||
</button>
|
||||
{% endfor %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% include "two_factor/_wizard_actions.html" %}
|
||||
</form>
|
||||
|
||||
{% block 'backup_tokens' %}
|
||||
{% if backup_tokens %}
|
||||
<hr>
|
||||
<div class="backup_tokens_form">
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<p>{% trans "As a last resort, you can use a backup token:" %}</p>
|
||||
<div class="d-flex min-vh-100 align-items-center py-4" cz-shortcut-listen="true">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-3 offset-md-4">
|
||||
<h1>{% block title %}{% trans "Login" %}{% endblock %}</h1>
|
||||
{% if wizard.steps.current == 'auth' %}
|
||||
<p>{% blocktrans %}Enter your credentials.{% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'token' %}
|
||||
<p>{{ device|as_verbose_action }}</p>
|
||||
{% elif wizard.steps.current == 'backup' %}
|
||||
<p>{% blocktrans trimmed %}Use this form for entering backup tokens for logging in.
|
||||
These tokens have been generated for you to print and keep safe. Please
|
||||
enter one of these backup tokens to login to your account.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% include "two_factor/_wizard_forms.html" %}
|
||||
|
||||
{# hidden submit button to enable [enter] key #}
|
||||
<input type="submit" value="" hidden />
|
||||
|
||||
{% if other_devices %}
|
||||
<p>{% trans "Or, alternatively, use one of your other authentication methods:" %}</p>
|
||||
<p>
|
||||
<button name="wizard_goto_step" type="submit" value="backup"
|
||||
class="btn btn-sm btn-secondary btn-block">{% trans "Use Backup Token" %}</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% for other in other_devices %}
|
||||
<button name="challenge_device" value="{{ other.persistent_id }}"
|
||||
class="btn btn-secondary btn-block" type="submit">
|
||||
{{ other|as_action }}
|
||||
</button>
|
||||
{% endfor %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% include "two_factor/_wizard_actions.html" %}
|
||||
</form>
|
||||
|
||||
{% block 'backup_tokens' %}
|
||||
{% if backup_tokens %}
|
||||
<hr>
|
||||
<div class="backup_tokens_form">
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<p>{% trans "As a last resort, you can use a backup token:" %}</p>
|
||||
<p>
|
||||
<button name="wizard_goto_step" type="submit" value="backup"
|
||||
class="btn btn-sm btn-secondary btn-block">{% trans "Use Backup Token" %}</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<!-- Theme switcher -->
|
||||
{% block theme_switcher %}
|
||||
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
|
||||
{% include 'includes/theme_switcher.html' %}
|
||||
</div>
|
||||
{% endblock theme_switcher %}
|
||||
|
@ -1,20 +1,35 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% extends "two_factor/_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Permission Denied" %}{% endblock %}</h1>
|
||||
<div class="d-flex min-vh-100 align-items-center py-4" cz-shortcut-listen="true">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4 offset-md-4">
|
||||
<h1>{% block title %}{% trans "Permission Denied" %}{% endblock %}</h1>
|
||||
|
||||
<p>{% blocktrans trimmed %}The page you requested, enforces users to verify using
|
||||
two-factor authentication for security reasons. You need to enable these
|
||||
security features in order to access this page.{% endblocktrans %}</p>
|
||||
|
||||
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||
account. Enable two-factor authentication for enhanced account
|
||||
security.{% endblocktrans %}</p>
|
||||
<p>
|
||||
<a href="javascript:history.go(-1)"
|
||||
class="float-right btn btn-link">{% trans "Go back" %}</a>
|
||||
<a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||
</p>
|
||||
<p>{% blocktrans trimmed %}The page you requested, enforces users to verify using
|
||||
two-factor authentication for security reasons. You need to enable these
|
||||
security features in order to access this page.{% endblocktrans %}</p>
|
||||
|
||||
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||
account. Enable two-factor authentication for enhanced account
|
||||
security.{% endblocktrans %}</p>
|
||||
<p>
|
||||
<a href="javascript:history.go(-1)"
|
||||
class="float-right btn btn-link">{% trans "Go back" %}</a>
|
||||
<a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<!-- Theme switcher -->
|
||||
{% block theme_switcher %}
|
||||
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
|
||||
{% include 'includes/theme_switcher.html' %}
|
||||
</div>
|
||||
{% endblock theme_switcher %}
|
||||
|
@ -5,6 +5,8 @@
|
||||
{{ form.media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
||||
{% if wizard.steps.current == 'welcome' %}
|
||||
@ -52,13 +54,14 @@
|
||||
token in the field below. Your YubiKey will be linked to your
|
||||
account.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
<div class="input-group mb-3 d-flex justify-content-center">
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% include "two_factor/_wizard_forms.html" %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% include "two_factor/_wizard_forms.html" %}
|
||||
{# hidden submit button to enable [enter] key #}
|
||||
<input type="submit" value="" hidden />
|
||||
|
||||
{# hidden submit button to enable [enter] key #}
|
||||
<input type="submit" value="" hidden />
|
||||
|
||||
{% include "two_factor/_wizard_actions.html" %}
|
||||
</form>
|
||||
{% include "two_factor/_wizard_actions.html" %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,6 +1,8 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
||||
|
||||
|
@ -1,14 +1,19 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Disable Two-factor Authentication" %}{% endblock %}</h1>
|
||||
<p>{% blocktrans trimmed %}You are about to disable two-factor authentication. This
|
||||
weakens your account security, are you sure?{% endblocktrans %}</p>
|
||||
<h1>{% block title %}{% trans "Disable Two-factor Authentication" %}{% endblock %}</h1>
|
||||
<p>{% blocktrans trimmed %}You are about to disable two-factor authentication. This
|
||||
weakens your account security, are you sure?{% endblocktrans %}</p>
|
||||
<div class="input-group mb-3 d-flex justify-content-center">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<table>{{ form.as_table }}</table>
|
||||
<button class="btn btn-danger"
|
||||
type="submit">{% trans "Disable" %}</button>
|
||||
<div>{{ form|crispy }}</div>
|
||||
<button class="btn btn-danger" type="submit">{% trans "Disable" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,62 +1,59 @@
|
||||
{% extends "two_factor/_base.html" %}
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
{% load two_factor_tags %}
|
||||
|
||||
{% block 'navigation' %}
|
||||
{% include 'collector/includes/navigation.html' %}
|
||||
{% endblock 'navigation' %}
|
||||
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Account Security" %}{% endblock %}</h1>
|
||||
<h1>{% block title %}{% trans "Account Security" %}{% endblock %}</h1>
|
||||
{% if default_device %}
|
||||
<p>{% blocktrans with primary=default_device|as_action %}Primary method: {{ primary }}{% endblocktrans %}</p>
|
||||
|
||||
{% if default_device %}
|
||||
<p>{% blocktrans with primary=default_device|as_action %}Primary method: {{ primary }}{% endblocktrans %}</p>
|
||||
{% if available_phone_methods %}
|
||||
<h2>{% trans "Backup Phone Numbers" %}</h2>
|
||||
<p>{% blocktrans trimmed %}If your primary method is not available, we are able to
|
||||
send backup tokens to the phone numbers listed below.{% endblocktrans %}</p>
|
||||
<ul>
|
||||
{% for phone in backup_phones %}
|
||||
<li>
|
||||
{{ phone|as_action }}
|
||||
<form method="post" action="{% url 'two_factor:phone_delete' phone.id %}"
|
||||
onsubmit="return confirm({% trans 'Are you sure?' %})">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-sm btn-warning"
|
||||
type="submit">{% trans "Unregister" %}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p><a href="{% url 'two_factor:phone_create' %}"
|
||||
class="btn btn-info">{% trans "Add Phone Number" %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% trans "Backup Tokens" %}</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}If you don't have any device with you, you can access
|
||||
your account using backup tokens.{% endblocktrans %}
|
||||
{% blocktrans trimmed count counter=backup_tokens %}
|
||||
You have only one backup token remaining.
|
||||
{% plural %}
|
||||
You have {{ counter }} backup tokens remaining.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p><a href="{% url 'two_factor:backup_tokens' %}"
|
||||
class="btn btn-info">{% trans "Show Codes" %}</a></p>
|
||||
|
||||
{% if available_phone_methods %}
|
||||
<h2>{% trans "Backup Phone Numbers" %}</h2>
|
||||
<p>{% blocktrans trimmed %}If your primary method is not available, we are able to
|
||||
send backup tokens to the phone numbers listed below.{% endblocktrans %}</p>
|
||||
<ul>
|
||||
{% for phone in backup_phones %}
|
||||
<li>
|
||||
{{ phone|as_action }}
|
||||
<form method="post" action="{% url 'two_factor:phone_delete' phone.id %}"
|
||||
onsubmit="return confirm({% trans 'Are you sure?' %})">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-sm btn-warning"
|
||||
type="submit">{% trans "Unregister" %}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p><a href="{% url 'two_factor:phone_create' %}"
|
||||
class="btn btn-info">{% trans "Add Phone Number" %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% trans "Backup Tokens" %}</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}If you don't have any device with you, you can access
|
||||
your account using backup tokens.{% endblocktrans %}
|
||||
{% blocktrans trimmed count counter=backup_tokens %}
|
||||
You have only one backup token remaining.
|
||||
{% plural %}
|
||||
You have {{ counter }} backup tokens remaining.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p><a href="{% url 'two_factor:backup_tokens' %}"
|
||||
class="btn btn-info">{% trans "Show Codes" %}</a></p>
|
||||
|
||||
<h3>{% trans "Disable Two-Factor Authentication" %}</h3>
|
||||
<p>{% blocktrans trimmed %}However we strongly discourage you to do so, you can
|
||||
also disable two-factor authentication for your account.{% endblocktrans %}</p>
|
||||
<p><a class="btn btn-secondary" href="{% url 'two_factor:disable' %}">
|
||||
{% trans "Disable Two-Factor Authentication" %}</a></p>
|
||||
{% else %}
|
||||
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||
account. Enable two-factor authentication for enhanced account
|
||||
security.{% endblocktrans %}</p>
|
||||
<p><a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
<h3>{% trans "Disable Two-Factor Authentication" %}</h3>
|
||||
<p>{% blocktrans trimmed %}However we strongly discourage you to do so, you can
|
||||
also disable two-factor authentication for your account.{% endblocktrans %}</p>
|
||||
<p><a class="btn btn-secondary" href="{% url 'two_factor:disable' %}">
|
||||
{% trans "Disable Two-Factor Authentication" %}</a></p>
|
||||
{% else %}
|
||||
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||
account. Enable two-factor authentication for enhanced account
|
||||
security.{% endblocktrans %}</p>
|
||||
<p><a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|