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:
@@ -77,8 +77,13 @@ import {
|
||||
getResponseErrorMessage,
|
||||
} from '@/lib/errors/get-error-message'
|
||||
import { notifySessionExpired } from '@/lib/auth/session-timeout-shared'
|
||||
import { exceedsHostedUploadLimit, tooLargeMessage } from '@/lib/documents/upload-size'
|
||||
import {
|
||||
exceedsHostedUploadLimit,
|
||||
exceedsInboxUploadLimit,
|
||||
inboxTooLargeMessage,
|
||||
} from '@/lib/documents/upload-size'
|
||||
import { shrinkImageForUpload } from '@/lib/documents/shrink-image'
|
||||
import { uploadViaSignedUrl } from '@/lib/documents/direct-upload'
|
||||
|
||||
type AccountingMethod = 'accrual' | 'cash'
|
||||
|
||||
@@ -879,25 +884,28 @@ export default function InvoiceInboxWorkspace(_props: WorkspaceComponentProps) {
|
||||
) => {
|
||||
// A phone photo is routinely larger than the request body the platform
|
||||
// will carry, and it rejects the upload itself, before the route can say
|
||||
// anything useful about it. Shrink what can be shrunk, and refuse the rest
|
||||
// here, where we can name the size instead of letting the transfer fail.
|
||||
// anything useful about it. Shrink what can be shrunk; what cannot be (a
|
||||
// scanned PDF) goes straight to Storage through a signed URL instead of a
|
||||
// multipart body. Only the inbox's own ceiling refuses anything now, here,
|
||||
// where we can name the size instead of letting the transfer fail.
|
||||
const file = exceedsHostedUploadLimit(original.size)
|
||||
? await shrinkImageForUpload(original)
|
||||
: original
|
||||
if (exceedsHostedUploadLimit(file.size)) {
|
||||
if (exceedsInboxUploadLimit(file.size)) {
|
||||
reportUploadFailure({
|
||||
status: 0,
|
||||
size: file.size,
|
||||
type: file.type || 'unknown',
|
||||
reason: 'over hosted body limit, refused client-side',
|
||||
reason: 'over inbox ceiling, refused client-side',
|
||||
})
|
||||
toast({
|
||||
title: 'Uppladdning misslyckades',
|
||||
description: tooLargeMessage(file.size),
|
||||
description: inboxTooLargeMessage(file.size),
|
||||
variant: 'destructive',
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
const directToStorage = exceedsHostedUploadLimit(file.size)
|
||||
|
||||
// Optimistic placeholder: gives the user an immediate visual response
|
||||
// for the 3-8s while extraction runs. Removed once the real row arrives.
|
||||
@@ -929,12 +937,17 @@ export default function InvoiceInboxWorkspace(_props: WorkspaceComponentProps) {
|
||||
}
|
||||
setIsUploading(true)
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
const res = await fetch('/api/extensions/ext/invoice-inbox/upload', {
|
||||
method: 'POST',
|
||||
body: fd,
|
||||
})
|
||||
let res: Response
|
||||
if (directToStorage) {
|
||||
res = await uploadViaSignedUrl(file)
|
||||
} else {
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
res = await fetch('/api/extensions/ext/invoice-inbox/upload', {
|
||||
method: 'POST',
|
||||
body: fd,
|
||||
})
|
||||
}
|
||||
if (!res.ok) throw await resolveFailure(res)
|
||||
const json = await res.json()
|
||||
if (json.data?.extraction_skipped) {
|
||||
|
||||
Reference in New Issue
Block a user