Add: modelviewsets for all models
This commit is contained in:
		
							parent
							
								
									90d7e64db3
								
							
						
					
					
						commit
						3f37ed95ed
					
				| @ -0,0 +1,25 @@ | |||||||
|  | # 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), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -41,7 +41,7 @@ class Archive(models.Model): | |||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     def save(self, *args, **kwargs): |     def save(self, *args, **kwargs): | ||||||
|         # calculate sha 1 hash sum and write md5 field to db |         # calculate md5 hash sum and write md5 field to db | ||||||
|         with self.file.open('rb') as f: |         with self.file.open('rb') as f: | ||||||
|             md5 = hashlib.md5() |             md5 = hashlib.md5() | ||||||
|             for byte_block in iter(lambda: f.read(4096), b""): |             for byte_block in iter(lambda: f.read(4096), b""): | ||||||
| @ -72,7 +72,7 @@ class Ticket(models.Model): | |||||||
|     number = models.IntegerField(unique=True, db_index=True) |     number = models.IntegerField(unique=True, db_index=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) |     token = models.UUIDField(unique=True, default=uuid.uuid4, editable=False) | ||||||
|     attempts = models.IntegerField(default=5, validators=[ |     attempts = models.IntegerField(default=5, validators=[ | ||||||
|             MaxValueValidator(10), |             MaxValueValidator(10), | ||||||
|             MinValueValidator(0) |             MinValueValidator(0) | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								logs_collector/collector/permissions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								logs_collector/collector/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 | ||||||
| @ -1,11 +1,59 @@ | |||||||
| from rest_framework import serializers | from rest_framework import serializers | ||||||
| 
 | 
 | ||||||
| from .models import Archive | from .models import Archive, Platform, Ticket | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TimestampField(serializers.Field): | ||||||
|  |     def to_representation(self, value): | ||||||
|  |         return value.timestamp() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class JsTimestampField(serializers.Field): | ||||||
|  |     def to_representation(self, value): | ||||||
|  |         return round(value.timestamp()*1000) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PublicArchiveUploadSerializer(serializers.ModelSerializer): | class PublicArchiveUploadSerializer(serializers.ModelSerializer): | ||||||
|     ticket = serializers.ReadOnlyField(source='ticket.token') |  | ||||||
| 
 |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = Archive |         model = Archive | ||||||
|         fields = ['file', 'ticket'] |         fields = ['file', 'ticket'] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArchiveSerializer(serializers.ModelSerializer): | ||||||
|  |     time_create = JsTimestampField(read_only=True) | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         model = Archive | ||||||
|  |         fields = ['id', 'file', 'ticket', 'time_create'] | ||||||
|  | 
 | ||||||
|  |     def to_representation(self, instance): | ||||||
|  |         print(int(round(instance.time_create.timestamp()))) | ||||||
|  |         return super().to_representation(instance) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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' | ||||||
|  |         ] | ||||||
|  | |||||||
| @ -7,7 +7,9 @@ from . import views | |||||||
| app_name = 'collector' | app_name = 'collector' | ||||||
| 
 | 
 | ||||||
| router = routers.DefaultRouter() | router = routers.DefaultRouter() | ||||||
| router.register(r'archives', views.PublicArchiveUploadViewSet) | router.register(r'archives', views.ArchiveViewSet) | ||||||
|  | router.register(r'platforms', views.PlatformViewSet) | ||||||
|  | router.register(r'tickets', views.TicketViewSet) | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,8 +3,8 @@ import os | |||||||
| 
 | 
 | ||||||
| def logs_dir_path(instance, filename): | def logs_dir_path(instance, filename): | ||||||
|     # file will be uploaded to |     # file will be uploaded to | ||||||
|     # MEDIA_ROOT_FOR_SENSITIVE_FILES/<ticket>/<filename> |     # MEDIA_ROOT_FOR_SENSITIVE_FILES/<ticket-token>/<filename> | ||||||
|     return f'{instance.ticket}/{filename}' |     return f'{instance.ticket.number}/{filename}' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_file_size(file_path, unit='bytes'): | def get_file_size(file_path, unit='bytes'): | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import json | import json | ||||||
| from django.core.exceptions import ValidationError | from django.core.exceptions import ValidationError, ObjectDoesNotExist | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | from django.contrib.auth.mixins import LoginRequiredMixin | ||||||
| from django.http import FileResponse, JsonResponse | from django.http import FileResponse, JsonResponse | ||||||
| from django.views import generic | from django.views import generic | ||||||
| @ -8,17 +8,25 @@ from django.urls import reverse_lazy | |||||||
| from django.db.models import Q | from django.db.models import Q | ||||||
| 
 | 
 | ||||||
| from rest_framework import status | from rest_framework import status | ||||||
| from rest_framework.response import Response | # from rest_framework.decorators import action | ||||||
| from rest_framework.parsers import FormParser, MultiPartParser | from rest_framework.parsers import FormParser, MultiPartParser | ||||||
|  | from rest_framework.permissions import IsAuthenticated | ||||||
|  | from rest_framework.response import Response | ||||||
| 
 | 
 | ||||||
| from rest_framework import mixins | # from rest_framework import mixins | ||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework import viewsets | ||||||
| 
 | 
 | ||||||
| from .models import Archive, Ticket | from .models import Archive, Ticket, Platform | ||||||
| from .forms import TicketForm | from .forms import TicketForm | ||||||
| from .utils import PageTitleViewMixin, is_ajax | from .utils import PageTitleViewMixin, is_ajax | ||||||
|  | from .permissions import IsGuestUpload | ||||||
| 
 | 
 | ||||||
| from .serializers import PublicArchiveUploadSerializer | from .serializers import ( | ||||||
|  |     PublicArchiveUploadSerializer, | ||||||
|  |     ArchiveSerializer, | ||||||
|  |     PlatformSerializer, | ||||||
|  |     TicketSerializer | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ArchiveHandlerView(LoginRequiredMixin, SingleObjectMixin, generic.View): | class ArchiveHandlerView(LoginRequiredMixin, SingleObjectMixin, generic.View): | ||||||
| @ -174,15 +182,16 @@ class DeleteTicketHandler(SingleObjectMixin, generic.View): | |||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PublicArchiveUploadViewSet(mixins.CreateModelMixin, GenericViewSet): | class ArchiveViewSet(viewsets.ModelViewSet): | ||||||
|     queryset = Archive.objects.order_by('-time_create') |     queryset = Archive.objects.order_by('-time_create') | ||||||
|     serializer_class = PublicArchiveUploadSerializer |     serializer_class = ArchiveSerializer | ||||||
|     parser_classes = (MultiPartParser, FormParser) |     parser_classes = (MultiPartParser, FormParser) | ||||||
|  |     permission_classes = (IsGuestUpload, ) | ||||||
| 
 | 
 | ||||||
|     def create(self, request, *args, **kwargs): |     def create(self, request, *args, **kwargs): | ||||||
|         # ! upload-token protection: |         # ! upload-token protection: | ||||||
|         upload_token = request.headers.get('upload-token', '') |         upload_token = request.headers.get('upload-token', '') | ||||||
|         if upload_token: |         if not request.user.is_authenticated and upload_token: | ||||||
|             try: |             try: | ||||||
|                 bound_ticket = Ticket.objects.get(token=upload_token) |                 bound_ticket = Ticket.objects.get(token=upload_token) | ||||||
|                 if bound_ticket.resolved: |                 if bound_ticket.resolved: | ||||||
| @ -198,13 +207,15 @@ class PublicArchiveUploadViewSet(mixins.CreateModelMixin, GenericViewSet): | |||||||
|                 bound_ticket.attempts -= 1 |                 bound_ticket.attempts -= 1 | ||||||
|                 bound_ticket.save() |                 bound_ticket.save() | ||||||
|                 # ? mixin bound ticket to request.data from user |                 # ? mixin bound ticket to request.data from user | ||||||
|                 request.data['ticket'] = bound_ticket |                 request.data['ticket'] = bound_ticket.number | ||||||
|             except ValidationError: |                 # ? change serializer for guest user | ||||||
|  |                 self.serializer_class = PublicArchiveUploadSerializer | ||||||
|  |             except (ValidationError, ObjectDoesNotExist,): | ||||||
|                 return Response( |                 return Response( | ||||||
|                     {'error': f'token {upload_token} is not valid'}, |                     {'error': f'token {upload_token} is not valid'}, | ||||||
|                     status=status.HTTP_403_FORBIDDEN |                     status=status.HTTP_403_FORBIDDEN | ||||||
|                 ) |                 ) | ||||||
|         else: |         elif not request.user.is_authenticated: | ||||||
|             return Response( |             return Response( | ||||||
|                 {'error': 'Header Upload-Token is required'}, |                 {'error': 'Header Upload-Token is required'}, | ||||||
|                 status=status.HTTP_401_UNAUTHORIZED |                 status=status.HTTP_401_UNAUTHORIZED | ||||||
| @ -220,5 +231,16 @@ class PublicArchiveUploadViewSet(mixins.CreateModelMixin, GenericViewSet): | |||||||
|             headers=headers |             headers=headers | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def perform_create(self, serializer): | 
 | ||||||
|         serializer.save(ticket=self.request.data['ticket']) | 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, ) | ||||||
|  | |||||||
| @ -135,3 +135,18 @@ MEDIA_URL_FOR_SENSITIVE_FILES = '/archives/' | |||||||
| CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" | CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" | ||||||
| 
 | 
 | ||||||
| CRISPY_TEMPLATE_PACK = "bootstrap5" | CRISPY_TEMPLATE_PACK = "bootstrap5" | ||||||
|  | 
 | ||||||
|  | # https://www.django-rest-framework.org/api-guide/settings/ | ||||||
|  | REST_FRAMEWORK = { | ||||||
|  |     'DEFAULT_RENDERER_CLASSES': [ | ||||||
|  |         'rest_framework.renderers.JSONRenderer', | ||||||
|  |         'rest_framework.renderers.BrowsableAPIRenderer', | ||||||
|  |     ], | ||||||
|  |     'DEFAULT_PARSER_CLASSES': [ | ||||||
|  |         'rest_framework.parsers.JSONParser', | ||||||
|  |         'rest_framework.renderers.BrowsableAPIRenderer', | ||||||
|  |         'rest_framework.parsers.MultiPartParser' | ||||||
|  |     ], | ||||||
|  |     # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',  # noqa:E501 | ||||||
|  |     # 'PAGE_SIZE': 3, | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user