/** * One-shot backfill — write `token` into every PurchaseOrderDocumentEntry * in `purchase_orders.documents_history` that doesn't have one yet. * * Why this exists * ──────────────── * The PO PDF route used to store the human-readable filename in * `documents_history[i].token`, which then 404'd when PB's file proxy * was asked to resolve it. The fix in the route handler stores the * real on-disk token (the slug PB returns from the upload) instead — * but legacy rows created before that change still have `file` and no * `token`, so the new GET handler would 404 on them. * * Strategy: * - For each PO row, read `documents_history` and `documents`. * - For each entry missing `token`, look up `documents[idx]` (or * `documents[0]` as a fallback). The two arrays are kept in sync * by the POST handler, so index alignment is correct. * - Patch the row with the new history array. * * Idempotent — re-running this script is a no-op once every entry has * a token. * * Run with (from WFDASH_NEXTJS so .env.local is found): * npx tsx scripts/migrations/2026-07-09-po-pdf-tokens.ts */ import { getAdminPocketBase } from "@/lib/server/pocketbase-admin"; type Entry = { version: number; file: string; token?: string; generatedAt: string; generatedBy: string; reason: string; status: "current" | "superseded"; }; type PoRow = { id: string; documents?: string[] | string; documents_history?: Entry[]; }; async function main() { const pb = await getAdminPocketBase(); let patchedRows = 0; let patchedEntries = 0; let unresolvable = 0; const list = await pb.collection("purchase_orders").getList(1, 500, { fields: "id,documents,documents_history", filter: "documents_history != null", }); for (const po of list.items) { const history = po.documents_history ?? []; if (history.length === 0) continue; const docsRaw = po.documents ?? []; const docs = Array.isArray(docsRaw) ? docsRaw : docsRaw ? [docsRaw] : []; let changed = false; const newHistory = history.map((entry, idx) => { if (entry.token) return entry; const token = docs[idx] || docs[0] || ""; if (!token) { unresolvable += 1; return entry; } changed = true; return { ...entry, token }; }); if (changed) { await pb.collection("purchase_orders").update(po.id, { documents_history: newHistory, }); patchedRows += 1; patchedEntries += newHistory.filter((e) => e.token).length - history.filter((e) => e.token).length; } } console.log( `[po-pdf-tokens] rows patched=${patchedRows} entries patched=${patchedEntries} unresolvable=${unresolvable}`, ); } main().catch((err) => { console.error("[po-pdf-tokens] failed:", err); process.exit(1); });