feat(api): replace presigned uploads with direct upload for filesystem backend #129
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adapt the file upload/download flow so it works without MinIO presigned URLs when using the filesystem backend.
Context
Current upload flow (MinIO presigned)
POST /api/uploads/presignwith{filename, content_type, size}HandlePresignUploadinfile_handlers.go) generates a presigned PUT URL vias.storage.PresignPut()and returns{object_key, upload_url, expires_at}useFileUpload.ts) PUTs file bytes directly to the MinIO presigned URL usingXMLHttpRequest(with progress tracking)POST /api/items/{partNumber}/fileswith{object_key, filename, content_type, size}HandleAssociateItemFile) copies from temp key to permanent key vias.storage.Copy(), deletes temp, creates DB recordCurrent revision upload flow (already direct)
POST /api/items/{partNumber}/file(HandleUploadFileinhandlers.go) already accepts multipart form upload directly — no presigning needed. This path works as-is with the filesystem backend.Problem
The filesystem backend cannot generate presigned URLs (no HTTP server on the storage layer). The presign → direct-PUT → associate flow breaks.
Requirements
Option A: Direct multipart upload (recommended)
Modify the item file attachment flow to support direct multipart upload through the Go server, similar to how
HandleUploadFilealready works for revisions:HandleAssociateItemFileto acceptmultipart/form-dataFileStore.Put()The presigned flow can remain available when
storage.backend == "minio"for backward compatibility.Frontend changes (
web/src/hooks/useFileUpload.ts)POSTwithmultipart/form-dataXMLHttpRequestorfetchwithReadableStreamfor upload progressDownload path
HandleDownloadFileinhandlers.goalready streams froms.storage.Get()/s.storage.GetVersion()— this works as-is with the filesystem backend.For item file attachments, add a download handler if one doesn't exist (currently only list/create/delete for item files — no download endpoint).
Routes to add/modify
Current storage-related routes in
internal/api/routes.go:Changes needed:
POST /api/items/{partNumber}/files— accept both JSON (existing presigned flow) andmultipart/form-data(new direct upload)GET /api/items/{partNumber}/files/{fileId}/download— new endpoint for downloading attached filesPOST /api/uploads/presign— return 501 or skip when filesystem backend is activeThumbnail upload
HandleSetItemThumbnailcurrently accepts{object_key}referencing a pre-uploaded temp file. Same pattern as item files — needs a direct upload path or acceptmultipart/form-data.Acceptance criteria
useFileUpload.tssupports direct upload pathHandleUploadFile) continues working as-isHandleDownloadFile) continues working as-isPriority
P1
Depends on
Part of
Storage Migration: MinIO → PostgreSQL + Filesystem