Files: downloadable from admin

for convenience
This commit is contained in:
Klaas van Schelven
2025-04-11 09:15:09 +02:00
parent 0bd899fdfa
commit b160a6df06
4 changed files with 50 additions and 3 deletions

View File

@@ -55,6 +55,7 @@ urlpatterns = [
path('teams/', include('teams.urls')),
path('events/', include('events.urls')),
path('issues/', include('issues.urls')),
path('files/', include('files.urls')),
path('admin/', admin.site.urls),

View File

@@ -1,4 +1,7 @@
from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from .models import Chunk, File, FileMetadata
@@ -11,9 +14,15 @@ class ChunkAdmin(admin.ModelAdmin):
@admin.register(File)
class FileAdmin(admin.ModelAdmin):
list_display = ('filename', 'checksum', 'size')
list_display = ('filename', 'checksum', 'size', 'download_link')
search_fields = ('checksum',)
readonly_fields = ('data',)
readonly_fields = ('data', 'download_link')
def download_link(self, obj):
return format_html(
'<a href="{}">{}</a>',
reverse("download_file", args=(obj.checksum,)), str(obj.filename),
)
@admin.register(FileMetadata)

27
files/urls.py Normal file
View File

@@ -0,0 +1,27 @@
from django.urls import path
from django.urls import register_converter
from .views import download_file
def regex_converter(passed_regex):
# copy/pasta w/ issues/urls.py
class RegexConverter:
regex = passed_regex
def to_python(self, value):
return value
def to_url(self, value):
return value
return RegexConverter
register_converter(regex_converter("[0-9a-f]{40}"), "sha1")
urlpatterns = [
path('downloads/<sha1:checksum>/', download_file, name='download_file'),
]

View File

@@ -3,9 +3,11 @@ import json
from hashlib import sha1
from gzip import GzipFile
from io import BytesIO
from os.path import basename
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import user_passes_test
from sentry.assemble import ChunkFileState
@@ -154,7 +156,7 @@ def assemble_artifact_bundle(bundle_checksum, chunk_checksums):
checksum = sha1(file_data).hexdigest()
filename = manifest_entry.get("url", filename)[:255]
filename = basename(manifest_entry.get("url", filename))[:255]
file, _ = File.objects.get_or_create(
checksum=checksum,
@@ -226,3 +228,11 @@ def artifact_bundle_assemble(request, organization_slug):
# NOTE: as it stands, we process the bundle inline, so arguably we could return "OK" here too; "CREATED" is what
# sentry returns though, so for faithful mimicking it's the safest bet.
return JsonResponse({"state": ChunkFileState.CREATED, "missingChunks": []})
@user_passes_test(lambda u: u.is_superuser)
def download_file(request, checksum):
file = File.objects.get(checksum=checksum)
response = HttpResponse(file.data, content_type="application/octet-stream")
response["Content-Disposition"] = f"attachment; filename={file.filename}"
return response