Adds Core-owned identity contracts, a Core-owned Better Auth database schema (ekklesia_core_user/session/account/verification), a first-class Core-owned migration source that coexists with module-owned migrations under the exact existing migration system, and an explicit Better Auth construction boundary. No authentication endpoint or sign-in UI is exposed yet (ADR 0010). Co-Authored-By: Claude Sonnet 5 <[email protected]>
58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import type { DoctorCheck, DoctorCheckId, DoctorReport, DoctorResult } from "./doctor.js";
|
|
|
|
interface DoctorSection {
|
|
readonly checkIds: readonly DoctorCheckId[];
|
|
readonly title: string;
|
|
}
|
|
|
|
function section(title: string, checkIds: readonly DoctorCheckId[]): DoctorSection {
|
|
return Object.freeze({ checkIds: Object.freeze([...checkIds]), title });
|
|
}
|
|
|
|
const sections: readonly DoctorSection[] = Object.freeze([
|
|
section("Platform", ["platform.runtime"]),
|
|
section("Modules", ["modules.discovery", "modules.dependencies"]),
|
|
section("Data definitions", ["schemas.discovery", "migrations.discovery"]),
|
|
section("PostgreSQL", ["postgresql.connection", "postgresql.plan"]),
|
|
section("Identity", ["identity.configuration"]),
|
|
section("Object storage", ["object-storage.integration"]),
|
|
]);
|
|
|
|
const statusSymbols: Readonly<Record<DoctorCheck["status"], string>> = Object.freeze({
|
|
failure: "✗",
|
|
pass: "✓",
|
|
skipped: "○",
|
|
warning: "!",
|
|
});
|
|
|
|
const resultLabels: Readonly<Record<DoctorResult, string>> = Object.freeze({
|
|
"not-ready": "NOT READY",
|
|
ready: "READY",
|
|
"ready-with-warnings": "READY WITH WARNINGS",
|
|
});
|
|
|
|
export function formatDoctorReport(report: DoctorReport): readonly string[] {
|
|
const checks = new Map(report.checks.map((check) => [check.id, check]));
|
|
const lines = ["Ekklesia Doctor", ""];
|
|
|
|
for (const [sectionIndex, section] of sections.entries()) {
|
|
lines.push(section.title);
|
|
for (const checkId of section.checkIds) {
|
|
const check = checks.get(checkId);
|
|
if (!check) {
|
|
continue;
|
|
}
|
|
lines.push(` ${statusSymbols[check.status]} ${check.summary}`);
|
|
for (const detail of check.details) {
|
|
lines.push(` ${detail}`);
|
|
}
|
|
}
|
|
if (sectionIndex < sections.length - 1) {
|
|
lines.push("");
|
|
}
|
|
}
|
|
|
|
lines.push("", `Result: ${resultLabels[report.result]}`);
|
|
return Object.freeze(lines);
|
|
}
|