// Host → tenant slug parser for the portal middleware. // // In dev we serve at .localhost:3000 (e.g. acme.localhost:3000). In // prod we serve at .breakpilot.com. Backstage lives at the apex — // no subdomain — and resolves to a fixed `__backstage__` slug. export type HostMatch = | { kind: "tenant"; slug: string } | { kind: "backstage" } | { kind: "apex" } | { kind: "unknown" }; // Longest-first so `stage.breakpilot.com` is matched before `breakpilot.com`. // Built-ins cover dev (localhost) + the canonical breakpilot.com targets. // PORTAL_APEX_HOSTS is a comma-separated env override for per-environment // hosts (e.g. portal-dev.meghsakha.com while breakpilot.com isn't registered). const APEX_HOSTS = (() => { const base = ["stage.breakpilot.com", "breakpilot.com", "localhost"]; const extra = (process.env.PORTAL_APEX_HOSTS ?? "") .split(",") .map((h) => h.trim().toLowerCase()) .filter(Boolean); // Longest-first to keep the suffix-strip loop correct. return Array.from(new Set([...extra, ...base])).sort( (a, b) => b.length - a.length, ); })(); const APEX_SET = new Set(APEX_HOSTS); export function parseHost(host: string | null | undefined): HostMatch { if (!host) return { kind: "unknown" }; const hostNoPort = host.split(":")[0].toLowerCase(); if (APEX_SET.has(hostNoPort)) return { kind: "apex" }; // Strip the known apex suffix to extract the subdomain. for (const apex of APEX_HOSTS) { const suffix = `.${apex}`; if (hostNoPort.endsWith(suffix)) { const sub = hostNoPort.slice(0, -suffix.length); if (!sub) return { kind: "apex" }; // Backstage is reserved. if (sub === "backstage") return { kind: "backstage" }; // Slugs are [a-z0-9-]{2,40} per the tenant-registry schema check. if (/^[a-z0-9-]{2,40}$/.test(sub)) { return { kind: "tenant", slug: sub }; } return { kind: "unknown" }; } } return { kind: "unknown" }; }