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('teams/', include('teams.urls')),
path('events/', include('events.urls')), path('events/', include('events.urls')),
path('issues/', include('issues.urls')), path('issues/', include('issues.urls')),
path('files/', include('files.urls')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),

View File

@@ -1,4 +1,7 @@
from django.contrib import admin from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from .models import Chunk, File, FileMetadata from .models import Chunk, File, FileMetadata
@@ -11,9 +14,15 @@ class ChunkAdmin(admin.ModelAdmin):
@admin.register(File) @admin.register(File)
class FileAdmin(admin.ModelAdmin): class FileAdmin(admin.ModelAdmin):
list_display = ('filename', 'checksum', 'size') list_display = ('filename', 'checksum', 'size', 'download_link')
search_fields = ('checksum',) 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) @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 hashlib import sha1
from gzip import GzipFile from gzip import GzipFile
from io import BytesIO from io import BytesIO
from os.path import basename
from django.http import JsonResponse, HttpResponse from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import user_passes_test
from sentry.assemble import ChunkFileState from sentry.assemble import ChunkFileState
@@ -154,7 +156,7 @@ def assemble_artifact_bundle(bundle_checksum, chunk_checksums):
checksum = sha1(file_data).hexdigest() 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( file, _ = File.objects.get_or_create(
checksum=checksum, 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 # 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. # sentry returns though, so for faithful mimicking it's the safest bet.
return JsonResponse({"state": ChunkFileState.CREATED, "missingChunks": []}) 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