import type { FastifyInstance } from "fastify"; import type { ThtRunner } from "../tht/tht-runner.js"; import type { PrincipalContext } from "../auth/principal.js"; import type { Settings } from "../settings/settings-store.js"; import type { WorkspaceRegistry } from "../workspaces/registry.js"; import { hasPermission, isPrincipalContext, requirePermission } from "../auth/authorization.js"; export function sqlRoutes(app: FastifyInstance, deps: { tht: ThtRunner; getSettings: (principal: PrincipalContext) => Promise; workspaceRegistry: WorkspaceRegistry; }): void { const runnerFor = (principal: PrincipalContext): any => { const runner = deps.tht as any; return typeof runner.withPrincipal === "function" ? runner.withPrincipal(principal) : runner; }; const readPrincipal = (principal: PrincipalContext): PrincipalContext => ({ ...principal, // The harness still consumes this compatibility bit; it must never exceed session.read_all. isAdmin: hasPermission(principal, "session.read_all"), }); const isNotFound = (error: unknown) => /not found|non trovata|inesistente|404/i.test( error instanceof Error ? error.message : String(error), ); const locate = async (principal: PrincipalContext, id: string, legacyWorkspace?: string) => { const runner = runnerFor(readPrincipal(principal)); if (typeof runner.sessionShow !== "function") return { manifest: {}, workspace: legacyWorkspace }; const registry = deps.workspaceRegistry as Partial; const revisions = typeof registry.listRetainedSnapshots === "function" ? await registry.listRetainedSnapshots.call(deps.workspaceRegistry) : await deps.workspaceRegistry.list(); for (const revision of revisions) { try { const manifest = await runner.sessionShow(id, revision.snapshotPath); if (!manifest) continue; const saved = manifest as { workspace_id?: string; workspace_revision?: string }; if (saved.workspace_id && saved.workspace_revision) { const pinned = await deps.workspaceRegistry.readPinned(saved.workspace_id, saved.workspace_revision); return { manifest, workspace: pinned.workspaceConfigPath ?? (pinned as any).revision?.snapshotPath, }; } return { manifest, workspace: revision.snapshotPath }; } catch (error) { if (!isNotFound(error)) throw error; } } try { const manifest = await runner.sessionShow(id, legacyWorkspace); return manifest ? { manifest, workspace: legacyWorkspace } : undefined; } catch (error) { if (isNotFound(error)) return undefined; throw error; } }; app.post("/sessions/:id/sql/preview", async (req, reply) => { const id = (req.params as any).id as string; const { limit, offset } = (req.body as any) ?? {}; let principal: PrincipalContext; let workspace: string | undefined; try { const authorized = requirePermission(req, reply, "session.use"); if (!isPrincipalContext(authorized)) return authorized; principal = authorized; const settings = await deps.getSettings(principal); const located = await locate(principal, id, settings.workspace); if (!located) return reply.code(404).send({ error: "session not found" }); workspace = located.workspace; } catch { return reply.code(503).send({ error: "session storage is unavailable" }); } try { return await runnerFor(readPrincipal(principal)).sqlPreview(id, { limit, offset }, workspace); } catch (error: any) { return reply.code(500).send({ error: error.message ?? String(error) }); } }); app.post("/sessions/:id/sql/export", async (req, reply) => { const id = (req.params as any).id as string; let principal: PrincipalContext; let workspace: string | undefined; try { const authorized = requirePermission(req, reply, "session.use"); if (!isPrincipalContext(authorized)) return authorized; principal = authorized; const settings = await deps.getSettings(principal); const located = await locate(principal, id, settings.workspace); if (!located) return reply.code(404).send({ error: "session not found" }); workspace = located.workspace; } catch { return reply.code(503).send({ error: "session storage is unavailable" }); } try { return await runnerFor(readPrincipal(principal)).sqlExport(id, workspace); } catch (error: any) { return reply.code(500).send({ error: error.message ?? String(error) }); } }); }