Feat/tusd service resumable uploads#670
Open
whlongg wants to merge 1 commit into
Open
Conversation
80f7c54 to
eb8f35c
Compare
magnified103
reviewed
Jul 8, 2026
| expiry_seconds = getattr(settings, 'TUSD_JWT_EXPIRY_SECONDS', 7200) | ||
| payload = { | ||
| 'sub': str(user_id), | ||
| 'problem_code': problem_code, |
Member
There was a problem hiding this comment.
Can you verify whether these attributes are actually used in other places?
Also this module is intended to be 'context-agnostic'. Therefore, I think problem_code should be renamed to something else.
Contributor
Author
There was a problem hiding this comment.
The sub field should be kept for standard JWT compliance (RFC 7519), future-proof authorization checks at the hook level, and robust audit trailing/logging.
eb8f35c to
07151e2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Implement resumable uploads via the tus protocol (
tusd) for large test data files, mitigating reverse-proxy request body limitations.Type of change: New feature
What
resumable_upload, responsible for generating JWT upload intents (/upload/intent/) and handling incomingtusdwebhooks (/upload/hooks/).tus-js-clientintotemplates/problem/data.html. This enables chunked, resumable uploads for ZIP files exceeding the defined threshold (default: 100MB).ProblemDataView.post()injudge/views/problem_data.py. The view now correctly handles files that have already been relocated toDMOJ_PROBLEM_DATA_ROOTby thepre-finishblocking hook fromtusd.dmoj/settings.pyfor TUSD configurations (endpoints, JWT secrets, hook secrets, and thresholds). AddsPyJWT>=2.0torequirements.txtfor secure token signing and verification.TUSD_LOCAL_DATA_DIR) in thepre-finishwebhook to resolve file system path mismatches between thetusdcontainer and the Django host environment.ProblemDataView.post()to ensure the database stays perfectly synced after the file is stealthily moved into the system by the webhook.Why
Currently, uploading test data (ZIP files) for problems on VNOJ is processed directly via standard Django forms. However, reverse proxies like Cloudflare impose strict request body limits (e.g., 200MB) which cause direct uploads of large datasets (sometimes spanning several gigabytes) to fail.
By utilizing tusd as a dedicated upload server and the tus protocol for chunked, resumable uploads, we bypass these proxy constraints, resulting in a stable and reliable upload pipeline. Crucially, the implementation utilizes blocking
pre-finishhooks, ensuring file processing and relocation are fully synchronized with Django, thereby completely eliminating race conditions before the client receives a success response.sequenceDiagram participant Browser participant Django participant tusd Note over Browser,Django: Phase 1: Request Upload Intent Browser->>Django: POST /upload/intent/ (problem_code, file_type) Django->>Django: Check permissions (session/cookie)<br>Check if problem exists & editable<br>Check quota Django-->>Browser: JWT token (contains intent metadata) Note over Browser,tusd: Phase 2: Chunked Upload Browser->>tusd: POST /files/ (tus protocol, JWT in metadata) tusd->>Django: pre-create hook → POST /upload/hooks/ Django->>Django: Decode JWT, verify expiration Django-->>tusd: 200 OK (allow upload creation) loop Each chunk Browser->>tusd: PATCH /files/{id} (chunk data) end Note over tusd,Django: Phase 3: Pre-finish (BLOCKING) tusd->>Django: pre-finish hook → POST /upload/hooks/ Django->>Django: Decode JWT from metadata<br>Move file from tusd → DMOJ_PROBLEM_DATA_ROOT/{problem_code}/ Django-->>tusd: 200 OK Note over tusd: Now it returns 204 to Browser tusd-->>Browser: HTTP 204 (upload complete) Note over Browser: onSuccess firesFixes #484
Deployment & Testing Notes
Local Testing with Docker
To test this setup locally, spin up a
tusdcontainer with the appropriate hooks pointing to your local Django server:docker run -p 8080:8080 -v /home/whlong/Documents/VNOI-Admin/tusd-data:/srv/tusd-data \ --add-host=host.docker.internal:host-gateway \ tusproject/tusd \ -hooks-http http://host.docker.internal:8000/upload/hooks/ \ -hooks-enabled-events pre-create,pre-finish \ -cors-allow-origin ".*"Production Setup
tusdserver, ensuring CORS and max body size settings are correctly handled for the/files/endpoint.dmoj/local_settings.pyfor production:TUSD_ENDPOINT: The public URL of the tusd server (e.g.,https://judge.example.com/files/).TUSD_DATA_DIR: Path to the directory where tusd stores chunks (needs to be accessible by Django).TUSD_HOOK_SECRET: A shared secret string used to authenticate webhooks sent from tusd to Django.TUSD_JWT_SECRET: The secret key used to sign the upload intent JWTs (can fallback to Django'sSECRET_KEY).TUSD_JWT_EXPIRY_SECONDS: TTL for the intent token (default: 7200s).TUSD_UPLOAD_THRESHOLD_BYTES: File size threshold to trigger tus uploads (default: 100MB).TUSD_ALLOWED_FILE_TYPES: Tuple of permitted file types for the upload hooks.How Has This Been Tested?
resumable_upload/tests/test_views.py) for theresumable_uploadapp, testing JWT token logic, views, intent generation, and webhook validation.tusdcontainer via Docker with hooks configured (-hooks-enabled-events pre-create,pre-finish) pointing to the local Django instance.DMOJ_PROBLEM_DATA_ROOT/{problem_code}/upon completion.onError.Checklist
Put an
xin the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.By submitting this pull request, I confirm that my contribution is made under the terms of the AGPL-3.0 License.