mirror of
https://github.com/MOIS3Y/logs-collector.git
synced 2025-09-13 13:13:01 +02:00
Refactoring: using the apps directory is redundant
This commit is contained in:
0
logs_collector/collector/api/__init__.py
Normal file
0
logs_collector/collector/api/__init__.py
Normal file
39
logs_collector/collector/api/filters.py
Normal file
39
logs_collector/collector/api/filters.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django_filters.rest_framework import (
|
||||
CharFilter,
|
||||
FilterSet,
|
||||
NumberFilter,
|
||||
)
|
||||
from django_filters import widgets
|
||||
|
||||
from collector.models import Archive, Ticket
|
||||
from .utils import DateTimeFilterMixin
|
||||
|
||||
|
||||
class ArchiveFilter(DateTimeFilterMixin, FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = Archive
|
||||
fields = {
|
||||
'id': ['exact', 'in', 'lte', 'gte'],
|
||||
'ticket': ['exact', 'in', 'lte', 'gte'],
|
||||
'time_create': ['exact', 'lte', 'gte']
|
||||
}
|
||||
|
||||
|
||||
class TicketFilter(DateTimeFilterMixin, FilterSet):
|
||||
number = NumberFilter(
|
||||
field_name='number',
|
||||
widget=widgets.CSVWidget(),
|
||||
)
|
||||
user = CharFilter(
|
||||
field_name='user__username'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Ticket
|
||||
fields = {
|
||||
'id': ['exact', 'in', 'lte', 'gte'],
|
||||
'number': ['exact', 'contains', 'in', 'lte', 'gte'],
|
||||
'resolved': ['exact'],
|
||||
'user': ['exact']
|
||||
}
|
13
logs_collector/collector/api/permissions.py
Normal file
13
logs_collector/collector/api/permissions.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from rest_framework import permissions
|
||||
|
||||
|
||||
class IsGuestUpload(permissions.BasePermission):
|
||||
"""
|
||||
Special permission class for the ability to upload attachments
|
||||
to an unauthorized user using a ticket token
|
||||
"""
|
||||
def has_permission(self, request, view):
|
||||
if request.method in ('HEAD', 'OPTIONS', 'POST',):
|
||||
return True
|
||||
|
||||
return request.user.is_authenticated
|
60
logs_collector/collector/api/serializers.py
Normal file
60
logs_collector/collector/api/serializers.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from drf_spectacular.openapi import OpenApiTypes
|
||||
|
||||
from collector.models import Archive, Platform, Ticket
|
||||
|
||||
|
||||
@extend_schema_field(OpenApiTypes.NUMBER)
|
||||
class TimestampField(serializers.Field):
|
||||
def to_representation(self, value) -> int:
|
||||
return value.timestamp()
|
||||
|
||||
|
||||
@extend_schema_field(OpenApiTypes.NUMBER)
|
||||
class JsTimestampField(serializers.Field):
|
||||
def to_representation(self, value) -> int:
|
||||
return round(value.timestamp()*1000)
|
||||
|
||||
|
||||
class PublicArchiveUploadSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Archive
|
||||
fields = ['file', 'ticket']
|
||||
|
||||
|
||||
class ArchiveSerializer(serializers.ModelSerializer):
|
||||
time_create = JsTimestampField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Archive
|
||||
fields = ['id', 'file', 'ticket', 'time_create']
|
||||
|
||||
|
||||
class PlatformSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Platform
|
||||
fields = ['id', 'name', 'pretty_name']
|
||||
|
||||
|
||||
class TicketSerializer(serializers.ModelSerializer):
|
||||
time_create = JsTimestampField(read_only=True)
|
||||
time_update = JsTimestampField(read_only=True)
|
||||
token = serializers.UUIDField(read_only=True)
|
||||
user = serializers.ReadOnlyField(source='user.username')
|
||||
|
||||
class Meta:
|
||||
model = Ticket
|
||||
fields = [
|
||||
'id',
|
||||
'number',
|
||||
'resolved',
|
||||
'token',
|
||||
'attempts',
|
||||
'platform',
|
||||
'time_create',
|
||||
'time_update',
|
||||
'user'
|
||||
]
|
21
logs_collector/collector/api/urls.py
Normal file
21
logs_collector/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/collector/api/utils.py
Normal file
20
logs_collector/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'
|
||||
)
|
94
logs_collector/collector/api/views.py
Normal file
94
logs_collector/collector/api/views.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
||||
|
||||
from rest_framework import status
|
||||
# from rest_framework.decorators import action
|
||||
from rest_framework.parsers import FormParser, MultiPartParser
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import viewsets
|
||||
from rest_framework import filters
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
||||
from collector.models import Archive, Ticket, Platform
|
||||
|
||||
from .filters import ArchiveFilter, TicketFilter
|
||||
from .permissions import IsGuestUpload
|
||||
from .serializers import (
|
||||
PublicArchiveUploadSerializer,
|
||||
ArchiveSerializer,
|
||||
PlatformSerializer,
|
||||
TicketSerializer
|
||||
)
|
||||
|
||||
|
||||
class ArchiveViewSet(viewsets.ModelViewSet):
|
||||
queryset = Archive.objects.order_by('-time_create')
|
||||
serializer_class = ArchiveSerializer
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
permission_classes = (IsGuestUpload, )
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = ArchiveFilter
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
# ! upload-token protection:
|
||||
upload_token = request.headers.get('upload-token', '')
|
||||
if not request.user.is_authenticated and upload_token:
|
||||
try:
|
||||
bound_ticket = Ticket.objects.get(token=upload_token)
|
||||
if bound_ticket.resolved:
|
||||
return Response(
|
||||
{'error': f'ticket {upload_token} already resolved'},
|
||||
status=status.HTTP_423_LOCKED
|
||||
)
|
||||
if bound_ticket.attempts <= 0:
|
||||
return Response(
|
||||
{'error': f'token {upload_token} expired'},
|
||||
status=status.HTTP_423_LOCKED
|
||||
)
|
||||
bound_ticket.attempts -= 1
|
||||
bound_ticket.save()
|
||||
# ? mixin bound ticket number to request.data from user
|
||||
request.data['ticket'] = bound_ticket.number
|
||||
# ? change serializer for guest user
|
||||
self.serializer_class = PublicArchiveUploadSerializer
|
||||
except (ValidationError, ObjectDoesNotExist,):
|
||||
return Response(
|
||||
{'error': f'token {upload_token} is not valid'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
elif not request.user.is_authenticated:
|
||||
return Response(
|
||||
{'error': 'Header Upload-Token is required'},
|
||||
status=status.HTTP_401_UNAUTHORIZED
|
||||
)
|
||||
# ! default create method:
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
self.perform_create(serializer)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(
|
||||
serializer.data,
|
||||
status=status.HTTP_201_CREATED,
|
||||
headers=headers
|
||||
)
|
||||
|
||||
|
||||
class PlatformViewSet(viewsets.ModelViewSet):
|
||||
queryset = Platform.objects.all()
|
||||
lookup_field = 'name'
|
||||
serializer_class = PlatformSerializer
|
||||
permission_classes = (IsAuthenticated, )
|
||||
|
||||
|
||||
class TicketViewSet(viewsets.ModelViewSet):
|
||||
queryset = Ticket.objects.order_by('-time_create')
|
||||
lookup_field = 'number'
|
||||
serializer_class = TicketSerializer
|
||||
permission_classes = (IsAuthenticated, )
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
|
||||
filterset_class = TicketFilter
|
||||
search_fields = ['number']
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(user=self.request.user)
|
Reference in New Issue
Block a user