// What the autoconfiguration module (automx2) holds, as stored: its provider, server, domain and server_domain // rows, read from its SQLite file inside its container with the sqlite3 program the image carries, opened // read-only. Nothing is written and nothing is interpreted: the rows are the evidence for what automx answers // to mail clients (novox/hq issue 1630, ADR 0480), and the reason the answer may be empty. import type { Exec } from "./health.js"; export const AUTOMX_DATABASE = "/data/db.sqlite"; /** The tables read, each with the one query that reads it. Fixed text: the tool takes no query from its caller. */ export const AUTOMX_TABLES: Record = { provider: "SELECT id, name, short_name FROM provider ORDER BY id", server: "SELECT id, prio, name, port, type, socket_type, user_name, authentication FROM server ORDER BY id", domain: "SELECT id, name, provider_id, ldapserver_id FROM domain ORDER BY id", server_domain: "SELECT server_id, domain_id FROM server_domain ORDER BY domain_id, server_id", }; export type Row = Record; export type AutomxRows = { database: string; tables: Record; /** servers whose name is empty: the client is told an empty host name for them */ empty_names: { id: number; type: string }[]; /** tables that could not be read, each with what sqlite3 or docker said on one line */ unreadable: Record; }; /** sqlite3 -json prints one JSON array, or nothing at all for a table without rows. */ export function parseRows(output: string): Row[] { const text = output.trim(); if (text === "") return []; const rows = JSON.parse(text) as unknown; if (!Array.isArray(rows)) throw new Error("sqlite3 did not print a list of rows"); return rows as Row[]; } /** The command that reads one table: read-only, JSON, one fixed statement. */ export function readCommand(sql: string): string[] { return ["sqlite3", "-readonly", "-json", AUTOMX_DATABASE, sql]; } /** Read every table; one that fails is said in `unreadable` and does not hide the others. */ export async function readAutomx(exec: Exec, container: string): Promise { const answer: AutomxRows = { database: AUTOMX_DATABASE, tables: {}, empty_names: [], unreadable: {} }; for (const [table, sql] of Object.entries(AUTOMX_TABLES)) { try { answer.tables[table] = parseRows(await exec(container, readCommand(sql))); } catch (e) { const err = e as { stderr?: string; message?: string }; const text = (err?.stderr ?? "").trim() || (err?.message ?? String(e)).trim(); answer.unreadable[table] = text.split("\n").map((l) => l.trim()).filter(Boolean)[0] ?? "it failed and said nothing"; } } for (const s of answer.tables.server ?? []) { if (String(s.name ?? "").trim() === "") answer.empty_names.push({ id: Number(s.id), type: String(s.type) }); } return answer; }