Refactoring: new multi app structure
@ -3,4 +3,5 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class AccountConfig(AppConfig):
|
class AccountConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
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 = [
|
urlpatterns = [
|
||||||
# WEB LOGOUT:
|
# WEB LOGOUT:
|
||||||
path(
|
path(
|
||||||
'accounts/logout/',
|
'account/logout/',
|
||||||
LogoutView.as_view(next_page=settings.LOGOUT_REDIRECT_URL),
|
LogoutView.as_view(next_page=settings.LOGOUT_REDIRECT_URL),
|
||||||
name='logout'
|
name='logout'
|
||||||
)
|
)
|
@ -5,7 +5,7 @@ from django_filters.rest_framework import (
|
|||||||
)
|
)
|
||||||
from django_filters import widgets
|
from django_filters import widgets
|
||||||
|
|
||||||
from .models import Archive, Ticket
|
from apps.collector.models import Archive, Ticket
|
||||||
from .utils import DateTimeFilterMixin
|
from .utils import DateTimeFilterMixin
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@ from rest_framework import serializers
|
|||||||
from drf_spectacular.utils import extend_schema_field
|
from drf_spectacular.utils import extend_schema_field
|
||||||
from drf_spectacular.openapi import OpenApiTypes
|
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)
|
@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.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 import status
|
||||||
# from rest_framework.decorators import action
|
# from rest_framework.decorators import action
|
||||||
@ -15,14 +10,10 @@ from rest_framework import filters
|
|||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
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 .filters import ArchiveFilter, TicketFilter
|
||||||
from .utils import PageTitleViewMixin
|
|
||||||
from .permissions import IsGuestUpload
|
from .permissions import IsGuestUpload
|
||||||
|
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
PublicArchiveUploadSerializer,
|
PublicArchiveUploadSerializer,
|
||||||
ArchiveSerializer,
|
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):
|
class ArchiveViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Archive.objects.order_by('-time_create')
|
queryset = Archive.objects.order_by('-time_create')
|
||||||
serializer_class = ArchiveSerializer
|
serializer_class = ArchiveSerializer
|
@ -3,4 +3,5 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class CollectorConfig(AppConfig):
|
class CollectorConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
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
|
from django.conf import settings
|
||||||
import django.core.files.storage
|
import django.core.files.storage
|
||||||
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@ -21,7 +23,7 @@ class Migration(migrations.Migration):
|
|||||||
name='Platform',
|
name='Platform',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('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)),
|
('pretty_name', models.CharField(max_length=20)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -29,26 +31,29 @@ class Migration(migrations.Migration):
|
|||||||
name='Ticket',
|
name='Ticket',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('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)),
|
('resolved', models.BooleanField(default=False)),
|
||||||
('note', models.TextField(blank=True)),
|
('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_create', models.DateTimeField(auto_now_add=True)),
|
||||||
('time_update', models.DateTimeField(auto_now=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)),
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
],
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['-time_create'],
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Archive',
|
name='Archive',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('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)),
|
('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)),
|
||||||
('size', models.CharField(blank=True, max_length=50)),
|
('md5', models.CharField(editable=False, max_length=1024)),
|
||||||
('sha1', models.CharField(editable=False, max_length=1024)),
|
|
||||||
('time_create', models.DateTimeField(auto_now_add=True)),
|
('time_create', models.DateTimeField(auto_now_add=True)),
|
||||||
('time_update', models.DateTimeField(auto_now=True)),
|
('time_update', models.DateTimeField(auto_now=True)),
|
||||||
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='collector.ticket')),
|
('ticket', models.ForeignKey(db_column='ticket_number', on_delete=django.db.models.deletion.CASCADE, to='collector.ticket', to_field='number')),
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
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
|
>Cancel
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
href="{% url 'collector:archive-detail' archive.id %}"
|
href="{% url 'collector_api:archive-detail' archive.id %}"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-danger btn-archive-eraser"
|
class="btn btn-danger btn-archive-eraser"
|
||||||
data-bs-dismiss="modal"
|
data-bs-dismiss="modal"
|
@ -27,7 +27,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
type="button"
|
type="button"
|
||||||
href="{% url 'collector:ticket-detail' ticket.number %}"
|
href="{% url 'collector_api:ticket-detail' ticket.number %}"
|
||||||
class="btn btn-danger btn-ticket-del"
|
class="btn btn-danger btn-ticket-del"
|
||||||
data-bs-dismiss="modal"
|
data-bs-dismiss="modal"
|
||||||
data-jq-ticket-del-target="#div-ticket-{{ ticket.number }}"
|
data-jq-ticket-del-target="#div-ticket-{{ ticket.number }}"
|
@ -9,7 +9,7 @@
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
role="switch"
|
role="switch"
|
||||||
name="ticket-state"
|
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 %} ticket-state-switch="1" {% endif %}
|
||||||
{% if ticket.resolved %} checked {% endif %}>
|
{% if ticket.resolved %} checked {% endif %}>
|
||||||
</div>
|
</div>
|
@ -2,7 +2,7 @@ import markdown as md
|
|||||||
from django import template
|
from django import template
|
||||||
from django.template.defaultfilters import stringfilter
|
from django.template.defaultfilters import stringfilter
|
||||||
|
|
||||||
from collector.models import Platform
|
from apps.collector.models import Platform
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
@ -1,6 +1,4 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path
|
||||||
|
|
||||||
from rest_framework import routers
|
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
@ -50,17 +48,3 @@ urlpatterns = [
|
|||||||
name='update'
|
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
|
import os
|
||||||
from django_filters import NumberFilter
|
|
||||||
|
|
||||||
|
|
||||||
def logs_dir_path(instance, filename):
|
def logs_dir_path(instance, filename):
|
||||||
@ -10,6 +9,7 @@ def logs_dir_path(instance, filename):
|
|||||||
return f'{instance.ticket.number}/{filename}'
|
return f'{instance.ticket.number}/{filename}'
|
||||||
|
|
||||||
|
|
||||||
|
# deprecated
|
||||||
def get_file_size(file_path, unit='bytes'):
|
def get_file_size(file_path, unit='bytes'):
|
||||||
file_size = os.path.getsize(file_path)
|
file_size = os.path.getsize(file_path)
|
||||||
exponents_map = {'bytes': 0, 'kb': 1, 'mb': 2, 'gb': 3}
|
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)
|
return round(size, 3)
|
||||||
|
|
||||||
|
|
||||||
|
# deprecated
|
||||||
def is_ajax(request):
|
def is_ajax(request):
|
||||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||||
return True
|
return True
|
||||||
@ -40,22 +41,3 @@ class PageTitleViewMixin:
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['title'] = self.get_title()
|
context['title'] = self.get_title()
|
||||||
return context
|
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.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'collector.apps.CollectorConfig', # main app
|
'apps.collector.apps.CollectorConfig', # main app
|
||||||
'account.apps.AccountConfig', # account app
|
'apps.account.apps.AccountConfig', # account app
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework_simplejwt',
|
'rest_framework_simplejwt',
|
||||||
'django_filters',
|
'django_filters',
|
||||||
@ -129,6 +129,9 @@ USE_TZ = True
|
|||||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
STATIC_URL = 'static/'
|
||||||
|
STATICFILES_DIRS = [
|
||||||
|
BASE_DIR / "static",
|
||||||
|
]
|
||||||
|
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
# 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 two_factor.urls import urlpatterns as tf_urls
|
||||||
|
|
||||||
from logs_collector import settings
|
from logs_collector import settings
|
||||||
from account.utils import AdminSiteOTPRequiredMixinRedirectSetup
|
from apps.account.utils import AdminSiteOTPRequiredMixinRedirectSetup
|
||||||
|
|
||||||
|
|
||||||
# ? 2FA patch (Admin site protection)
|
# ? 2FA patch (Admin site protection)
|
||||||
@ -19,12 +19,29 @@ admin.site.__class__ = AdminSiteOTPRequiredMixinRedirectSetup
|
|||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path(
|
||||||
path('', include('collector.urls', namespace='collector')),
|
'admin/',
|
||||||
path('', include(tf_urls)),
|
admin.site.urls
|
||||||
path('', include('account.urls', namespace='account'))
|
),
|
||||||
|
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 += [
|
urlpatterns += [
|
||||||
# API PATTERNS
|
# API PATTERNS
|
||||||
path('api/v1/schema/', SpectacularAPIView.as_view(), name='schema'),
|
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' %}"
|
<a class="dropdown-item" type="button" href="{% url 'swagger-ui' %}"
|
||||||
><i class="bi bi-braces-asterisk"></i> Swagger</a>
|
><i class="bi bi-braces-asterisk"></i> Swagger</a>
|
||||||
</li>
|
</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><hr class="dropdown-divider" /></li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
@ -130,7 +137,7 @@
|
|||||||
<!-- Theme switcher-->
|
<!-- Theme switcher-->
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<div class="dropdown bd-mode-toggle">
|
<div class="dropdown bd-mode-toggle">
|
||||||
{% include 'collector/includes/theme_swither.html' %}
|
{% include 'includes/theme_switcher.html' %}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
@ -22,7 +22,7 @@
|
|||||||
/>
|
/>
|
||||||
</symbol>
|
</symbol>
|
||||||
</svg>
|
</svg>
|
||||||
<!--Theme switcher buttons-->
|
<!-- Theme switcher dropdown buttons -->
|
||||||
<button
|
<button
|
||||||
class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
||||||
id="bd-theme"
|
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 %}
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" data-bs-theme="auto">
|
{% block account_head %}
|
||||||
<head>
|
<title>Collector - {% block title %}{% endblock %}</title>
|
||||||
{% include 'collector/includes/metalinks.html' %}
|
{% block extra_media %}{% endblock %}
|
||||||
<title>Collector - {% block title %}{% endblock %}</title>
|
{% endblock account_head %}
|
||||||
{% block extra_media %}{% endblock %}
|
|
||||||
</head>
|
{% block account_content %}
|
||||||
<body>
|
{% block content_wrapper %}
|
||||||
<!-- Two factor auth-->
|
{% block content %}{% endblock %}
|
||||||
<section>
|
{% endblock %}
|
||||||
<header>
|
<section>
|
||||||
{% block 'navigation' %}{% endblock 'navigation' %}
|
<footer>
|
||||||
</header>
|
{% block theme_switcher %}{% endblock %}
|
||||||
</section>
|
</footer>
|
||||||
<section>
|
</section>
|
||||||
<main>
|
{% endblock account_content %}
|
||||||
<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>
|
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
{% extends "two_factor/_base.html" %}
|
{% extends "two_factor/_base.html" %}
|
||||||
|
|
||||||
{% block content_wrapper %}
|
{% block content_wrapper %}
|
||||||
<div class="container">
|
<section>
|
||||||
<div class="row">
|
<header>
|
||||||
<div class="col-md-4 offset-md-4">
|
{% block nav %}{% endblock nav %}
|
||||||
{% block content %}{% endblock %}
|
</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>
|
||||||
</div>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% if cancel_url %}
|
{% if cancel_url %}
|
||||||
<a href="{{ cancel_url }}"
|
<a href="{{ cancel_url }}" class="float-right btn btn-link">{% trans "Cancel" %}</a>
|
||||||
class="float-right btn btn-link">{% trans "Cancel" %}</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if wizard.steps.prev %}
|
{% if wizard.steps.prev %}
|
||||||
<button name="wizard_goto_step" type="submit"
|
<button
|
||||||
value="{{ wizard.steps.prev }}"
|
name="wizard_goto_step"
|
||||||
class="btn btn-secondary">{% trans "Back" %}</button>
|
type="submit"
|
||||||
|
value="{{ wizard.steps.prev }}"
|
||||||
|
class="btn btn-secondary">{% trans "Back" %}
|
||||||
|
</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button disabled name="" type="button" class="btn">{% trans "Back" %}</button>
|
<button disabled name="" type="button" class="btn">{% trans "Back" %}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
{% extends "two_factor/_base_focus.html" %}
|
{% extends "two_factor/_base_focus.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||||
|
|
||||||
{% block content %}
|
{% 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 %}
|
{% if device.token_set.count %}
|
||||||
<ul>
|
<ul class="list-group">
|
||||||
{% for token in device.token_set.all %}
|
{% for token in device.token_set.all %}
|
||||||
<li>{{ token.token }}</li>
|
<li class="list-group-item list-group-item-action">{{ token.token }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<p>{% blocktrans %}Print these tokens and keep them somewhere safe.{% endblocktrans %}</p>
|
<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>
|
<p>{% trans "You don't have any backup codes yet." %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form method="post">{% csrf_token %}{{ form.as_p }}
|
<form method="post">{% csrf_token %}{{ form|crispy }}
|
||||||
<a href="{% url 'two_factor:profile'%}"
|
<a
|
||||||
class="float-right btn btn-link">{% trans "Back to Account Security" %}</a>
|
href="{% url 'two_factor:profile'%}"
|
||||||
<button class="btn btn-primary" type="submit">{% trans "Generate Tokens" %}</button>
|
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>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "two_factor/_base_focus.html" %}
|
{% extends "two_factor/_base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load two_factor_tags %}
|
{% load two_factor_tags %}
|
||||||
|
|
||||||
@ -7,51 +7,65 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% block title %}{% trans "Login" %}{% endblock %}</h1>
|
<div class="d-flex min-vh-100 align-items-center py-4" cz-shortcut-listen="true">
|
||||||
|
<div class="container">
|
||||||
{% if wizard.steps.current == 'auth' %}
|
<div class="row">
|
||||||
<p>{% blocktrans %}Enter your credentials.{% endblocktrans %}</p>
|
<div class="col-md-3 offset-md-4">
|
||||||
{% elif wizard.steps.current == 'token' %}
|
<h1>{% block title %}{% trans "Login" %}{% endblock %}</h1>
|
||||||
<p>{{ device|as_verbose_action }}</p>
|
{% if wizard.steps.current == 'auth' %}
|
||||||
{% elif wizard.steps.current == 'backup' %}
|
<p>{% blocktrans %}Enter your credentials.{% endblocktrans %}</p>
|
||||||
<p>{% blocktrans trimmed %}Use this form for entering backup tokens for logging in.
|
{% elif wizard.steps.current == 'token' %}
|
||||||
These tokens have been generated for you to print and keep safe. Please
|
<p>{{ device|as_verbose_action }}</p>
|
||||||
enter one of these backup tokens to login to your account.{% endblocktrans %}</p>
|
{% elif wizard.steps.current == 'backup' %}
|
||||||
{% endif %}
|
<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
|
||||||
<form action="" method="post">{% csrf_token %}
|
enter one of these backup tokens to login to your account.{% endblocktrans %}</p>
|
||||||
{% include "two_factor/_wizard_forms.html" %}
|
{% endif %}
|
||||||
|
|
||||||
{# hidden submit button to enable [enter] key #}
|
<form action="" method="post">{% csrf_token %}
|
||||||
<input type="submit" value="" hidden />
|
{% include "two_factor/_wizard_forms.html" %}
|
||||||
|
|
||||||
{% if other_devices %}
|
{# hidden submit button to enable [enter] key #}
|
||||||
<p>{% trans "Or, alternatively, use one of your other authentication methods:" %}</p>
|
<input type="submit" value="" hidden />
|
||||||
<p>
|
|
||||||
{% for other in other_devices %}
|
{% if other_devices %}
|
||||||
<button name="challenge_device" value="{{ other.persistent_id }}"
|
<p>{% trans "Or, alternatively, use one of your other authentication methods:" %}</p>
|
||||||
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>
|
<p>
|
||||||
<button name="wizard_goto_step" type="submit" value="backup"
|
{% for other in other_devices %}
|
||||||
class="btn btn-sm btn-secondary btn-block">{% trans "Use Backup Token" %}</button>
|
<button name="challenge_device" value="{{ other.persistent_id }}"
|
||||||
</p>
|
class="btn btn-secondary btn-block" type="submit">
|
||||||
</form>
|
{{ other|as_action }}
|
||||||
</div>
|
</button>
|
||||||
{% endif %}
|
{% endfor %}</p>
|
||||||
{% endblock %}
|
{% 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 %}
|
{% 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 %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% 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
|
<p>{% blocktrans trimmed %}The page you requested, enforces users to verify using
|
||||||
two-factor authentication for security reasons. You need to enable these
|
two-factor authentication for security reasons. You need to enable these
|
||||||
security features in order to access this page.{% endblocktrans %}</p>
|
security features in order to access this page.{% endblocktrans %}</p>
|
||||||
|
|
||||||
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||||
account. Enable two-factor authentication for enhanced account
|
account. Enable two-factor authentication for enhanced account
|
||||||
security.{% endblocktrans %}</p>
|
security.{% endblocktrans %}</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="javascript:history.go(-1)"
|
<a href="javascript:history.go(-1)"
|
||||||
class="float-right btn btn-link">{% trans "Go back" %}</a>
|
class="float-right btn btn-link">{% trans "Go back" %}</a>
|
||||||
<a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
<a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||||
{% trans "Enable Two-Factor Authentication" %}</a>
|
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% 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 }}
|
{{ form.media }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
||||||
{% if wizard.steps.current == 'welcome' %}
|
{% if wizard.steps.current == 'welcome' %}
|
||||||
@ -52,13 +54,14 @@
|
|||||||
token in the field below. Your YubiKey will be linked to your
|
token in the field below. Your YubiKey will be linked to your
|
||||||
account.{% endblocktrans %}</p>
|
account.{% endblocktrans %}</p>
|
||||||
{% endif %}
|
{% 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 %}
|
{# hidden submit button to enable [enter] key #}
|
||||||
{% include "two_factor/_wizard_forms.html" %}
|
<input type="submit" value="" hidden />
|
||||||
|
|
||||||
{# hidden submit button to enable [enter] key #}
|
{% include "two_factor/_wizard_actions.html" %}
|
||||||
<input type="submit" value="" hidden />
|
</form>
|
||||||
|
</div>
|
||||||
{% include "two_factor/_wizard_actions.html" %}
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% extends "two_factor/_base_focus.html" %}
|
{% extends "two_factor/_base_focus.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
||||||
|
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
{% extends "two_factor/_base_focus.html" %}
|
{% extends "two_factor/_base_focus.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% block title %}{% trans "Disable Two-factor Authentication" %}{% endblock %}</h1>
|
<h1>{% block title %}{% trans "Disable Two-factor Authentication" %}{% endblock %}</h1>
|
||||||
<p>{% blocktrans trimmed %}You are about to disable two-factor authentication. This
|
<p>{% blocktrans trimmed %}You are about to disable two-factor authentication. This
|
||||||
weakens your account security, are you sure?{% endblocktrans %}</p>
|
weakens your account security, are you sure?{% endblocktrans %}</p>
|
||||||
|
<div class="input-group mb-3 d-flex justify-content-center">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<table>{{ form.as_table }}</table>
|
<div>{{ form|crispy }}</div>
|
||||||
<button class="btn btn-danger"
|
<button class="btn btn-danger" type="submit">{% trans "Disable" %}</button>
|
||||||
type="submit">{% trans "Disable" %}</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,62 +1,59 @@
|
|||||||
{% extends "two_factor/_base.html" %}
|
{% extends "two_factor/_base_focus.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load two_factor_tags %}
|
{% load two_factor_tags %}
|
||||||
|
|
||||||
{% block 'navigation' %}
|
{% block nav %}{% include 'includes/navigation.html' %}{% endblock nav %}
|
||||||
{% include 'collector/includes/navigation.html' %}
|
|
||||||
{% endblock 'navigation' %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% 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 %}
|
{% if available_phone_methods %}
|
||||||
<p>{% blocktrans with primary=default_device|as_action %}Primary method: {{ primary }}{% endblocktrans %}</p>
|
<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 %}
|
<h3>{% trans "Disable Two-Factor Authentication" %}</h3>
|
||||||
<h2>{% trans "Backup Phone Numbers" %}</h2>
|
<p>{% blocktrans trimmed %}However we strongly discourage you to do so, you can
|
||||||
<p>{% blocktrans trimmed %}If your primary method is not available, we are able to
|
also disable two-factor authentication for your account.{% endblocktrans %}</p>
|
||||||
send backup tokens to the phone numbers listed below.{% endblocktrans %}</p>
|
<p><a class="btn btn-secondary" href="{% url 'two_factor:disable' %}">
|
||||||
<ul>
|
{% trans "Disable Two-Factor Authentication" %}</a></p>
|
||||||
{% for phone in backup_phones %}
|
{% else %}
|
||||||
<li>
|
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||||
{{ phone|as_action }}
|
account. Enable two-factor authentication for enhanced account
|
||||||
<form method="post" action="{% url 'two_factor:phone_delete' phone.id %}"
|
security.{% endblocktrans %}</p>
|
||||||
onsubmit="return confirm({% trans 'Are you sure?' %})">
|
<p><a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||||
{% csrf_token %}
|
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||||
<button class="btn btn-sm btn-warning"
|
</p>
|
||||||
type="submit">{% trans "Unregister" %}</button>
|
{% endif %}
|
||||||
</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 %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|