From 43b7030d3388a8785e59cb703dc3615b0d2928f2 Mon Sep 17 00:00:00 2001
From: Aroy-Art <Aroy-Art@pm.me>
Date: Thu, 28 Dec 2023 19:17:42 +0100
Subject: [PATCH 1/3] Add: basic upload & serve submission files url endpoint
 views

---
 archivist/apps/files/views.py | 83 +++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 archivist/apps/files/views.py

diff --git a/archivist/apps/files/views.py b/archivist/apps/files/views.py
new file mode 100644
index 0000000..5445b88
--- /dev/null
+++ b/archivist/apps/files/views.py
@@ -0,0 +1,83 @@
+# Create your views here.
+
+from django.contrib.auth.decorators import login_required
+from django.http import FileResponse, HttpResponse, HttpResponseRedirect
+from django.shortcuts import get_object_or_404, render
+
+import os
+import blake3
+
+from .forms import UploadFileForm
+from .models import Submission_File
+
+
+def compute_file_hash(file):
+    '''
+    Compute BLAKE3 hash of the file
+    '''
+    try:
+        hasher = blake3.blake3()
+        for chunk in file.chunks(chunk_size=65536):
+            hasher.update(chunk)
+        return hasher.hexdigest()
+    except Exception as e:
+        print(f"Error computing file hash: {e}")
+        return None
+
+
+@login_required(login_url="/login/")
+def serve_submission_file(request, file_hash):
+    '''
+    View function to serve submission files for download or inline viewing
+    '''
+    download = request.GET.get('d')
+
+    try:
+        submission_file = get_object_or_404(Submission_File, file_hash=file_hash)
+        file = submission_file.file.file
+        file_name = submission_file.file_name
+
+        response = FileResponse(file)
+        if download == "1":
+            response['Content-Disposition'] = f'attachment; filename="{file_name}"'
+        else:
+            response['Content-Disposition'] = f'inline; filename="{file_name}"'
+
+        return response
+
+    except:
+        return HttpResponse("File not found", status=404)
+
+
+@login_required(login_url="/login/")
+def fileUpload(request):
+    '''
+    View function for handling file uploads
+    '''
+    if request.method == 'POST':
+        form = UploadFileForm(request.POST, request.FILES)
+        if form.is_valid():
+            file = form.cleaned_data['file']
+            
+            file_name = file.name
+            file_hash = compute_file_hash(file)
+            
+            Null, file_ext = os.path.splitext(file_name)
+            hash_file_name = file_hash + file_ext
+            
+            new_submission_file, created = Submission_File.objects.get_or_create(file_hash=file_hash)
+            
+            new_submission_file.file_hash = file_hash
+            new_submission_file.file_name = file_name
+            new_submission_file.file.save(hash_file_name, file) 
+            
+            new_submission_file.save
+            
+            return HttpResponseRedirect("/")
+            
+    else:
+        form = UploadFileForm()
+        
+    return render(request, 'files/upload.html', {'form': form})
+
+    

From f55815bb29b3a6f5e17315de6f9f17855f69a310 Mon Sep 17 00:00:00 2001
From: Aroy-Art <Aroy-Art@pm.me>
Date: Thu, 28 Dec 2023 19:18:21 +0100
Subject: [PATCH 2/3] Add: url configs

---
 archivist/apps/files/urls.py | 9 +++++++++
 archivist/core/urls.py       | 1 +
 2 files changed, 10 insertions(+)
 create mode 100644 archivist/apps/files/urls.py

diff --git a/archivist/apps/files/urls.py b/archivist/apps/files/urls.py
new file mode 100644
index 0000000..29ea12e
--- /dev/null
+++ b/archivist/apps/files/urls.py
@@ -0,0 +1,9 @@
+from django.urls import re_path, path
+from . import views
+
+urlpatterns = [
+    # Add a URL pattern that captures the file path
+    path('submission/<str:file_hash>', views.serve_submission_file, name='serve_submission_file'),
+    # Other URL patterns if any
+    path('upload/', views.fileUpload, name='file_upload'),
+]
diff --git a/archivist/core/urls.py b/archivist/core/urls.py
index cc9a25f..c41d958 100644
--- a/archivist/core/urls.py
+++ b/archivist/core/urls.py
@@ -21,6 +21,7 @@ admin.site.site_header = 'Gallery Archivist Django Admin Panel'
 urlpatterns = [
     path('admin/', admin.site.urls),    # Django admin route
     path("", include("apps.authentication.urls")),    # Auth routes - login / register
+    path("files/", include("apps.files.urls")),
     
     path("", include("sites.furaffinity.urls")),
     

From dcb53001d25ba521c1397178a08e239ca674737b Mon Sep 17 00:00:00 2001
From: Aroy-Art <Aroy-Art@pm.me>
Date: Thu, 28 Dec 2023 19:21:14 +0100
Subject: [PATCH 3/3] Enable: the files app

---
 archivist/core/settings.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/archivist/core/settings.py b/archivist/core/settings.py
index 0f120c6..2e71221 100644
--- a/archivist/core/settings.py
+++ b/archivist/core/settings.py
@@ -61,6 +61,8 @@ INSTALLED_APPS = [
     "django_celery_beat",
     "django_celery_results",
     
+    'apps.files',
+    
     # Gallery Archivist apps    
     'sites.furaffinity'
 ]