import { createHash } from "node:crypto"; import type { CreatePlatformLeadSourceInput, PlatformLeadSourceAdminDto, UpdatePlatformLeadSourceInput } from "@hivegrid/db/platform-leads-admin.server"; export type PlatformLeadSourceBootstrapMode = "dry-run" | "apply"; export const PLATFORM_LEAD_INITIAL_SOURCE_CONFIGURATION = Object.freeze([ { key: "hivegrid-website", displayName: "HiveGrid Website", currentWebsiteSource: true }, { key: "hivegrid-legacy-cloud", displayName: "HiveGrid Cloud Legacy", currentWebsiteSource: false }, { key: "lead-inbox-legacy", displayName: "Lead Inbox Legacy", currentWebsiteSource: false }, ] as const); type CreateAction = { kind: "create"; key: string; input: Omit; }; type UpdateAction = { kind: "update"; key: string; input: Omit; }; type UnchangedAction = { kind: "unchanged"; key: string }; export type PlatformLeadSourceBootstrapAction = CreateAction | UpdateAction | UnchangedAction; export type PlatformLeadSourceBootstrapPlan = { hash: string; actions: PlatformLeadSourceBootstrapAction[]; }; export type PlatformLeadSourceBootstrapService = { createSource(input: CreatePlatformLeadSourceInput): Promise; updateSource(input: UpdatePlatformLeadSourceInput): Promise; }; export type PlatformLeadSourceBootstrapManifest = { schemaVersion: 1; runId: string; mode: PlatformLeadSourceBootstrapMode; startedAt: string; finishedAt: string; operatorBindingSha256: string; planSha256: string; expectedPlanSha256: string | null; planVerified: boolean | null; actions: Array<{ key: string; action: PlatformLeadSourceBootstrapAction["kind"] }>; counts: { createPlanned: number; updatePlanned: number; unchanged: number; created: number; updated: number; failed: number }; outcome: "validated" | "applied" | "blocked" | "failed"; credentialsIssued: 0; notificationsTriggered: 0; }; export class PlatformLeadSourceBootstrapError extends Error { public override readonly name = "PlatformLeadSourceBootstrapError"; public constructor(public readonly code: "INVALID_CONFIGURATION") { super(code); } } export function planPlatformLeadSourceBootstrap( existingSources: readonly PlatformLeadSourceAdminDto[], routingAdminUserId: string ): PlatformLeadSourceBootstrapPlan { const routingUserId = requireIdentifier(routingAdminUserId); const byKey = new Map(); for (const source of existingSources) { if (byKey.has(source.key)) throw new PlatformLeadSourceBootstrapError("INVALID_CONFIGURATION"); byKey.set(source.key, source); } const actions = PLATFORM_LEAD_INITIAL_SOURCE_CONFIGURATION.map((desired) => { const existing = byKey.get(desired.key); if (!existing) { return { kind: "create", key: desired.key, input: { key: desired.key, displayName: desired.displayName, enabled: desired.currentWebsiteSource, defaultAssigneeAdminUserId: desired.currentWebsiteSource ? routingUserId : null, notifyAdminUserIds: desired.currentWebsiteSource ? [routingUserId] : [], rateLimitPerMinute: 60, metadata: { managedBy: "R-LEADS" }, }, }; } if (!desired.currentWebsiteSource) { return existing.enabled ? { kind: "update", key: desired.key, input: { sourceId: existing.id, enabled: false } } : { kind: "unchanged", key: desired.key }; } const notifyAdminUserIds = [...new Set([...existing.notifyAdminUserIds, routingUserId])].sort(); const input: UpdateAction["input"] = { sourceId: existing.id }; if (!existing.enabled) input.enabled = true; if (existing.defaultAssigneeAdminUserId !== routingUserId) input.defaultAssigneeAdminUserId = routingUserId; if (!sameStringList(existing.notifyAdminUserIds, notifyAdminUserIds)) input.notifyAdminUserIds = notifyAdminUserIds; if (Object.keys(input).length === 1) return { kind: "unchanged", key: desired.key }; return { kind: "update", key: desired.key, input }; }); return { hash: hashPlan(actions), actions }; } export async function runPlatformLeadSourceBootstrap(input: { mode: PlatformLeadSourceBootstrapMode; runId: string; actorAdminUserId: string; routingAdminUserId: string; existingSources: readonly PlatformLeadSourceAdminDto[]; expectedPlanSha256?: string; service?: PlatformLeadSourceBootstrapService; clock?: () => Date; }): Promise { const clock = input.clock ?? (() => new Date()); const startedAt = validDate(clock()); const actorAdminUserId = requireIdentifier(input.actorAdminUserId); const routingAdminUserId = requireIdentifier(input.routingAdminUserId); const runId = requireRunId(input.runId); const plan = planPlatformLeadSourceBootstrap(input.existingSources, routingAdminUserId); const expectedPlanSha256 = input.expectedPlanSha256?.trim().toLowerCase() ?? null; const planVerified = expectedPlanSha256 === null ? null : plan.hash === expectedPlanSha256; if (input.mode === "apply" && (!expectedPlanSha256 || !/^[a-f0-9]{64}$/u.test(expectedPlanSha256) || !input.service)) { throw new PlatformLeadSourceBootstrapError("INVALID_CONFIGURATION"); } let created = 0; let updated = 0; let failed = 0; if (input.mode === "apply" && planVerified) { for (const action of plan.actions) { try { if (action.kind === "create") { await input.service!.createSource({ ...action.input, actorAdminUserId, reason: "R-LEADS initial platform lead source bootstrap", requestId: runId, }); created += 1; } else if (action.kind === "update") { await input.service!.updateSource({ ...action.input, actorAdminUserId, reason: "R-LEADS platform lead source routing bootstrap", requestId: runId, }); updated += 1; } } catch { failed += 1; } } } const createPlanned = plan.actions.filter(({ kind }) => kind === "create").length; const updatePlanned = plan.actions.filter(({ kind }) => kind === "update").length; const unchanged = plan.actions.filter(({ kind }) => kind === "unchanged").length; const outcome = input.mode === "dry-run" ? "validated" : planVerified === false ? "blocked" : failed > 0 ? "failed" : "applied"; return { schemaVersion: 1, runId, mode: input.mode, startedAt: startedAt.toISOString(), finishedAt: validDate(clock()).toISOString(), operatorBindingSha256: sha256(routingAdminUserId), planSha256: plan.hash, expectedPlanSha256, planVerified, actions: plan.actions.map(({ key, kind }) => ({ key, action: kind })), counts: { createPlanned, updatePlanned, unchanged, created, updated, failed }, outcome, credentialsIssued: 0, notificationsTriggered: 0, }; } export function isSuccessfulPlatformLeadSourceBootstrapManifest(manifest: PlatformLeadSourceBootstrapManifest): boolean { return manifest.outcome === "validated" || manifest.outcome === "applied"; } function hashPlan(actions: readonly PlatformLeadSourceBootstrapAction[]): string { return sha256( JSON.stringify( actions.map((action) => action.kind === "unchanged" ? action : { kind: action.kind, key: action.key, input: sortRecord(action.input as Record) } ) ) ); } function sortRecord(record: Record): Record { return Object.fromEntries( Object.entries(record) .sort(([left], [right]) => left.localeCompare(right)) .map(([key, value]) => [key, Array.isArray(value) ? [...value].sort() : value]) ); } function sameStringList(left: readonly string[], right: readonly string[]): boolean { if (left.length !== right.length) return false; const sortedLeft = [...left].sort(); return sortedLeft.every((value, index) => value === right[index]); } function requireIdentifier(value: string): string { if (typeof value !== "string") throw new PlatformLeadSourceBootstrapError("INVALID_CONFIGURATION"); const normalized = value.trim(); if (!normalized || normalized.length > 191 || !/^[A-Za-z0-9_-]+$/u.test(normalized)) { throw new PlatformLeadSourceBootstrapError("INVALID_CONFIGURATION"); } return normalized; } function requireRunId(value: string): string { if (typeof value !== "string") throw new PlatformLeadSourceBootstrapError("INVALID_CONFIGURATION"); const normalized = value.trim(); if (!/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,190}$/u.test(normalized)) { throw new PlatformLeadSourceBootstrapError("INVALID_CONFIGURATION"); } return normalized; } function validDate(value: Date): Date { if (!(value instanceof Date) || !Number.isFinite(value.getTime())) throw new PlatformLeadSourceBootstrapError("INVALID_CONFIGURATION"); return value; } function sha256(value: string): string { return createHash("sha256").update(value, "utf8").digest("hex"); }