/** * Next.js instrumentation hook — runs once when the server boots. * * Scope is intentionally narrow: warn early about ZITADEL misconfigurations * that would otherwise cause silent feature failures (Bugs 20, 21, 23, 24 * from the test triage). The check is fire-and-forget — it must NEVER * crash the server, even if ZITADEL is briefly unreachable at boot. * * Add new self-checks here only if they meet the same bar: cheap, side-effect * free, and useful at the precise moment a misconfiguration would otherwise * go unnoticed. * * Docs: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation */ const REQUIRED_ROLE_KEYS = [ "owner", "user", "platform_admin", "platform_operator", ] as const; export async function register() { if (process.env.NEXT_RUNTIME !== "nodejs") return; // Skip during `next build` — there's no need to talk to ZITADEL just to // produce a static build, and we don't want CI builds to depend on it. if (process.env.NEXT_PHASE === "phase-production-build") return; // Lazy import: the instrumentation file runs in a constrained context // before app code; importing at top-level would pull NextAuth/etc. const { listProjectRoles } = await import("@/lib/zitadel"); try { const present = new Set(await listProjectRoles()); const missing = REQUIRED_ROLE_KEYS.filter((k) => !present.has(k)); if (missing.length === 0) { console.log( `[startup] ZITADEL project roles OK (${REQUIRED_ROLE_KEYS.length} canonical keys present).` ); return; } console.warn( `[startup] ZITADEL project ${process.env.ZITADEL_PROJECT_ID} is missing canonical role key(s): ${missing.join(", ")}. ` + `Customer invites and team-page badges will not work. ` + `Run \`node --env-file=.env scripts/zitadel-roles.mjs apply\` to repair.` ); } catch (err) { // Never block startup. The portal can still serve unauthenticated // pages and the operator can investigate at leisure. console.warn("[startup] ZITADEL self-check failed (continuing):", err); } }