admin docs: verify object existence, mark dead links, cleaner 404

The DB can record a pdf_minio_path before the object is uploaded (e.g. a
prepared-filing path written for an order whose prep never completed -- Paul
Wilson / Mark Adams MCS-150s). The documents list now HEAD-checks each key and
returns an exists flag; the UI shows 'not generated yet' instead of a dead View
button, and the stream endpoint returns a clean 404 for a missing object.
This commit is contained in:
justin 2026-06-16 00:22:35 -05:00
parent bce5db4a09
commit 1f3b36b29e
2 changed files with 23 additions and 4 deletions

View file

@ -668,7 +668,21 @@ router.get("/api/v1/admin/compliance-orders/:order_number/documents", requireAdm
const exists = await pool.query("SELECT 1 FROM compliance_orders WHERE order_number = $1", [req.params.order_number]); const exists = await pool.query("SELECT 1 FROM compliance_orders WHERE order_number = $1", [req.params.order_number]);
if (exists.rows.length === 0) { res.status(404).json({ error: "Order not found." }); return; } if (exists.rows.length === 0) { res.status(404).json({ error: "Order not found." }); return; }
const docs = await collectOrderDocuments(req.params.order_number); const docs = await collectOrderDocuments(req.params.order_number);
res.json({ order_number: req.params.order_number, documents: docs }); // Verify each object actually exists in storage so the UI can disable dead
// links (a key can be recorded in the DB before the object is uploaded, e.g.
// a prepared-filing path written for an order whose prep never completed).
const checked = await Promise.all(docs.map(async (d) => {
let present = false;
try {
const url = await presignInternal(d.key);
if (url) {
const head = await fetch(url, { method: "HEAD" });
present = head.ok;
}
} catch { /* treat as missing */ }
return { ...d, exists: present };
}));
res.json({ order_number: req.params.order_number, documents: checked });
} catch (err) { } catch (err) {
console.error("[admin/compliance-orders/documents] Error:", err); console.error("[admin/compliance-orders/documents] Error:", err);
res.status(500).json({ error: "Could not list documents." }); res.status(500).json({ error: "Could not list documents." });
@ -696,6 +710,10 @@ router.get("/api/v1/admin/compliance-orders/:order_number/document", requireAdmi
if (!url) { res.status(502).json({ error: "Could not generate object URL." }); return; } if (!url) { res.status(502).json({ error: "Could not generate object URL." }); return; }
const upstream = await fetch(url); const upstream = await fetch(url);
if (upstream.status === 404) {
res.status(404).json({ error: "Document not found in storage (it may not have been generated yet)." });
return;
}
if (!upstream.ok || !upstream.body) { if (!upstream.ok || !upstream.body) {
res.status(502).json({ error: `Object fetch failed (${upstream.status}).` }); res.status(502).json({ error: `Object fetch failed (${upstream.status}).` });
return; return;

View file

@ -386,12 +386,13 @@
box.innerHTML = documents.map((d) => { box.innerHTML = documents.map((d) => {
const u = API + "/api/v1/admin/compliance-orders/" + encodeURIComponent(orderNumber) const u = API + "/api/v1/admin/compliance-orders/" + encodeURIComponent(orderNumber)
+ "/document?key=" + encodeURIComponent(d.key) + "&token=" + tok; + "/document?key=" + encodeURIComponent(d.key) + "&token=" + tok;
const action = (d.exists === false)
? `<span class="small" style="color:#9ca3af;">not generated yet</span>`
: `<a href="${u}" target="_blank" rel="noopener" class="btn btn-blue btn-sm" style="text-decoration:none;">View</a>`;
return `<div class="svc-row"><div class="svc-left"> return `<div class="svc-row"><div class="svc-left">
<span style="font-size:13px;">${esc(d.label)}</span> <span style="font-size:13px;">${esc(d.label)}</span>
<span class="small">${esc(d.key.split("/").pop())}</span> <span class="small">${esc(d.key.split("/").pop())}</span>
</div><div class="svc-right"> </div><div class="svc-right">${action}</div></div>`;
<a href="${u}" target="_blank" rel="noopener" class="btn btn-blue btn-sm" style="text-decoration:none;">View</a>
</div></div>`;
}).join(""); }).join("");
} catch (e) { } catch (e) {
box.innerHTML = `<div style="color:#b91c1c;font-size:12px;">Could not load documents: ${esc(e.message)}</div>`; box.innerHTML = `<div style="color:#b91c1c;font-size:12px;">Could not load documents: ${esc(e.message)}</div>`;