using Microsoft.AspNetCore.Http; namespace ErsatzTV; // Phase (a) of the Blazor -> ChicoryTV SPA cutover (ersatztv#91). // // The React SPA (served under /app) is now the default UI: the legacy Blazor // routes below 302-redirect to their SPA equivalents. Only routes that already // have SPA parity are listed here. Blazor pages WITHOUT a SPA equivalent are // deliberately left reachable (no redirect) so their functionality stays // available while the SPA catches up: // /system/health (Blazor home escape hatch; Index.razor also lives here), // /channels/{id} edit, /channels/numbers, /media/* (collections etc.), // /ffmpeg, /watermarks, /blocks, /decos, /templates, /deco-templates, // schedule/playout detail editors, /system/logs, /system/troubleshooting. // // Phase (b) removes the redirected Blazor pages entirely, but that is GATED on // full SPA parity for every route in this map. Until then this map is the // single source of truth for "what has migrated" and is expected to grow. public static class LegacyUiRedirects { // Blazor route -> SPA route. EXACT paths only (no prefix matching); a single // trailing slash on the request is normalized away before lookup. public static readonly IReadOnlyDictionary Map = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["/"] = "/app", ["/channels"] = "/app/channels", ["/channels/add"] = "/app/new-channel", ["/schedules"] = "/app/schedules", ["/playouts"] = "/app/playouts", ["/media/libraries"] = "/app/libraries", ["/settings/ffmpeg"] = "/app/settings/streaming", ["/settings/hdhr"] = "/app/settings/system", ["/settings/logging"] = "/app/settings/logging", ["/settings/playout"] = "/app/settings/playout", ["/settings/scanner"] = "/app/settings/scanner", ["/settings/ui"] = "/app/settings/general", ["/settings/xmltv"] = "/app/settings/xmltv" }; public static bool TryGetRedirect(PathString path, out string target) { target = string.Empty; string value = path.Value; if (string.IsNullOrEmpty(value)) { return false; } // Normalize a single trailing slash so "/channels/" matches "/channels" // (but keep root "/" intact). Guard against a path of all slashes (e.g. // "//") collapsing down to "/" and falsely matching the root entry. if (value.Length > 1 && value.EndsWith('/')) { string trimmed = value[..^1]; if (trimmed == "/") { return false; } value = trimmed; } if (Map.TryGetValue(value, out string mapped)) { target = mapped; return true; } return false; } }