Compare commits
	
		
			No commits in common. "b2bea677efba67522452cf0ce395c47d6ab909c1" and "a1679b69c3b83aca710f5f0de0790513df46f262" have entirely different histories.
		
	
	
		
			b2bea677ef
			...
			a1679b69c3
		
	
		
| @ -29,8 +29,7 @@ ARG VERSION=0.1.0 \ | |||||||
|     USER_NAME=collector \ |     USER_NAME=collector \ | ||||||
|     USER_GROUP=collector \ |     USER_GROUP=collector \ | ||||||
|     APP_UID=1000 \ |     APP_UID=1000 \ | ||||||
|     APP_GID=1000 \ |     APP_GID=1000 | ||||||
|     HEALTHCHECK_PATH=api/v1/check/health/ |  | ||||||
| 
 | 
 | ||||||
| # copy app dependences | # copy app dependences | ||||||
| COPY --from=base /usr/local/lib/python3.10/site-packages/ /usr/local/lib/python3.10/site-packages/ | COPY --from=base /usr/local/lib/python3.10/site-packages/ /usr/local/lib/python3.10/site-packages/ | ||||||
| @ -62,7 +61,7 @@ LABEL maintainer="s.zhukovskii@ispsystem.com" | |||||||
| LABEL me.zhukovsky.logs-collector.version=v${VERSION} | LABEL me.zhukovsky.logs-collector.version=v${VERSION} | ||||||
| 
 | 
 | ||||||
| # call the health check endpoint of app | # call the health check endpoint of app | ||||||
| HEALTHCHECK CMD curl --fail http://localhost:${WEB_PORT}/${HEALTHCHECK_PATH} || exit 1 | HEALTHCHECK CMD curl --fail http://localhost:${WEB_PORT} || exit 1 | ||||||
| 
 | 
 | ||||||
| # run app | # run app | ||||||
| ENTRYPOINT [ "sh", "entrypoint.sh" ] | ENTRYPOINT [ "sh", "entrypoint.sh" ] | ||||||
|  | |||||||
| @ -11,15 +11,3 @@ class IsGuestUpload(permissions.BasePermission): | |||||||
|             return True |             return True | ||||||
| 
 | 
 | ||||||
|         return request.user.is_authenticated |         return request.user.is_authenticated | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class IsGuestCheckUrls(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', 'GET',): |  | ||||||
|             return True |  | ||||||
| 
 |  | ||||||
|         return request.user.is_authenticated |  | ||||||
|  | |||||||
| @ -67,25 +67,3 @@ class StorageInfoSerializer(serializers.Serializer): | |||||||
|     free = serializers.IntegerField(read_only=True) |     free = serializers.IntegerField(read_only=True) | ||||||
|     used_percent = serializers.IntegerField(read_only=True) |     used_percent = serializers.IntegerField(read_only=True) | ||||||
|     status = serializers.CharField(read_only=True) |     status = serializers.CharField(read_only=True) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class TokenStateRootSerializer(serializers.Serializer): |  | ||||||
|     info = serializers.CharField(read_only=True, default="manual message") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class TokenStateSerializer(serializers.ModelSerializer): |  | ||||||
|     token = serializers.UUIDField(read_only=True) |  | ||||||
|     attempts = serializers.IntegerField(read_only=True) |  | ||||||
|     resolved = serializers.BooleanField(read_only=True) |  | ||||||
| 
 |  | ||||||
|     class Meta: |  | ||||||
|         model = Ticket |  | ||||||
|         fields = [ |  | ||||||
|             'token', |  | ||||||
|             'attempts', |  | ||||||
|             'resolved' |  | ||||||
|         ] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class AppHealthInfoSerializer(serializers.Serializer): |  | ||||||
|     status = serializers.CharField(read_only=True, default="ok") |  | ||||||
|  | |||||||
| @ -15,31 +15,8 @@ router.register(r'archives', views.ArchiveViewSet) | |||||||
| router.register(r'platforms', views.PlatformViewSet) | router.register(r'platforms', views.PlatformViewSet) | ||||||
| router.register(r'tickets', views.TicketViewSet) | router.register(r'tickets', views.TicketViewSet) | ||||||
| 
 | 
 | ||||||
| check_urlpatterns = [ |  | ||||||
|     path( |  | ||||||
|         'health/', |  | ||||||
|         views.AppHealthInfo.as_view(), |  | ||||||
|         name='app-info' |  | ||||||
|     ), |  | ||||||
|     path( |  | ||||||
|         'storage/', |  | ||||||
|         views.StorageInfo.as_view(), |  | ||||||
|         name='storage-info' |  | ||||||
|     ), |  | ||||||
|     path( |  | ||||||
|         'token/', |  | ||||||
|         views.TokenStateRoot.as_view(), |  | ||||||
|         name='token-root' |  | ||||||
|     ), |  | ||||||
|     path( |  | ||||||
|         'token/<str:token>', |  | ||||||
|         views.TokenStateInfo.as_view(), |  | ||||||
|         name='token-info' |  | ||||||
|     ), |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     # CRUD: |     # CRUD: | ||||||
|     path('v1/', include(router.urls)), |     path('v1/', include(router.urls)), | ||||||
|     path('v1/check/', include(check_urlpatterns)), |     path('v1/storage/', views.StorageInfo.as_view(), name='storage-info'), | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -10,7 +10,9 @@ from rest_framework.parsers import ( | |||||||
| ) | ) | ||||||
| from rest_framework.permissions import IsAuthenticated | from rest_framework.permissions import IsAuthenticated | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework import filters, generics, views, viewsets | from rest_framework import viewsets | ||||||
|  | from rest_framework import views | ||||||
|  | from rest_framework import filters | ||||||
| 
 | 
 | ||||||
| from django_filters.rest_framework import DjangoFilterBackend | from django_filters.rest_framework import DjangoFilterBackend | ||||||
| 
 | 
 | ||||||
| @ -21,16 +23,13 @@ from collector.models import Archive, Ticket, Platform | |||||||
| from collector.utils.helpers import get_mount_fs_info | from collector.utils.helpers import get_mount_fs_info | ||||||
| 
 | 
 | ||||||
| from .filters import ArchiveFilter, TicketFilter | from .filters import ArchiveFilter, TicketFilter | ||||||
| from .permissions import IsGuestUpload, IsGuestCheckUrls | from .permissions import IsGuestUpload | ||||||
| from .serializers import ( | from .serializers import ( | ||||||
|     PublicArchiveUploadSerializer, |     PublicArchiveUploadSerializer, | ||||||
|     ArchiveSerializer, |     ArchiveSerializer, | ||||||
|     PlatformSerializer, |     PlatformSerializer, | ||||||
|     TicketSerializer, |     TicketSerializer, | ||||||
|     StorageInfoSerializer, |     StorageInfoSerializer, | ||||||
|     TokenStateSerializer, |  | ||||||
|     AppHealthInfoSerializer, |  | ||||||
|     TokenStateRootSerializer, |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -83,35 +82,29 @@ class ArchiveViewSet(viewsets.ModelViewSet): | |||||||
|                 bound_ticket = Ticket.objects.get(token=upload_token) |                 bound_ticket = Ticket.objects.get(token=upload_token) | ||||||
|                 if bound_ticket.resolved: |                 if bound_ticket.resolved: | ||||||
|                     return Response( |                     return Response( | ||||||
|                         {'detail': f'ticket {bound_ticket} already resolved'}, |                         {'error': f'ticket {bound_ticket} already resolved'}, | ||||||
|                         status=status.HTTP_423_LOCKED |                         status=status.HTTP_423_LOCKED | ||||||
|                     ) |                     ) | ||||||
|                 if bound_ticket.attempts <= 0: |                 if bound_ticket.attempts <= 0: | ||||||
|                     return Response( |                     return Response( | ||||||
|                         {'detail': f'token {upload_token} expired'}, |                         {'error': f'token {upload_token} expired'}, | ||||||
|                         status=status.HTTP_423_LOCKED |                         status=status.HTTP_423_LOCKED | ||||||
|                     ) |                     ) | ||||||
|                 bound_ticket.attempts -= 1 |                 bound_ticket.attempts -= 1 | ||||||
|                 bound_ticket.save() |                 bound_ticket.save() | ||||||
|                 # ? mixin bound ticket number to request.data from user |                 # ? mixin bound ticket number to request.data from user | ||||||
|                 try: |  | ||||||
|                 request.data['ticket'] = bound_ticket.number |                 request.data['ticket'] = bound_ticket.number | ||||||
|                 except AttributeError: |  | ||||||
|                     return Response( |  | ||||||
|                         {'detail': 'Bad Request'}, |  | ||||||
|                         status=status.HTTP_400_BAD_REQUEST |  | ||||||
|                     ) |  | ||||||
|                 # ? change serializer for guest user |                 # ? change serializer for guest user | ||||||
|                 if not request.user.is_authenticated: |                 if not request.user.is_authenticated: | ||||||
|                     self.serializer_class = PublicArchiveUploadSerializer |                     self.serializer_class = PublicArchiveUploadSerializer | ||||||
|             except (ValidationError, ObjectDoesNotExist,): |             except (ValidationError, ObjectDoesNotExist,): | ||||||
|                 return Response( |                 return Response( | ||||||
|                     {'detail': 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: |         else: | ||||||
|             return Response( |             return Response( | ||||||
|                 {'detail': 'Header Upload-Token is required'}, |                 {'error': 'Header Upload-Token is required'}, | ||||||
|                 status=status.HTTP_401_UNAUTHORIZED |                 status=status.HTTP_401_UNAUTHORIZED | ||||||
|             ) |             ) | ||||||
|         # ! default create method: |         # ! default create method: | ||||||
| @ -179,40 +172,3 @@ class StorageInfo(views.APIView): | |||||||
|     ) |     ) | ||||||
|     def get(self, request): |     def get(self, request): | ||||||
|         return Response(get_mount_fs_info(settings.DATA_DIR)) |         return Response(get_mount_fs_info(settings.DATA_DIR)) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class TokenStateRoot(views.APIView): |  | ||||||
|     """ Show the message of a specific upload token URL""" |  | ||||||
|     permission_classes = (IsGuestCheckUrls,) |  | ||||||
| 
 |  | ||||||
|     @extend_schema( |  | ||||||
|         responses=TokenStateRootSerializer, |  | ||||||
|         summary='Show info message how get token status' |  | ||||||
|     ) |  | ||||||
|     def get(self, request): |  | ||||||
|         message = "to find out the status of the token, place it in the URL" |  | ||||||
|         return Response({"detail": message}, status=status.HTTP_303_SEE_OTHER) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @extend_schema_view( |  | ||||||
|     get=extend_schema( |  | ||||||
|         summary='Show the status of a specific upload token' |  | ||||||
|     ) |  | ||||||
| ) |  | ||||||
| class TokenStateInfo(generics.RetrieveAPIView): |  | ||||||
|     """ Show the status of a specific upload token""" |  | ||||||
|     queryset = Ticket.objects.order_by('-time_create') |  | ||||||
|     lookup_field = 'token' |  | ||||||
|     serializer_class = TokenStateSerializer |  | ||||||
|     permission_classes = (IsGuestCheckUrls,) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class AppHealthInfo(views.APIView): |  | ||||||
|     permission_classes = (IsGuestCheckUrls,) |  | ||||||
| 
 |  | ||||||
|     @extend_schema( |  | ||||||
|         responses=AppHealthInfoSerializer, |  | ||||||
|         summary='Show app status' |  | ||||||
|     ) |  | ||||||
|     def get(self, request): |  | ||||||
|         return Response({'status': 'ok'}, status=status.HTTP_200_OK) |  | ||||||
|  | |||||||
| @ -1,59 +0,0 @@ | |||||||
| import os |  | ||||||
| from django.core.management.base import BaseCommand |  | ||||||
| from django.apps import apps |  | ||||||
| from django.db.models import Q |  | ||||||
| from django.conf import settings |  | ||||||
| from django.db.models import FileField |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Command(BaseCommand): |  | ||||||
|     # HELP MESSAGE: |  | ||||||
|     help_part1 = 'This command deletes all media files from' |  | ||||||
|     help_part2 = 'the MEDIA_ROOT directory which are no longer referenced' |  | ||||||
|     help_part3 = 'by any of the models from installed_apps' |  | ||||||
|     help = f'{help_part1} {help_part2} {help_part3}' |  | ||||||
| 
 |  | ||||||
|     def handle(self, *args, **options): |  | ||||||
|         all_models = apps.get_models() |  | ||||||
|         physical_files = set() |  | ||||||
|         db_files = set() |  | ||||||
|         # Get all files from the database |  | ||||||
|         for model in all_models: |  | ||||||
|             file_fields = [] |  | ||||||
|             filters = Q() |  | ||||||
|             for f_ in model._meta.fields: |  | ||||||
|                 if isinstance(f_, FileField): |  | ||||||
|                     file_fields.append(f_.name) |  | ||||||
|                     is_null = {'{}__isnull'.format(f_.name): True} |  | ||||||
|                     is_empty = {'{}__exact'.format(f_.name): ''} |  | ||||||
|                     filters &= Q(**is_null) | Q(**is_empty) |  | ||||||
|             # only retrieve the models which have non-empty, |  | ||||||
|             # non-null file fields |  | ||||||
|             if file_fields: |  | ||||||
|                 files = model.objects.exclude(filters).values_list( |  | ||||||
|                     *file_fields, |  | ||||||
|                     flat=True |  | ||||||
|                 ).distinct() |  | ||||||
|                 db_files.update(files) |  | ||||||
|         # Get all files from the MEDIA_ROOT, recursively |  | ||||||
|         media_root = getattr(settings, 'MEDIA_ROOT', None) |  | ||||||
|         if media_root is not None: |  | ||||||
|             for relative_root, dirs, files in os.walk(media_root): |  | ||||||
|                 for file_ in files: |  | ||||||
|                     # Compute the relative file path to the media directory, |  | ||||||
|                     # so it can be compared to the values from the db |  | ||||||
|                     relative_file = os.path.join( |  | ||||||
|                         os.path.relpath(relative_root, media_root), file_ |  | ||||||
|                     ) |  | ||||||
|                     physical_files.add(relative_file) |  | ||||||
|         # Compute the difference and delete those files |  | ||||||
|         deletables = physical_files - db_files |  | ||||||
|         if deletables: |  | ||||||
|             for file_ in deletables: |  | ||||||
|                 os.remove(os.path.join(media_root, file_)) |  | ||||||
|             # Bottom-up - delete all empty folders |  | ||||||
|             for relative_root, dirs, files in os.walk( |  | ||||||
|                     media_root, topdown=False): |  | ||||||
|                 for dir_ in dirs: |  | ||||||
|                     if not os.listdir(os.path.join(relative_root, dir_)): |  | ||||||
|                         os.rmdir(os.path.join(relative_root, dir_)) |  | ||||||
| @ -1,24 +0,0 @@ | |||||||
| from django.http import HttpResponse |  | ||||||
| from django.template import loader |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class HttpResponseNotAllowedMiddleware: |  | ||||||
|     def __init__(self, get_response): |  | ||||||
|         self.get_response = get_response |  | ||||||
|         # One-time configuration and initialization. |  | ||||||
| 
 |  | ||||||
|     def __call__(self, request): |  | ||||||
| 
 |  | ||||||
|         # Code to be executed for each request before |  | ||||||
|         # the view (and later middleware) are called. |  | ||||||
| 
 |  | ||||||
|         response = self.get_response(request) |  | ||||||
| 
 |  | ||||||
|         # Code to be executed for each request/response after |  | ||||||
|         # the view is called. |  | ||||||
|         if response.status_code == 405: |  | ||||||
|             context = {} |  | ||||||
|             template = loader.get_template('405.html') |  | ||||||
|             return HttpResponse(template.render(context, request)) |  | ||||||
| 
 |  | ||||||
|         return response |  | ||||||
| @ -66,18 +66,4 @@ const updateStorageInfo = () => { | |||||||
|     }); |     }); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const genAlertMessage = ( | export {sizify, updateBsTooltip, updateStorageInfo}; | ||||||
|         alertMessage='Success message', |  | ||||||
|         alertType='success', |  | ||||||
|         extraClass='' |  | ||||||
|         ) => { |  | ||||||
|     let alertMessageHTML = [ |  | ||||||
|         `<div class="alert alert-${alertType} alert-dismissible ${extraClass}" role="alert">`, |  | ||||||
|         `   <div>${alertMessage}</div>`, |  | ||||||
|         '   <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>', |  | ||||||
|         '</div>' |  | ||||||
|     ].join('') |  | ||||||
|     return alertMessageHTML |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export {sizify, updateBsTooltip, updateStorageInfo, genAlertMessage}; |  | ||||||
|  | |||||||
| @ -1,33 +1,23 @@ | |||||||
| import {updateStorageInfo, genAlertMessage} from "./helpers.js"; | import {updateStorageInfo} from "./helpers.js"; | ||||||
| 
 | 
 | ||||||
| $(function () { | $(function () { | ||||||
|     // set global variables:
 |  | ||||||
|     const uploadForm = document.getElementById('upload_form'); |     const uploadForm = document.getElementById('upload_form'); | ||||||
|     const inputFile = document.getElementById('id_file'); |     const input_file = document.getElementById('id_file'); | ||||||
|     const progressBar = document.getElementById('progress'); |     const progress_bar = document.getElementById('progress'); | ||||||
|     const alertContainer = document.getElementById('alert'); |     const alert_container = document.getElementById('alert'); | ||||||
|     // get upload form:
 |      | ||||||
|     $("#upload_form").submit(function(e){ |     $("#upload_form").submit(function(e){ | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         // collect request data:
 |         // $form = $(this)
 | ||||||
|         let formData = new FormData(this); |         let formData = new FormData(this); | ||||||
|         let uploadToken = formData.get("token") |         let upload_token = formData.get("token") | ||||||
|         // generate the URL for token validation:
 |         const media_data = input_file.files[0]; | ||||||
|         let tokenStatusUrl = [ |         if(media_data != null){ | ||||||
|             progressBar.getAttribute('token-status-url'), |             progress_bar.classList.remove("not-visible"); | ||||||
|             uploadToken |  | ||||||
|         ].join('') |  | ||||||
|         // init upload file func:
 |  | ||||||
|         const uploadFile = () => { |  | ||||||
|             // toggle visible progress bar:
 |  | ||||||
|             const mediaData = inputFile.files[0]; |  | ||||||
|             if(mediaData != null){ |  | ||||||
|                 progressBar.classList.remove("not-visible"); |  | ||||||
|         } |         } | ||||||
|             // upload file (chunk) xrh request:
 |  | ||||||
|         $.ajax({ |         $.ajax({ | ||||||
|             type: 'POST', |             type: 'POST', | ||||||
|                 url: progressBar.getAttribute("upload-url"), |             url: progress_bar.getAttribute("upload-url"), | ||||||
|             data: formData, |             data: formData, | ||||||
|             dataType: 'json', |             dataType: 'json', | ||||||
|             xhr:function(){ |             xhr:function(){ | ||||||
| @ -37,7 +27,7 @@ $(function () { | |||||||
|                     if(e.lengthComputable){ |                     if(e.lengthComputable){ | ||||||
|                         const percentProgress = (e.loaded/e.total)*100; |                         const percentProgress = (e.loaded/e.total)*100; | ||||||
|                         console.log(percentProgress); |                         console.log(percentProgress); | ||||||
|                             progressBar.innerHTML = ` |                         progress_bar.innerHTML = ` | ||||||
|                         <div |                         <div | ||||||
|                             class="progress-bar progress-bar-striped progress-bar-animated" |                             class="progress-bar progress-bar-striped progress-bar-animated" | ||||||
|                             style="width: ${percentProgress}%" |                             style="width: ${percentProgress}%" | ||||||
| @ -47,20 +37,22 @@ $(function () { | |||||||
|                 }); |                 }); | ||||||
|                 return xhr |                 return xhr | ||||||
|             }, |             }, | ||||||
|                 // set auth method:
 |  | ||||||
|             beforeSend: function(xhr) { |             beforeSend: function(xhr) { | ||||||
|                     if (uploadToken) { |                 if (upload_token) { | ||||||
|                         xhr.setRequestHeader("Upload-Token", uploadToken); |                     xhr.setRequestHeader("Upload-Token", upload_token); | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             success: function(data, textStatus, jqXHR){ |             success: function(data, textStatus, jqXHR){ | ||||||
|                     alertContainer.innerHTML = genAlertMessage( |                 console.log(jqXHR.status); | ||||||
|                         'The file has been successfully uploaded to the server. Thank you!', |                 let type = "success"; | ||||||
|                         'success', |                 alert_container.innerHTML = [ | ||||||
|                         'col-lg-6' |                     `<div class="alert alert-${type} alert-dismissible col-lg-6" role="alert">`, | ||||||
|                     ) |                     `   <div>The file has been successfully uploaded to the server. Thank you!</div>`, | ||||||
|  |                     '   <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>', | ||||||
|  |                     '</div>' | ||||||
|  |                 ].join('') | ||||||
|                 uploadForm.reset() |                 uploadForm.reset() | ||||||
|                     progressBar.classList.add('not-visible') |                 progress_bar.classList.add('not-visible') | ||||||
|                 try { |                 try { | ||||||
|                     updateStorageInfo(); |                     updateStorageInfo(); | ||||||
|                 } catch (error) { |                 } catch (error) { | ||||||
| @ -68,71 +60,29 @@ $(function () { | |||||||
|                 }; |                 }; | ||||||
|             }, |             }, | ||||||
|             error: function(jqXHR, textStatus, errorThrown){ |             error: function(jqXHR, textStatus, errorThrown){ | ||||||
|                     let errorMessage = "Unexpected error. Try again please" |                 console.log(jqXHR); | ||||||
|                     if (jqXHR.status === 423 || jqXHR.status === 403) { |                 let type = "danger"; | ||||||
|                         errorMessage = `Error ${jqXHR.status} <br> ${jqXHR.responseJSON.detail}` |                 let error_message = "Unexpected error. Try again please" | ||||||
|  |                 if (jqXHR.status === 423) { | ||||||
|  |                     error_message = `Error ${jqXHR.status}: ${jqXHR.responseJSON.error}` | ||||||
|  |                 } | ||||||
|  |                 if (jqXHR.status === 403) { | ||||||
|  |                     error_message = `Error ${jqXHR.status}: ${jqXHR.responseJSON.error}` | ||||||
|                 } |                 } | ||||||
|                 if (jqXHR.status === 401) { |                 if (jqXHR.status === 401) { | ||||||
|                         errorMessage = `Error ${jqXHR.status} <br> The token field cannot be empty` |                     error_message = 'The token field cannot be empty' | ||||||
|                 } |                 } | ||||||
|                     if (jqXHR.status === 400) { |                 alert_container.innerHTML = [ | ||||||
|                         errorMessage = `Error ${jqXHR.status} <br> ${jqXHR.responseJSON.detail}` |                     `<div class="alert alert-${type} alert-dismissible col-lg-6" role="alert">`, | ||||||
|                     } |                     `   <div>${error_message}</div>`, | ||||||
|                     alertContainer.innerHTML = genAlertMessage( |                     '   <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>', | ||||||
|                         errorMessage, |                     '</div>' | ||||||
|                         'danger', |                 ].join('') | ||||||
|                         'col-lg-6' |                 progress_bar.classList.add('not-visible') | ||||||
|                     ) |  | ||||||
|                     progressBar.classList.add('not-visible') |  | ||||||
|             }, |             }, | ||||||
|             cache: false, |             cache: false, | ||||||
|             contentType: false, |             contentType: false, | ||||||
|             processData: false, |             processData: false, | ||||||
|         }); |         }); | ||||||
|         } |  | ||||||
|         // check token status and upload file if token valid:
 |  | ||||||
|         $.ajax({ |  | ||||||
|             type: 'GET', |  | ||||||
|             url: tokenStatusUrl, |  | ||||||
|             dataType: "json", |  | ||||||
|             success: function (data, textStatus, jqXHR) { |  | ||||||
|                 if (data.attempts === 0) { |  | ||||||
|                     alertContainer.innerHTML = genAlertMessage( |  | ||||||
|                         `Error 423 <br> Token: ${uploadToken} expired`, |  | ||||||
|                         'danger', |  | ||||||
|                         'col-lg-6' |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
|                 else if (data.resolved === true) { |  | ||||||
|                     alertContainer.innerHTML = genAlertMessage( |  | ||||||
|                         `Error 423 <br> Ticket bound with token: ${uploadToken} <br> already resolved`, |  | ||||||
|                         'danger', |  | ||||||
|                         'col-lg-6' |  | ||||||
|                     ); |  | ||||||
|                 } else { |  | ||||||
|                     alertContainer.innerHTML = genAlertMessage( |  | ||||||
|                         `Token: ${uploadToken} is valid. <br> Starting to upload...`, |  | ||||||
|                         'success', |  | ||||||
|                         'col-lg-6' |  | ||||||
|                     ); |  | ||||||
|                     uploadFile(); |  | ||||||
|                 }; |  | ||||||
|             }, |  | ||||||
|             error: function(jqXHR){ |  | ||||||
|                 if (jqXHR.responseJSON.detail) { |  | ||||||
|                     alertContainer.innerHTML = genAlertMessage( |  | ||||||
|                         `Error 403 <br> Token: ${uploadToken} is not valid`, |  | ||||||
|                         'danger', |  | ||||||
|                         'col-lg-6' |  | ||||||
|                     ) |  | ||||||
|                 } else { |  | ||||||
|                     alertContainer.innerHTML = genAlertMessage( |  | ||||||
|                         `Unexpected error. Try again please`, |  | ||||||
|                         'danger', |  | ||||||
|                         'col-lg-6' |  | ||||||
|                     ) |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|         }); |  | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -35,8 +35,7 @@ | |||||||
|           <div |           <div | ||||||
|             id="progress" |             id="progress" | ||||||
|             upload-url="{% url 'collector_api:archive-list' %}" |             upload-url="{% url 'collector_api:archive-list' %}" | ||||||
|             token-status-url="{% url 'collector_api:token-root' %}" |             class="progress" | ||||||
|             class="progress not-visible" |  | ||||||
|             role="progressbar" |             role="progressbar" | ||||||
|             aria-label="Example 20px high" |             aria-label="Example 20px high" | ||||||
|             aria-valuenow="25" |             aria-valuenow="25" | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | from django.contrib.auth.mixins import LoginRequiredMixin | ||||||
| from django.http import FileResponse, Http404 | from django.http import FileResponse | ||||||
| from django.views import generic | from django.views import generic | ||||||
| from django.views.generic.detail import SingleObjectMixin | from django.views.generic.detail import SingleObjectMixin | ||||||
| from django.db.models import Q | from django.db.models import Q | ||||||
| @ -35,10 +35,6 @@ class ArchiveHandlerView( | |||||||
| 
 | 
 | ||||||
|     def get(self, request, path): |     def get(self, request, path): | ||||||
|         self.object = self.get_object() |         self.object = self.get_object() | ||||||
|         try: |  | ||||||
|             self.object.file.size |  | ||||||
|         except FileNotFoundError: |  | ||||||
|             raise Http404(f'File: {self.object.file} not found') |  | ||||||
|         return FileResponse(self.object.file) |         return FileResponse(self.object.file) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -89,7 +89,6 @@ MIDDLEWARE = [ | |||||||
|     'django.contrib.messages.middleware.MessageMiddleware', |     'django.contrib.messages.middleware.MessageMiddleware', | ||||||
|     'django.middleware.clickjacking.XFrameOptionsMiddleware', |     'django.middleware.clickjacking.XFrameOptionsMiddleware', | ||||||
|     'whitenoise.middleware.WhiteNoiseMiddleware', |     'whitenoise.middleware.WhiteNoiseMiddleware', | ||||||
|     'collector.middleware.HttpResponseNotAllowedMiddleware', |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| ROOT_URLCONF = 'logs_collector.urls' | ROOT_URLCONF = 'logs_collector.urls' | ||||||
|  | |||||||
| @ -1,10 +0,0 @@ | |||||||
| {% extends 'errors.html' %} |  | ||||||
| {% load static %} |  | ||||||
| 
 |  | ||||||
| {% block title %} Logs Collector - CSRF error {% endblock title %} |  | ||||||
| 
 |  | ||||||
| {% block status_code %}403{% endblock status_code %} |  | ||||||
| {% block error_message %} |  | ||||||
| <p class="fs-3"> <span class="text-danger">Opps!</span> CSRF verification failed.</p> |  | ||||||
| <p class="lead">Request aborted</p> |  | ||||||
| {% endblock error_message %} |  | ||||||
| @ -1,10 +0,0 @@ | |||||||
| {% extends 'errors.html' %} |  | ||||||
| {% load static %} |  | ||||||
| 
 |  | ||||||
| {% block title %} Logs Collector - Not Found {% endblock title %} |  | ||||||
| 
 |  | ||||||
| {% block status_code %}404{% endblock status_code %} |  | ||||||
| {% block error_message %} |  | ||||||
| <p class="fs-3"> <span class="text-danger">Opps!</span> Page not found.</p> |  | ||||||
| <p class="lead">The content you're looking for doesn't exist.</p> |  | ||||||
| {% endblock error_message %} |  | ||||||
| @ -1,14 +0,0 @@ | |||||||
| {% extends 'errors.html' %} |  | ||||||
| {% load static %} |  | ||||||
| 
 |  | ||||||
| {% block title %} Logs Collector - Method not allowed {% endblock title %} |  | ||||||
| 
 |  | ||||||
| {% block status_code %}405{% endblock status_code %} |  | ||||||
| {% block error_message %} |  | ||||||
| <p class="fs-3"> <span class="text-danger">Opps!</span> Method not allowed</p> |  | ||||||
| <p class="lead"> |  | ||||||
|   Request method:  |  | ||||||
|   <span class="text-danger">{{ request.method }}</span>  |  | ||||||
|   isn't allowed for this URL |  | ||||||
| </p> |  | ||||||
| {% endblock error_message %} |  | ||||||
| @ -1,10 +0,0 @@ | |||||||
| {% extends 'errors.html' %} |  | ||||||
| {% load static %} |  | ||||||
| 
 |  | ||||||
| {% block title %} Logs Collector - Server error {% endblock title %} |  | ||||||
| 
 |  | ||||||
| {% block status_code %}500{% endblock status_code %} |  | ||||||
| {% block error_message %} |  | ||||||
| <p class="fs-3"> <span class="text-danger">Opps!</span> Server error</p> |  | ||||||
| <p class="lead">Unexpected error, please try again or contact system admin </p> |  | ||||||
| {% endblock error_message %} |  | ||||||
| @ -34,12 +34,10 @@ | |||||||
|     rel="stylesheet" |     rel="stylesheet" | ||||||
|     href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" |     href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" | ||||||
|     > |     > | ||||||
|     {% block errors_head %}{% endblock errors_head %} |  | ||||||
|     {% block collector_head %}{% endblock collector_head %} |     {% block collector_head %}{% endblock collector_head %} | ||||||
|     {% block account_head %}{% endblock account_head %} |     {% block account_head %}{% endblock account_head %} | ||||||
|   </head> |   </head> | ||||||
|   <body class="d-flex flex-column min-vh-100"> |   <body class="d-flex flex-column min-vh-100"> | ||||||
|     {% block http_errors %}{% endblock http_errors %} |  | ||||||
|     {% block collector_content %}{% endblock collector_content %} |     {% block collector_content %}{% endblock collector_content %} | ||||||
|     {% block account_content %}{% endblock account_content %} |     {% block account_content %}{% endblock account_content %} | ||||||
|     <!-- BS dependences JS--> |     <!-- BS dependences JS--> | ||||||
|  | |||||||
| @ -1,20 +0,0 @@ | |||||||
| {% extends 'base.html' %} |  | ||||||
| {% load static %} |  | ||||||
| 
 |  | ||||||
| {% block errors_head %} |  | ||||||
|   <title>{% block title %}{% endblock title %}</title> |  | ||||||
| {% endblock errors_head %} |  | ||||||
| 
 |  | ||||||
| {% block http_errors %} |  | ||||||
| <div class="d-flex align-items-center justify-content-center vh-100" > |  | ||||||
|   <div class="text-center"> |  | ||||||
|       <h1 class="display-1 fw-bold">{% block status_code %}{% endblock status_code %}</h1> |  | ||||||
|       {% block error_message %}{% endblock error_message %} |  | ||||||
|       <a href="{% url 'collector:index' %}" class="btn btn-secondary">Go Home</a> |  | ||||||
|   </div> |  | ||||||
|   <!-- Theme switcher --> |  | ||||||
|   <div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle"> |  | ||||||
|     {% include 'includes/theme_switcher.html' %} |  | ||||||
|   </div> |  | ||||||
| </div> |  | ||||||
| {% endblock http_errors %} |  | ||||||
| @ -1,29 +1,21 @@ | |||||||
| <nav class="navbar navbar-expand-lg bg-body-tertiary"> | <nav class="navbar navbar-expand-lg bg-body-tertiary"> | ||||||
|   <div class="container"> |   <div class="container d-flex justify-content-between"> | ||||||
|     <ul class="navbar-nav"> |     <ul class="navbar-nav"> | ||||||
|       <li class="nav-item"> |       <li class="nav-item"> | ||||||
|         <button class="btn"> |         <span class="text-muted"> | ||||||
|           v{{ version }} |           v{{ version }} | ||||||
|           {% if environment != 'Production' %} |           {% if environment != 'Production' %} | ||||||
|             Staging: {{ environment }} |             Staging: {{ environment }} | ||||||
|           {% endif %} |           {% endif %} | ||||||
|         </button> |           | © {{ author }}  | ||||||
|       </li> |         </span> | ||||||
|       <!-- Separator --> |  | ||||||
|       <li class="nav-item py-2 py-lg-1 col-12 col-lg-auto"> |  | ||||||
|         <div class="vr d-none d-lg-flex h-100 mx-lg-2 text-white"></div> |  | ||||||
|         <hr class="d-lg-none my-2 text-white-50"> |  | ||||||
|       </li> |  | ||||||
|       <li class="nav-item"> |  | ||||||
|         <button class='btn'> © {{ author }} </button> |  | ||||||
|       </li> |       </li> | ||||||
|     </ul> |     </ul> | ||||||
|     {% if request.user.is_authenticated %} |  | ||||||
|     <ul class="navbar-nav"> |     <ul class="navbar-nav"> | ||||||
|       <li class="nav-item"> |       <li class="nav-item"> | ||||||
|         <li> |         <li> | ||||||
|           <a |           <a | ||||||
|             class="btn" |             class="nav-link" | ||||||
|             type="button" |             type="button" | ||||||
|             href="{% url 'swagger-ui' %}" |             href="{% url 'swagger-ui' %}" | ||||||
|             target="_blank" |             target="_blank" | ||||||
| @ -36,6 +28,5 @@ | |||||||
|         </a> |         </a> | ||||||
|       </li> |       </li> | ||||||
|     </ul> |     </ul> | ||||||
|     {% endif %} |  | ||||||
|   </div> |   </div> | ||||||
| </nav> | </nav> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user