/** * Domain-Trennung Website ↔ App: `craftvia.de` zeigt nur die Produktseiten, `app.craftvia.de` die * Anwendung. Aktiv nur, wenn `MARKETING_HOST` gesetzt ist; die App-Domain kommt aus `AUTH_URL`. * Ohne `MARKETING_HOST` (lokal, Tests) läuft alles wie bisher auf einem Host. * * Links der Produktseiten bleiben relativ (/login, /testen): der Proxy leitet sie von der * Website-Domain auf die App-Domain um. So wird keine Domain beim Image-Bau festgeschrieben. */ export const MARKETING_PATHS = ["/", "/funktionen", "/sicherheit", "/preise", "/impressum"] as const; export type HostSplit = { marketingHost: string; appHost: string; appOrigin: string }; export type HostDecision = { kind: "next" } | { kind: "redirect"; location: string; status: 307 | 308 }; export function hostSplit(env: Record = process.env): HostSplit | null { const marketingHost = env.MARKETING_HOST?.trim() .toLowerCase() .replace(/^https?:\/\//, "") .replace(/[/:].*$/, ""); const authUrl = env.AUTH_URL?.trim(); if (!marketingHost || !authUrl) return null; let app: URL; try { app = new URL(authUrl); } catch { return null; } const appHost = app.hostname.toLowerCase(); if (appHost === marketingHost) return null; return { marketingHost, appHost, appOrigin: app.origin }; } export function isMarketingPath(pathname: string): boolean { return MARKETING_PATHS.some((p) => pathname === p || (p !== "/" && pathname.startsWith(p + "/"))); } export function routeByHost(input: { host: string; pathname: string; search: string; hasSession: boolean }, split: HostSplit): HostDecision { const host = input.host.toLowerCase().split(",")[0].trim().replace(/:\d+$/, ""); const target = input.pathname + input.search; const marketingOrigin = `https://${split.marketingHost}`; // www → Hauptdomain if (host === `www.${split.marketingHost}`) return { kind: "redirect", location: marketingOrigin + target, status: 308 }; // Website-Domain: nur Produktseiten, alles andere (Login, Testphase, App, API) gehört zur App if (host === split.marketingHost) { return isMarketingPath(input.pathname) ? { kind: "next" } : { kind: "redirect", location: split.appOrigin + target, status: 308 }; } // Interne Aufrufe (Healthcheck über 127.0.0.1, Worker) bleiben unverändert if (host !== split.appHost) return { kind: "next" }; // App-Domain: Produktseiten wohnen auf der Website, die Startseite ist der Login if (input.pathname !== "/" && isMarketingPath(input.pathname)) return { kind: "redirect", location: marketingOrigin + target, status: 308 }; if (input.pathname === "/" && !input.hasSession) return { kind: "redirect", location: `${split.appOrigin}/login`, status: 307 }; return { kind: "next" }; }