Enable stl download

This commit is contained in:
2026-05-17 12:38:58 +03:00
parent 8ffac1b8a7
commit 553ac9abf1
4 changed files with 68 additions and 7 deletions

View File

@@ -18,9 +18,11 @@ import secrets
from django.contrib import messages
from django.db import IntegrityError, transaction
from django.http import FileResponse, Http404
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from django.utils.html import format_html
from django.views.decorators.http import require_safe
from django.views.generic import CreateView
from .emails import send_confirmation_email
@@ -28,6 +30,48 @@ from .forms import SubmissionForm
from .models import Submission, VerifiedEmail
@require_safe
def serve_stl(request, path: str):
"""Auth-checked passthrough to a Submission's uploaded `.stl`. URL is
`/media/<path>` so it matches `MEDIA_URL` -- which means
`{{ submission.stl_file.url }}` in templates Just Works.
`path` is the relative name Django stored (e.g. `stl/foo.stl`); we
look the row up by that string rather than reading the filesystem
directly so an attacker can't escape `MEDIA_ROOT` with `..` segments.
Access tiers match `SubmissionDetailView` (plan.md §7.4 step 7):
- staff: yes (operators need the file to drive the printer).
- authenticated owner (`submitted_by == request.user`): yes.
- everyone else: 404 -- we deliberately don't 403 so the existence
of a row isn't leaked to non-owners by status code.
Guests can't authenticate by design, so they can't pull their own STL
via this path. That's consistent with the detail page's two-tier
access; the submitter has the file on their own machine in any case.
"""
sub = Submission.objects.filter(stl_file=path).first()
if sub is None:
raise Http404
user = request.user
is_owner = (
user.is_authenticated
and sub.submitted_by_id is not None
and sub.submitted_by_id == user.id
)
if not (user.is_staff or is_owner):
raise Http404
# `FieldFile.open("rb")` is rooted in MEDIA_ROOT; FileResponse handles
# streaming, range requests, and closing the handle for us.
filename = sub.stl_file.name.rsplit("/", 1)[-1]
return FileResponse(
sub.stl_file.open("rb"),
as_attachment=True,
filename=filename,
)
class SubmitView(CreateView):
"""Public submit form. GET renders, POST creates a Submission."""