new-site/api/src/routes/admin.ts
justin bce5db4a09 admin: view order PDFs from MinIO (signed forms, prepared filings, evidence)
Adds a Documents section to the compliance-order detail drawer so you can
review the actual filing PDFs before approving an order:
  GET /api/v1/admin/compliance-orders/:id/documents  list viewable objects
  GET /api/v1/admin/compliance-orders/:id/document?key=&token=  stream one

Key discovery pulls from esign_records (unsigned + signed docs per order),
intake_data.filing_status (pdf_minio_path, attested_pdf, evidence/*), and the
order's engagement_letter / rmd_packet columns.

Rather than hand out presigned URLs (MinIO's public host is IP-allowlisted to a
few office IPs, so links break elsewhere), the API streams the object through
itself from internal minio:9000, gated by the admin JWT. The stream endpoint
accepts the token via ?token= (new middleware requireAdminQueryOrHeader) so a
PDF opens in a new tab, and refuses any key that isn't one of the order's own
documents.
2026-06-16 00:20:15 -05:00

724 lines
30 KiB
TypeScript

import { Router } from "express";
import bcrypt from "bcryptjs";
import { pool } from "../db.js";
import { requireAdmin, requireAdminQueryOrHeader, signAdminToken } from "../middleware/admin-auth.js";
import { submitLimiter } from "../middleware/rate-limit.js";
const router = Router();
// =====================================================================
// Auth
// =====================================================================
/** POST /api/v1/admin/login — Authenticate and receive JWT. */
router.post("/api/v1/admin/login", submitLimiter, async (req, res) => {
try {
const { username, password } = req.body ?? {};
if (!username || !password) {
res.status(400).json({ error: "Username and password required." });
return;
}
const result = await pool.query(
"SELECT id, username, password_hash, display_name, active FROM admin_users WHERE username = $1",
[username.toLowerCase().trim()],
);
if (result.rows.length === 0) {
res.status(401).json({ error: "Invalid credentials." });
return;
}
const user = result.rows[0];
if (!user.active) {
res.status(403).json({ error: "Account is disabled." });
return;
}
const valid = await bcrypt.compare(password, user.password_hash);
if (!valid) {
res.status(401).json({ error: "Invalid credentials." });
return;
}
// Update last login
await pool.query("UPDATE admin_users SET last_login_at = now() WHERE id = $1", [user.id]);
const token = signAdminToken({ id: user.id, username: user.username });
res.json({
token,
user: { id: user.id, username: user.username, display_name: user.display_name },
});
} catch (err) {
console.error("[admin/login] Error:", err);
res.status(500).json({ error: "Login failed." });
}
});
/** GET /api/v1/admin/me — Verify token and return current user. */
router.get("/api/v1/admin/me", requireAdmin, async (req, res) => {
res.json({ user: req.admin });
});
// =====================================================================
// Order Queue — Formation Orders
// =====================================================================
/** GET /api/v1/admin/formations — List all formation orders with filtering. */
router.get("/api/v1/admin/formations", requireAdmin, async (req, res) => {
try {
const status = req.query.status as string || "";
const automation = req.query.automation as string || "";
const priority = req.query.priority as string || "";
const assigned = req.query.assigned as string || "";
const limit = Math.min(parseInt(req.query.limit as string, 10) || 50, 200);
const offset = parseInt(req.query.offset as string, 10) || 0;
let where = "WHERE 1=1";
const params: any[] = [];
let paramIdx = 1;
if (status) { where += ` AND f.status = $${paramIdx++}`; params.push(status); }
if (automation) { where += ` AND f.automation_status = $${paramIdx++}`; params.push(automation); }
if (priority) { where += ` AND f.priority = $${paramIdx++}`; params.push(priority); }
if (assigned === "unassigned") { where += " AND f.assigned_to IS NULL"; }
else if (assigned === "me") { where += ` AND f.assigned_to = $${paramIdx++}`; params.push(req.admin!.id); }
else if (assigned) { where += ` AND f.assigned_to = $${paramIdx++}`; params.push(parseInt(assigned, 10)); }
const countResult = await pool.query(
`SELECT COUNT(*) as total FROM formation_orders f ${where}`, params,
);
params.push(limit, offset);
const result = await pool.query(
`SELECT f.*, a.username as assigned_username, a.display_name as assigned_name
FROM formation_orders f
LEFT JOIN admin_users a ON f.assigned_to = a.id
${where}
ORDER BY
CASE f.priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'normal' THEN 2 WHEN 'low' THEN 3 END,
f.created_at DESC
LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
params,
);
res.json({
orders: result.rows,
total: parseInt(countResult.rows[0].total, 10),
limit,
offset,
});
} catch (err) {
console.error("[admin/formations] Error:", err);
res.status(500).json({ error: "Could not load orders." });
}
});
/** GET /api/v1/admin/formations/:id — Single order with full details + audit log. */
router.get("/api/v1/admin/formations/:id", requireAdmin, async (req, res) => {
try {
const id = parseInt(req.params.id, 10);
const order = await pool.query(
`SELECT f.*, a.username as assigned_username, a.display_name as assigned_name
FROM formation_orders f
LEFT JOIN admin_users a ON f.assigned_to = a.id
WHERE f.id = $1`, [id],
);
if (order.rows.length === 0) { res.status(404).json({ error: "Order not found." }); return; }
const audit = await pool.query(
`SELECT * FROM order_audit_log WHERE order_type = 'formation' AND order_id = $1 ORDER BY created_at DESC`,
[id],
);
const discount = await pool.query(
`SELECT * FROM discount_usage WHERE order_type = 'formation' AND order_id = $1`,
[id],
);
res.json({
order: order.rows[0],
audit_log: audit.rows,
discount: discount.rows[0] || null,
});
} catch (err) {
console.error("[admin/formations/:id] Error:", err);
res.status(500).json({ error: "Could not load order." });
}
});
/** PATCH /api/v1/admin/formations/:id — Update order status, priority, assignment, notes. */
router.patch("/api/v1/admin/formations/:id", requireAdmin, async (req, res) => {
try {
const id = parseInt(req.params.id, 10);
const { status, automation_status, priority, assigned_to, admin_notes, note } = req.body ?? {};
// Fetch current state
const current = await pool.query("SELECT * FROM formation_orders WHERE id = $1", [id]);
if (current.rows.length === 0) { res.status(404).json({ error: "Order not found." }); return; }
const order = current.rows[0];
const updates: string[] = [];
const params: any[] = [];
let idx = 1;
if (status && status !== order.status) {
updates.push(`status = $${idx++}`); params.push(status);
// Log status change
await pool.query(
`INSERT INTO order_audit_log (order_type, order_id, order_number, action, from_status, to_status, actor_type, actor_id, actor_name, note)
VALUES ('formation', $1, $2, 'status_change', $3, $4, 'admin', $5, $6, $7)`,
[id, order.order_number, order.status, status, req.admin!.id, req.admin!.username, note || null],
);
if (status === "delivered") {
updates.push(`delivered_at = now()`);
}
if (status === "filed") {
updates.push(`filed_at = now()`);
}
}
if (automation_status && automation_status !== order.automation_status) {
updates.push(`automation_status = $${idx++}`); params.push(automation_status);
await pool.query(
`INSERT INTO order_audit_log (order_type, order_id, order_number, action, from_status, to_status, actor_type, actor_id, actor_name, note)
VALUES ('formation', $1, $2, 'automation_update', $3, $4, 'admin', $5, $6, $7)`,
[id, order.order_number, order.automation_status, automation_status, req.admin!.id, req.admin!.username, note || null],
);
}
if (priority && priority !== order.priority) {
updates.push(`priority = $${idx++}`); params.push(priority);
}
if (assigned_to !== undefined) {
updates.push(`assigned_to = $${idx++}`); params.push(assigned_to || null);
await pool.query(
`INSERT INTO order_audit_log (order_type, order_id, order_number, action, actor_type, actor_id, actor_name, note)
VALUES ('formation', $1, $2, 'assigned', 'admin', $3, $4, $5)`,
[id, order.order_number, req.admin!.id, req.admin!.username, `Assigned to admin #${assigned_to || "unassigned"}`],
);
}
if (admin_notes !== undefined) {
updates.push(`admin_notes = $${idx++}`); params.push(admin_notes);
}
// Add a note to audit log if provided without other changes
if (note && !status && !automation_status) {
await pool.query(
`INSERT INTO order_audit_log (order_type, order_id, order_number, action, actor_type, actor_id, actor_name, note)
VALUES ('formation', $1, $2, 'note_added', 'admin', $3, $4, $5)`,
[id, order.order_number, req.admin!.id, req.admin!.username, note],
);
}
if (updates.length > 0) {
updates.push("last_activity_at = now()");
updates.push(`updated_at = now()`);
params.push(id);
await pool.query(
`UPDATE formation_orders SET ${updates.join(", ")} WHERE id = $${idx}`,
params,
);
}
res.json({ success: true, message: "Order updated." });
} catch (err) {
console.error("[admin/formations/:id PATCH] Error:", err);
res.status(500).json({ error: "Could not update order." });
}
});
// =====================================================================
// Dashboard Stats
// =====================================================================
/** GET /api/v1/admin/stats — Queue overview counts. */
router.get("/api/v1/admin/stats", requireAdmin, async (req, res) => {
try {
const formations = await pool.query(`
SELECT
COUNT(*) FILTER (WHERE status = 'received') as received,
COUNT(*) FILTER (WHERE status = 'processing') as processing,
COUNT(*) FILTER (WHERE status = 'submitted') as submitted,
COUNT(*) FILTER (WHERE status = 'filed') as filed,
COUNT(*) FILTER (WHERE status = 'delivered') as delivered,
COUNT(*) FILTER (WHERE status = 'cancelled') as cancelled,
COUNT(*) FILTER (WHERE automation_status = 'failed') as automation_failed,
COUNT(*) FILTER (WHERE automation_status = 'manual') as manual_required,
COUNT(*) FILTER (WHERE priority = 'urgent') as urgent,
COUNT(*) FILTER (WHERE assigned_to IS NULL AND status NOT IN ('delivered','cancelled')) as unassigned,
COUNT(*) as total
FROM formation_orders
`);
const subscribers = await pool.query(
"SELECT COUNT(*) as total, COUNT(*) FILTER (WHERE unsubscribed = FALSE) as active FROM subscribers",
);
const quotes = await pool.query(
"SELECT COUNT(*) FILTER (WHERE status = 'pending') as pending FROM quotes",
);
const tickets = await pool.query(
"SELECT COUNT(*) as total FROM tickets WHERE created_at > now() - interval '24 hours'",
);
const revenue = await pool.query(
"SELECT COALESCE(SUM(total_cents), 0) as total_cents FROM formation_orders WHERE status NOT IN ('cancelled')",
);
res.json({
formations: formations.rows[0],
subscribers: subscribers.rows[0],
quotes: quotes.rows[0],
tickets_24h: parseInt(tickets.rows[0].total, 10),
revenue_cents: parseInt(revenue.rows[0].total_cents, 10),
});
} catch (err) {
console.error("[admin/stats] Error:", err);
res.status(500).json({ error: "Could not load stats." });
}
});
// =====================================================================
// Audit Log
// =====================================================================
/** GET /api/v1/admin/audit — Recent audit log entries. */
router.get("/api/v1/admin/audit", requireAdmin, async (req, res) => {
try {
const limit = Math.min(parseInt(req.query.limit as string, 10) || 50, 200);
const result = await pool.query(
"SELECT * FROM order_audit_log ORDER BY created_at DESC LIMIT $1",
[limit],
);
res.json({ entries: result.rows });
} catch (err) {
console.error("[admin/audit] Error:", err);
res.status(500).json({ error: "Could not load audit log." });
}
});
// =====================================================================
// Compliance Orders (telecom / DOT / healthcare service fulfillment)
//
// The legacy admin SPA only manages formation_orders. Compliance service
// orders (compliance_orders) had no admin surface at all -- you couldn't see
// what was paid, what was stuck on intake, or approve a prepared filing for
// submission. These endpoints back the /admin/compliance-orders page.
// =====================================================================
/** GET /api/v1/admin/compliance-orders — list, grouped by batch, with filters. */
router.get("/api/v1/admin/compliance-orders", requireAdmin, async (req, res) => {
try {
const payment = (req.query.payment as string) || "";
const fulfillment = (req.query.fulfillment as string) || "";
const intake = (req.query.intake as string) || ""; // 'incomplete' | 'complete'
const q = ((req.query.q as string) || "").trim().toLowerCase();
const limit = Math.min(parseInt(req.query.limit as string, 10) || 200, 500);
const where: string[] = ["1=1"];
const params: any[] = [];
let i = 1;
if (payment) { where.push(`co.payment_status = $${i++}`); params.push(payment); }
if (fulfillment === "none") { where.push(`co.fulfillment_status IS NULL`); }
else if (fulfillment) { where.push(`co.fulfillment_status = $${i++}`); params.push(fulfillment); }
if (intake === "incomplete") { where.push(`COALESCE(co.intake_data_validated, FALSE) = FALSE`); }
else if (intake === "complete") { where.push(`co.intake_data_validated = TRUE`); }
if (q) {
where.push(`(lower(co.customer_email) LIKE $${i} OR lower(co.customer_name) LIKE $${i} OR lower(co.order_number) LIKE $${i} OR lower(COALESCE(co.batch_id,'')) LIKE $${i})`);
params.push(`%${q}%`); i++;
}
params.push(limit);
const { rows } = await pool.query(
`SELECT co.order_number, co.batch_id, co.service_slug, co.service_name,
co.customer_email, co.customer_name, co.customer_phone,
co.payment_status, co.payment_method, co.paid_at,
co.service_fee_cents, co.gov_fee_cents, co.surcharge_cents, co.discount_cents,
co.fulfillment_status, co.fulfillment_status_at,
COALESCE(co.intake_data_validated, FALSE) AS intake_data_validated,
co.intake_reminder_count, co.intake_reminder_last_at,
co.erpnext_sales_order, co.created_at
FROM compliance_orders co
WHERE ${where.join(" AND ")}
ORDER BY co.created_at DESC
LIMIT $${i}`,
params,
);
// Group multi-service batches (batch_id) into a single card; standalone
// orders (no batch_id) become their own one-item group keyed by order_number.
type Grp = {
group_id: string;
is_batch: boolean;
customer_name: string;
customer_email: string;
customer_phone: string | null;
payment_status: string;
payment_method: string | null;
paid_at: string | null;
created_at: string;
total_cents: number;
intake_all_complete: boolean;
intake_any_incomplete: boolean;
max_reminder_count: number;
last_reminded_at: string | null;
services: any[];
};
const groups = new Map<string, Grp>();
for (const r of rows as any[]) {
const key = r.batch_id || r.order_number;
let g = groups.get(key);
if (!g) {
g = {
group_id: key,
is_batch: !!r.batch_id,
customer_name: r.customer_name,
customer_email: r.customer_email,
customer_phone: r.customer_phone,
payment_status: r.payment_status,
payment_method: r.payment_method,
paid_at: r.paid_at,
created_at: r.created_at,
total_cents: 0,
intake_all_complete: true,
intake_any_incomplete: false,
max_reminder_count: 0,
last_reminded_at: null,
services: [],
};
groups.set(key, g);
}
g.total_cents += Number(r.service_fee_cents || 0) + Number(r.gov_fee_cents || 0)
+ Number(r.surcharge_cents || 0) - Number(r.discount_cents || 0);
if (!r.intake_data_validated) { g.intake_all_complete = false; g.intake_any_incomplete = true; }
g.max_reminder_count = Math.max(g.max_reminder_count, Number(r.intake_reminder_count || 0));
if (r.intake_reminder_last_at && (!g.last_reminded_at || r.intake_reminder_last_at > g.last_reminded_at)) {
g.last_reminded_at = r.intake_reminder_last_at;
}
g.services.push({
order_number: r.order_number,
service_slug: r.service_slug,
service_name: r.service_name || r.service_slug,
fulfillment_status: r.fulfillment_status,
fulfillment_status_at: r.fulfillment_status_at,
intake_data_validated: r.intake_data_validated,
erpnext_sales_order: r.erpnext_sales_order,
ready_to_approve: r.fulfillment_status === "ready_to_file",
});
}
res.json({ groups: Array.from(groups.values()) });
} catch (err) {
console.error("[admin/compliance-orders] Error:", err);
res.status(500).json({ error: "Could not load compliance orders." });
}
});
/** GET /api/v1/admin/compliance-orders/stats — queue overview counts. */
router.get("/api/v1/admin/compliance-orders/stats", requireAdmin, async (_req, res) => {
try {
const { rows } = await pool.query(`
SELECT
COUNT(*) FILTER (WHERE payment_status = 'paid') AS paid,
COUNT(*) FILTER (WHERE payment_status = 'pending_payment') AS pending_payment,
COUNT(*) FILTER (WHERE payment_status = 'paid' AND COALESCE(intake_data_validated, FALSE) = FALSE) AS paid_intake_incomplete,
COUNT(*) FILTER (WHERE fulfillment_status = 'ready_to_file') AS ready_to_file,
COUNT(*) FILTER (WHERE fulfillment_status = 'awaiting_intake') AS awaiting_intake,
COUNT(*) FILTER (WHERE fulfillment_status = 'completed') AS completed,
COUNT(*) AS total
FROM compliance_orders
`);
res.json(rows[0]);
} catch (err) {
console.error("[admin/compliance-orders/stats] Error:", err);
res.status(500).json({ error: "Could not load stats." });
}
});
/** GET /api/v1/admin/compliance-orders/:order_number — single order full detail. */
router.get("/api/v1/admin/compliance-orders/:order_number", requireAdmin, async (req, res) => {
try {
const { rows } = await pool.query(
`SELECT co.*, te.legal_name AS entity_name, te.frn AS entity_frn
FROM compliance_orders co
LEFT JOIN telecom_entities te ON te.id = co.telecom_entity_id
WHERE co.order_number = $1`,
[req.params.order_number],
);
if (rows.length === 0) { res.status(404).json({ error: "Order not found." }); return; }
const order = rows[0];
const audit = await pool.query(
`SELECT * FROM order_audit_log
WHERE order_number = $1 AND order_type IN ('compliance', 'compliance_batch')
ORDER BY created_at DESC`,
[order.order_number],
);
res.json({ order, audit_log: audit.rows });
} catch (err) {
console.error("[admin/compliance-orders/:id] Error:", err);
res.status(500).json({ error: "Could not load order." });
}
});
/**
* POST /api/v1/admin/compliance-orders/:order_number/approve
* Approve a prepared filing held at fulfillment_status='ready_to_file' and
* dispatch the worker to actually submit it to the government system.
*/
router.post("/api/v1/admin/compliance-orders/:order_number/approve", requireAdmin, async (req, res) => {
const id = req.params.order_number;
try {
const { rows } = await pool.query(
`SELECT order_number, service_slug, fulfillment_status
FROM compliance_orders WHERE order_number = $1`,
[id],
);
const order = rows[0];
if (!order) { res.status(404).json({ error: "Order not found." }); return; }
if (order.fulfillment_status !== "ready_to_file") {
res.status(409).json({
error: `Order is not awaiting submission approval (status=${order.fulfillment_status ?? "none"}).`,
});
return;
}
await pool.query(
`UPDATE compliance_orders
SET fulfillment_status = 'authorization_signed', fulfillment_status_at = now(), updated_at = now()
WHERE order_number = $1`,
[id],
);
await pool.query(
`INSERT INTO order_audit_log (order_type, order_id, order_number, action, from_status, to_status, actor_type, actor_id, actor_name, note)
VALUES ('compliance', 0, $1, 'approved_for_submission', 'ready_to_file', 'authorization_signed', 'admin', $2, $3, $4)`,
[id, req.admin!.id, req.admin!.username, (req.body?.note as string) || "Approved + dispatched for government submission"],
);
const workerUrl = process.env.WORKER_URL || "http://workers:8090";
let dispatched = false;
try {
const r = await fetch(`${workerUrl}/jobs`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "process_compliance_service",
order_name: id,
order_number: id,
service_slug: order.service_slug,
admin_approved: true,
}),
});
dispatched = r.ok;
} catch (err) {
console.error(`[admin/compliance-orders] approve dispatch failed for ${id}:`, err);
}
res.json({ success: true, order_number: id, dispatched });
} catch (err) {
console.error(`[admin/compliance-orders] approve error for ${id}:`, err);
res.status(500).json({ error: "Approve failed." });
}
});
/**
* POST /api/v1/admin/compliance-orders/:order_number/rearm-intake
* Re-arm the daily intake reminder for a paid+incomplete order whose reminders
* went quiet (clears intake_reminder_last_at so the next daily run nudges it).
* Optionally resets the count back to 0 with { reset_count: true }.
*/
router.post("/api/v1/admin/compliance-orders/:order_number/rearm-intake", requireAdmin, async (req, res) => {
const id = req.params.order_number;
try {
const resetCount = req.body?.reset_count === true;
const { rows } = await pool.query(
`SELECT order_number, batch_id, payment_status,
COALESCE(intake_data_validated, FALSE) AS intake_data_validated
FROM compliance_orders WHERE order_number = $1`,
[id],
);
const order = rows[0];
if (!order) { res.status(404).json({ error: "Order not found." }); return; }
if (order.payment_status !== "paid") {
res.status(409).json({ error: "Only paid orders can be re-armed." });
return;
}
if (order.intake_data_validated) {
res.status(409).json({ error: "Intake is already complete for this order." });
return;
}
// Re-arm the whole batch when this order belongs to one, so a multi-service
// customer gets a single consolidated nudge (matches the worker grouping).
const filter = order.batch_id
? { clause: "batch_id = $1", val: order.batch_id }
: { clause: "order_number = $1", val: id };
const updated = await pool.query(
`UPDATE compliance_orders
SET intake_reminder_last_at = NULL
${resetCount ? ", intake_reminder_count = 0" : ""},
updated_at = now()
WHERE ${filter.clause}
AND payment_status = 'paid'
AND COALESCE(intake_data_validated, FALSE) = FALSE
RETURNING order_number`,
[filter.val],
);
await pool.query(
`INSERT INTO order_audit_log (order_type, order_id, order_number, action, actor_type, actor_id, actor_name, note)
VALUES ('compliance', 0, $1, 'intake_reminder_rearmed', 'admin', $2, $3, $4)`,
[id, req.admin!.id, req.admin!.username,
`Re-armed intake reminder for ${updated.rowCount} order(s)${resetCount ? " (count reset to 0)" : ""}`],
);
res.json({ success: true, rearmed: updated.rowCount });
} catch (err) {
console.error(`[admin/compliance-orders] rearm-intake error for ${id}:`, err);
res.status(500).json({ error: "Re-arm failed." });
}
});
// ── Document discovery + viewing ─────────────────────────────────────────────
//
// Every prepared/signed filing PDF lives in MinIO. We surface them so you can
// review the actual document (e.g. the signed MCS-150 / authorization) before
// approving an order for government submission. Keys come from three places:
// 1. esign_records — the unsigned + signed e-sign PDFs per order
// 2. intake_data.filing_status — pdf_minio_path / attested_pdf / evidence/*
// 3. compliance_orders cols — engagement_letter_minio_key, rmd_packet_minio_paths
//
// Browsers can't open MinIO's IP-allowlisted public host, so instead of handing
// out presigned URLs we STREAM the object through the API (JWT-gated). The
// stream endpoint accepts the token via ?token= so it works in a new tab.
const WORKER_URL_ADMIN = process.env.WORKER_URL || "http://workers:8090";
/** Ask the worker for a presigned (internal minio:9000) GET URL. */
async function presignInternal(key: string): Promise<string | null> {
if (!key) return null;
try {
const r = await fetch(`${WORKER_URL_ADMIN}/jobs/presign`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key, expires: 600, method: "GET" }),
});
if (!r.ok) return null;
const data = (await r.json()) as { url?: string };
return data.url || null;
} catch {
return null;
}
}
/** Collect every known MinIO document key for an order (deduped, labeled). */
async function collectOrderDocuments(orderNumber: string): Promise<Array<{ label: string; key: string }>> {
const docs: Array<{ label: string; key: string }> = [];
const seen = new Set<string>();
const add = (label: string, key: unknown) => {
if (typeof key === "string" && key && !seen.has(key)) {
seen.add(key);
docs.push({ label, key });
}
};
// 1. Order row: intake_data filing artifacts + per-order document columns.
const ord = await pool.query(
`SELECT intake_data, engagement_letter_minio_key, rmd_packet_minio_paths
FROM compliance_orders WHERE order_number = $1`,
[orderNumber],
);
if (ord.rows.length) {
const o = ord.rows[0];
const intake = (o.intake_data && typeof o.intake_data === "object" ? o.intake_data : {}) as Record<string, any>;
const fs = (intake.filing_status && typeof intake.filing_status === "object" ? intake.filing_status : {}) as Record<string, any>;
add("Prepared filing PDF", fs.pdf_minio_path);
add("Attested PDF (faxed)", fs.attested_pdf);
add("Confirmation screenshot", fs.screenshot_path);
if (fs.evidence && typeof fs.evidence === "object") {
for (const [k, v] of Object.entries(fs.evidence as Record<string, any>)) {
add("Evidence: " + k.replace(/_/g, " "), v);
}
}
add("Engagement letter", o.engagement_letter_minio_key);
const rmd = o.rmd_packet_minio_paths;
if (Array.isArray(rmd)) rmd.forEach((k, i) => add(`RMD packet ${i + 1}`, k));
else if (rmd && typeof rmd === "object") for (const [k, v] of Object.entries(rmd)) add("RMD: " + k, v);
}
// 2. e-sign records: the unsigned doc and (once signed) the signed doc.
const es = await pool.query(
`SELECT document_type, status, document_minio_key, signed_document_minio_key
FROM esign_records WHERE order_number = $1 ORDER BY created_at`,
[orderNumber],
);
for (const r of es.rows as any[]) {
const t = (r.document_type || "document").replace(/[-_]/g, " ");
if (r.signed_document_minio_key) add(`Signed ${t}`, r.signed_document_minio_key);
add(`${r.status === "signed" ? "Unsigned" : "Pending"} ${t}`, r.document_minio_key);
}
return docs;
}
/** GET /api/v1/admin/compliance-orders/:order_number/documents — list viewable PDFs. */
router.get("/api/v1/admin/compliance-orders/:order_number/documents", requireAdmin, async (req, res) => {
try {
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; }
const docs = await collectOrderDocuments(req.params.order_number);
res.json({ order_number: req.params.order_number, documents: docs });
} catch (err) {
console.error("[admin/compliance-orders/documents] Error:", err);
res.status(500).json({ error: "Could not list documents." });
}
});
/**
* GET /api/v1/admin/compliance-orders/:order_number/document?key=...&token=...
* Stream a single MinIO object through the API (JWT-gated via header or ?token).
* The key MUST be one we discovered for this order — prevents arbitrary-object
* reads from a valid admin token.
*/
router.get("/api/v1/admin/compliance-orders/:order_number/document", requireAdminQueryOrHeader, async (req, res) => {
try {
const key = typeof req.query.key === "string" ? req.query.key : "";
if (!key) { res.status(400).json({ error: "key required" }); return; }
const docs = await collectOrderDocuments(req.params.order_number);
if (!docs.some((d) => d.key === key)) {
res.status(403).json({ error: "Key does not belong to this order." });
return;
}
const url = await presignInternal(key);
if (!url) { res.status(502).json({ error: "Could not generate object URL." }); return; }
const upstream = await fetch(url);
if (!upstream.ok || !upstream.body) {
res.status(502).json({ error: `Object fetch failed (${upstream.status}).` });
return;
}
// Infer a sensible content type; default to PDF (almost all are).
const lower = key.toLowerCase();
const ct = lower.endsWith(".pdf") ? "application/pdf"
: lower.endsWith(".png") ? "image/png"
: lower.endsWith(".jpg") || lower.endsWith(".jpeg") ? "image/jpeg"
: lower.endsWith(".xlsx") ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
: upstream.headers.get("content-type") || "application/octet-stream";
res.setHeader("Content-Type", ct);
res.setHeader("Content-Disposition", `inline; filename="${key.split("/").pop() || "document"}"`);
const len = upstream.headers.get("content-length");
if (len) res.setHeader("Content-Length", len);
const buf = Buffer.from(await upstream.arrayBuffer());
res.end(buf);
} catch (err) {
console.error("[admin/compliance-orders/document] Error:", err);
res.status(500).json({ error: "Could not stream document." });
}
});
export default router;