diff --git a/src/app/api/v1/purchase-orders/[id]/pdf/route.ts b/src/app/api/v1/purchase-orders/[id]/pdf/route.ts index 1e9e424..e6fe29d 100644 --- a/src/app/api/v1/purchase-orders/[id]/pdf/route.ts +++ b/src/app/api/v1/purchase-orders/[id]/pdf/route.ts @@ -212,29 +212,32 @@ export async function POST( // PB file fields accept an array of File/Blob. Append to the // existing array so every regen is preserved (operator can scroll // through documents_history to find v1, v2, v3...). - const newHistoryEntry: PurchaseOrderDocumentEntry = { - version: nextVersion, - file: filename, - // Task 2 will swap this for the real PB token returned from - // the upload response. For now we persist an empty string so - // the schema compiles; the GET handler falls back to - // `documents[idx]` when token is empty (legacy rows). - token: "", - generatedAt: generatedAtIso, - generatedBy: body.generatedBy || "", - reason: (body.notes || "").slice(0, 500), - status: "current", - }; - const formData = new FormData(); formData.append( "documents", new Blob([new Uint8Array(pdfBuffer)], { type: "application/pdf" }), filename ); - // PB SDK FormData update only accepts the file — append the history - // via a second update call (it's JSON, not a file). - await pb.collection("purchase_orders").update(poId, formData); + // PB returns the stored file tokens (slugged) in the response. + // We need the actual on-disk token for later fetches, not the + // human-readable `filename` we sent. Append to the existing array + // and pick the last element. + const updated = await pb.collection("purchase_orders").update<{ + documents: string[] | string; + }>(poId, formData); + const docsRaw = updated?.documents ?? []; + const docs = Array.isArray(docsRaw) ? docsRaw : [docsRaw]; + const newToken = docs[docs.length - 1] ?? ""; + + const newHistoryEntry: PurchaseOrderDocumentEntry = { + version: nextVersion, + file: filename, + token: newToken, + generatedAt: generatedAtIso, + generatedBy: body.generatedBy || "", + reason: (body.notes || "").slice(0, 500), + status: "current", + }; // Append the new history entry. PB handles the array merge via // JSON Patch — actually no, with plain PATCH we'd replace the @@ -316,7 +319,9 @@ export async function GET( const v = parseInt(versionParam, 10); entry = history.find((h) => h.version === v); } else { - entry = history.find((h) => h.status === "current") ?? history[history.length - 1]; + entry = + history.find((h) => h.status === "current") ?? + history[history.length - 1]; } if (!entry) { @@ -326,21 +331,48 @@ export async function GET( ); } - const fileUrl = pb.files.getURL( - { collectionId: "purchase_orders", id: poId }, - entry.file, - ); - const fileRes = await fetch(fileUrl, { - headers: { Authorization: pb.authStore.token ?? "" }, - }); - if (!fileRes.ok) { + // Resolve a token. Newer rows have `entry.token` (PB-stored slug). + // Legacy rows may have only `entry.file` (the human-readable name). + // For legacy rows, fall back to the live `documents` array — the + // POST handler always appends to both arrays in sync, so index + // alignment gives us the right token. + let token = entry.token; + if (!token) { + const docsRaw = po.documents ?? []; + const docs = Array.isArray(docsRaw) ? docsRaw : [docsRaw]; + const idx = history.findIndex((h) => h.version === entry!.version); + token = docs[idx] || docs[0] || ""; + } + + if (!token) { return NextResponse.json( - { error: `Upstream PB file fetch failed: ${fileRes.status}` }, - { status: 502 } + { + error: `PDF token missing for version ${entry.version} — please regenerate.`, + }, + { status: 404 } ); } - const buf = Buffer.from(await fileRes.arrayBuffer()); + // Hand off to the existing file proxy. The proxy already has + // `purchase_orders` whitelisted and streams the bytes back with + // admin auth. + const proxyUrl = `/api/v1/files/purchase_orders/${poId}/${encodeURIComponent(token)}`; + const proxyRes = await fetch( + new URL(proxyUrl, req.nextUrl.origin).toString(), + { + headers: { cookie: req.headers.get("cookie") ?? "" }, + } + ); + + if (!proxyRes.ok) { + const body = await proxyRes.json().catch(() => ({})); + return NextResponse.json( + { error: body?.error || `Proxy fetch failed: ${proxyRes.status}` }, + { status: proxyRes.status === 404 ? 404 : 502 } + ); + } + + const buf = Buffer.from(await proxyRes.arrayBuffer()); const safePo = String(po.po_number || poId).replace(/[^\w.-]/g, "_"); const filename = `PO-v${entry.version}-${safePo}.pdf`;