feat(inbox): direct-to-storage upload for files over the hosted body limit (#1551) (#2030)

Hosted uploads larger than the 4 MB multipart ceiling (Vercel's 4.5 MB request-body cap) now go POST /upload/create (signed PUT URL, rate-limited) -> PUT to the raw Storage URL -> POST /upload/complete (server-side magic-byte and size validation, sha256, WORM move, idempotent), reusing the #1378 pending-upload primitives. uploadAndExtract is split into uploadDocument + processArchivedDocument so both paths share the inbox pipeline. Dokumentinkorgen and the supplier-invoice form use the new path only above the threshold; files that fit keep the multipart route. Cap stays at 10 MB (the issue asks for 20 MB: founder call). Refs #1551
This commit is contained in:
Jakob Wennberg
2026-08-30 11:55:42 +02:00
committed by GitHub
parent 523fba0419
commit 749f90fe62
14 changed files with 1391 additions and 53 deletions
+21
View File
@@ -2286,6 +2286,20 @@ const PROVIDER_MIGRATION: Record<string, StructuredErrorEntry> = {
// ─────────────────────────────────────────────────────────────────
const DOCUMENT: Record<string, StructuredErrorEntry> = {
// Signed-URL (direct-to-storage) upload: completion found no object under
// the reservation. The bytes never landed, or the reservation expired.
DOCUMENT_UPLOAD_NOT_FOUND: {
httpStatus: 404,
message_sv: 'Den uppladdade filen hittades inte eller har gått ut. Ladda upp filen igen.',
message_en: 'The uploaded file was not found or the upload has expired. Upload the file again.',
},
// Signed-URL upload completed against an empty object: the PUT sent no
// bytes, or sent them somewhere else.
DOC_UPLOAD_EMPTY: {
httpStatus: 400,
message_sv: 'Filen är tom. Ladda upp filen igen.',
message_en: 'The uploaded file is empty. Upload the file again.',
},
DOC_UPLOAD_NO_FILE: {
httpStatus: 400,
message_sv: 'Ingen fil bifogad.',
@@ -2394,6 +2408,13 @@ const INBOX_UPLOAD: Record<string, StructuredErrorEntry> = {
message_sv: 'Uppladdningen misslyckades. Försök igen.',
message_en: 'Upload failed.',
},
// A read-only (viewer) member: the storage policy admits the bytes on
// membership alone, the document_attachments insert policy does not.
INBOX_UPLOAD_NOT_PERMITTED: {
httpStatus: 403,
message_sv: 'Du har inte behörighet att ladda upp underlag i det här företaget. Medlemmar med läsbehörighet kan inte lägga till dokument.',
message_en: 'You do not have permission to upload documents to this company. Read-only members cannot add documents.',
},
INBOX_ATTACH_FAILED: {
httpStatus: 500,
message_sv: 'Bilagan kunde inte kopplas. Försök igen.',