fix(po-pdf): capture PB token on upload and route GET through proxy
This commit is contained in:
parent
1e99d23e8b
commit
6a7dcc0af2
1 changed files with 61 additions and 29 deletions
|
|
@ -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`;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue