import path from "node:path"; import { pathToFileURL } from "node:url"; import { safeParseEkklesiaUserId } from "@ekklesia/contracts"; import { bootstrapPlatformOwner, createAuthorizationDrizzleDatabase, PlatformOwnerBootstrapError, } from "../authorization/index.js"; import { resolveExecutableDatabaseTransport } from "./database-transport-environment.js"; class BootstrapCliError extends Error { constructor(message: string) { super(message); this.name = "BootstrapCliError"; } } const usage = "Usage: pnpm authorization:bootstrap-owner -- --user-id --confirm-user-id "; interface ParsedArgs { readonly confirmUserId: string; readonly userId: string; } /** A minimal, hand-rolled flag parser — this command takes exactly two required, named flags, so * a general-purpose argument-parsing dependency is not warranted. */ function parseArgs(argv: readonly string[]): ParsedArgs { let userId: string | undefined; let confirmUserId: string | undefined; for (let index = 0; index < argv.length; index += 1) { const flag = argv[index]; if (flag === "--user-id") { userId = argv[index + 1]; index += 1; } else if (flag === "--confirm-user-id") { confirmUserId = argv[index + 1]; index += 1; } } if (userId === undefined || confirmUserId === undefined) { throw new BootstrapCliError(usage); } return { confirmUserId, userId }; } async function main(): Promise { const { confirmUserId, userId: rawUserId } = parseArgs(process.argv.slice(2)); // Deliberately not "infer the first user" or "select by signup order" — the operator must name // the exact, opaque Ekklesia user id twice, and both must match exactly, before anything else // is even attempted. if (rawUserId !== confirmUserId) { throw new BootstrapCliError( "--user-id and --confirm-user-id must match exactly. No changes were made.", ); } const parsedUserId = safeParseEkklesiaUserId(rawUserId); if (!parsedUserId.success) { throw new BootstrapCliError("The provided user id is not a valid Ekklesia user id."); } // Read only when this command actually executes — never merely by importing this module (see // isDirectExecution() below) or any module it imports. const databaseUrl = process.env.DATABASE_URL; if (typeof databaseUrl !== "string" || databaseUrl.trim().length === 0) { throw new BootstrapCliError("DATABASE_URL is required."); } // Asserted before the connection is constructed, so an unsafe production transport never opens // a client at all. const transport = resolveExecutableDatabaseTransport(databaseUrl); const { close, db } = createAuthorizationDrizzleDatabase(databaseUrl, { connectionTimeoutMs: 10_000, operationTimeoutMs: 10_000, transport, }); try { await bootstrapPlatformOwner(db, { userId: parsedUserId.data }); console.log(`Platform-owner bootstrap complete for user "${parsedUserId.data}".`); } finally { await close().catch(() => undefined); } } function reportBootstrapCliError(error: unknown): void { if (error instanceof BootstrapCliError) { console.error(error.message); return; } if (error instanceof PlatformOwnerBootstrapError) { console.error(`[${error.code}] ${error.message}`); return; } console.error("Platform-owner bootstrap failed unexpectedly."); } function isDirectExecution(): boolean { const entrypoint = process.argv[1]; return ( entrypoint !== undefined && import.meta.url === pathToFileURL(path.resolve(entrypoint)).href ); } if (isDirectExecution()) { main().catch((error: unknown) => { reportBootstrapCliError(error); process.exitCode = 1; }); }