/** * Proxies to the worker's internal HTTP server (DCI_WORKER_URL, default http://127.0.0.1:8320; compose: * http://worker-maint:8320): GET /data-gaps and GET /trace/ (extraction debugger). 10 s timeout, 502 on failure. */ import type { FastifyInstance } from "fastify"; import { z } from "zod"; import { getEnv } from "../../env.js"; import { envelope, HttpError, parseQuery } from "../../lib/http.js"; import { strParam } from "../../lib/params.js"; async function workerGet(path: string): Promise { const base = getEnv().workerUrl.replace(/\/+$/, ""); const url = `${base}${path}`; let host = base; try { host = new URL(base).host; } catch { /* keep raw */ } try { const res = await fetch(url, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(10_000) }); const text = await res.text(); let body: unknown = text; try { body = JSON.parse(text); } catch { /* keep text */ } if (!res.ok) throw new HttpError(res.status === 404 ? 404 : 502, `worker answered HTTP ${res.status}`, { worker: host, path, body }); return body; } catch (e) { if (e instanceof HttpError) throw e; const msg = (e as Error).name === "TimeoutError" ? "timeout after 10 s" : (e as Error).message; throw new HttpError(502, `worker unreachable: ${msg}`, { worker: host, path }); } } export async function proxyAdminRoutes(app: FastifyInstance): Promise { app.get("/data-gaps", { schema: { summary: "Data gaps counters (proxied from the worker GET /data-gaps; 502 when the worker is unreachable)" } }, async () => envelope(await workerGet("/data-gaps"), { source: getEnv().workerUrl })); app.get("/documents/:id/trace", { schema: { summary: "Extraction debugger trace for a document (proxied from the worker GET /trace/?live=1 re-fetches the page)" } }, async (req) => { const { id } = req.params as { id: string }; const q = parseQuery(z.object({ live: strParam }), req.query); const live = q.live === "1" || q.live === "true" ? "1" : ""; return envelope(await workerGet(`/trace/${encodeURIComponent(id)}${live ? "?live=1" : ""}`), { source: getEnv().workerUrl, live: live === "1" }); }); }