/** * Kanonisches Routen-Manifest für die Auth-Gating-Garantie (Definition of * Done #1, Querschnittsstandard 1–3). * * EINZIGE Quelle der Wahrheit darüber, welche Routen geschützt sind und wie ein * ANONYMER Aufruf sich verhalten muss: * - Seiten -> "redirect" (auf /login, mit callbackUrl) * - APIs -> "401" (ohne Daten-Leak) * * Der Driftschutz (routes.manifest.spec.ts + tests/unit/routes-manifest.test.ts) * stellt sicher, dass jede neue Route unter src/app/** entweder hier steht oder * in PUBLIC_ALLOWLIST. Ungetestete Routen sind damit ausgeschlossen. * * Beispiel-UUIDs für dynamische Segmente: anonyme Aufrufe werden VOR jeder * DB-Abfrage abgewiesen, daher müssen diese IDs nicht existieren. */ export { PUBLIC_ALLOWLIST } from "../support/route-scan"; export type AnonExpectation = "redirect" | "401"; export interface RouteEntry { /** Konkreter URL-Pfad (dynamische Segmente bereits aufgelöst). */ path: string; /** Erwartetes Verhalten bei anonymem Zugriff. */ expectWhenAnon: AnonExpectation; /** true für API-Routen (request statt page-Navigation). */ api?: boolean; } const EX_VEHICLE = "00000000-0000-0000-0000-0000000000a1"; const EX_EQUIP = "00000000-0000-0000-0000-0000000000a2"; const EX_BRIGADE = "00000000-0000-0000-0000-0000000000a3"; const EX_TEMPLATE = "00000000-0000-0000-0000-0000000000a4"; const EX_CATEGORY = "00000000-0000-0000-0000-0000000000a5"; export const ROUTES: readonly RouteEntry[] = [ // (app) – Lese-Oberflächen { path: "/", expectWhenAnon: "redirect" }, { path: "/start", expectWhenAnon: "redirect" }, { path: "/fahrzeuge", expectWhenAnon: "redirect" }, { path: `/fahrzeuge/${EX_VEHICLE}`, expectWhenAnon: "redirect" }, { path: "/geraete", expectWhenAnon: "redirect" }, { path: `/geraete/${EX_EQUIP}`, expectWhenAnon: "redirect" }, { path: "/wehren", expectWhenAnon: "redirect" }, { path: `/wehren/${EX_BRIGADE}`, expectWhenAnon: "redirect" }, // (app)/verwaltung – Wehr-Bereich { path: "/verwaltung/benutzer", expectWhenAnon: "redirect" }, { path: "/verwaltung/profil", expectWhenAnon: "redirect" }, { path: "/verwaltung/fahrzeuge", expectWhenAnon: "redirect" }, { path: "/verwaltung/fahrzeuge/neu", expectWhenAnon: "redirect" }, { path: `/verwaltung/fahrzeuge/${EX_VEHICLE}`, expectWhenAnon: "redirect" }, { path: "/verwaltung/geraete", expectWhenAnon: "redirect" }, { path: "/verwaltung/geraete/neu", expectWhenAnon: "redirect" }, { path: `/verwaltung/geraete/${EX_EQUIP}`, expectWhenAnon: "redirect" }, // (admin) – Plattform-Verwaltung { path: "/admin", expectWhenAnon: "redirect" }, { path: "/admin/audit", expectWhenAnon: "redirect" }, { path: "/admin/merkmale", expectWhenAnon: "redirect" }, { path: "/admin/merkmale/proposals", expectWhenAnon: "redirect" }, { path: "/admin/vorlagen", expectWhenAnon: "redirect" }, { path: `/admin/vorlagen/${EX_TEMPLATE}`, expectWhenAnon: "redirect" }, { path: "/admin/geraete-kategorien", expectWhenAnon: "redirect" }, { path: `/admin/geraete-kategorien/${EX_CATEGORY}`, expectWhenAnon: "redirect", }, { path: "/admin/wehren", expectWhenAnon: "redirect" }, { path: "/admin/wehren/neu", expectWhenAnon: "redirect" }, { path: `/admin/wehren/${EX_BRIGADE}`, expectWhenAnon: "redirect" }, // APIs (kein /api/auth, /api/health -> öffentlich/Allowlist) { path: "/api/geo/geocode", expectWhenAnon: "401", api: true }, { path: "/api/geo/health", expectWhenAnon: "401", api: true }, ]; /** * Die Routen-Vorlagen (dynamische Segmente als Platzhalter), wie sie im * Dateisystem erscheinen. Wird vom Driftschutz mit discoverAppRoutes() * abgeglichen. */ export const DECLARED_ROUTE_TEMPLATES: ReadonlySet = new Set([ "/", "/start", "/fahrzeuge", "/fahrzeuge/[id]", "/geraete", "/geraete/[id]", "/wehren", "/wehren/[id]", "/verwaltung/benutzer", "/verwaltung/profil", "/verwaltung/fahrzeuge", "/verwaltung/fahrzeuge/neu", "/verwaltung/fahrzeuge/[id]", "/verwaltung/geraete", "/verwaltung/geraete/neu", "/verwaltung/geraete/[id]", "/admin", "/admin/audit", "/admin/merkmale", "/admin/merkmale/proposals", "/admin/vorlagen", "/admin/vorlagen/[id]", "/admin/geraete-kategorien", "/admin/geraete-kategorien/[id]", "/admin/wehren", "/admin/wehren/neu", "/admin/wehren/[id]", "/api/geo/geocode", "/api/geo/health", ]);