mesh/merge-gate head 454c5662ddcb merged into trunk 671ab9a3ba7b — pass: builds mailu, sent nowhere; no bus step; 1 wait(s) for a person; every machine com…
mesh/repo-check head 454c5662ddcb merged into trunk 671ab9a3ba7b — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivering: 0 machine step(s) passed
60 lines
2.9 KiB
TypeScript
60 lines
2.9 KiB
TypeScript
// 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<string, string> = {
|
|
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<string, string | number | null>;
|
|
|
|
export type AutomxRows = {
|
|
database: string;
|
|
tables: Record<string, Row[]>;
|
|
/** 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<string, string>;
|
|
};
|
|
|
|
/** 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<AutomxRows> {
|
|
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;
|
|
}
|