Skip to content

feat(assignments): Cloudinary uploads on submission (closes #1059)#1117

Open
jeromehardaway wants to merge 1 commit into
masterfrom
feat/assignment-file-uploads
Open

feat(assignments): Cloudinary uploads on submission (closes #1059)#1117
jeromehardaway wants to merge 1 commit into
masterfrom
feat/assignment-file-uploads

Conversation

@jeromehardaway

Copy link
Copy Markdown
Contributor

Summary

Previously the assignment submission page captured file metadata client-side and saved only `{name, size}` to `Submission.files` — actual files were never persisted (`// TODO: Upload files to Cloudinary if provided`).

  • New `/api/upload/file` endpoint: server-mediated Cloudinary upload covering non-image artifacts (PDF, ZIP, DOC/DOCX, TXT/MD, plus images). Auth-required; the admin secret never leaves the server. `resource_type: "auto"` so non-image types are handled, 15 MB body limit, an `allowed_formats` allow-list, and an overridable folder.
  • Submission page now validates extension + per-file size (10 MB) before reading the file, base64-encodes payloads, POSTs to the new endpoint, and stores the returned secure URLs in `Submission.files` JSON alongside the metadata.
  • `` got a proper `accept` filter so the OS dialog matches the actual allow-list, and the hint copy was rewritten to list the accepted types and size cap.

Closes #1059.

Test plan

  • `npm test` — 380/380 pass
  • `npx biome check` on changed files — clean
  • Submit an assignment with a PDF — file appears in Cloudinary under `assignments/{courseId}/{assignmentId}`, `Submission.files` JSON contains the URL
  • Submit with a `.exe` — UI rejects with a specific message before any upload kicks off
  • Submit with an 11 MB file — UI rejects with a specific message
  • Submit with no attachments — flow is unchanged, no upload attempt
  • Cloudinary upload fails — error surfaces on the page; no orphan `Submission` row created

Closes #1059.

Previously the submit page captured file metadata client-side and
saved only {name, size} strings — files were never actually persisted.
Now files are uploaded to Cloudinary before the submission record is
created, and the URLs are stored in Submission.files as JSON.

- New /api/upload/file endpoint: server-mediated Cloudinary upload
  for non-image artifacts (PDF, ZIP, DOC/DOCX, TXT/MD, images).
  Auth-required; admin secret never leaves the server. 15 MB body
  limit, resource_type "auto" so non-image types are handled.
- Submission page validates size (10 MB per file) and extension
  client-side before reading the file, then POSTs base64 payloads
  to /api/upload/file and threads the returned URLs through to the
  existing submissions endpoint.
- File input now uses a proper `accept` filter and shows the
  user-facing allow-list instead of "Common formats: ZIP, PDF,
  images".
@vercel

vercel Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vets-who-code-app Ready Ready Preview, Comment May 13, 2026 1:16pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Finish Cloudinary file upload in assignment submission flow (TODO)

1 participant