const PUBLIC_ROUTE_EXACT = new Set(["/", "/login", "/register"]); const GUEST_ONLY_ROUTE_EXACT = new Set(["/login", "/register"]); /** 公开主页 /@slug 与仅链接分享 /p/slug/t/token */ const PUBLIC_ROUTE_PREFIXES: string[] = ["/@", "/p/"]; export const DEFAULT_AUTHENTICATED_LANDING_PATH = "/"; function normalizePath(path: string) { const trimmed = path.trim(); if (!trimmed) { return "/"; } return trimmed.length > 1 ? trimmed.replace(/\/+$/, "") : trimmed; } function matchesExactOrPrefix(path: string, exact: Set, prefixes: string[]) { const normalized = normalizePath(path); if (exact.has(normalized)) { return true; } return prefixes.some((prefix) => normalized.startsWith(prefix)); } export function isPublicRoute(path: string) { return matchesExactOrPrefix(path, PUBLIC_ROUTE_EXACT, PUBLIC_ROUTE_PREFIXES); } export function isGuestOnlyRoute(path: string) { return GUEST_ONLY_ROUTE_EXACT.has(normalizePath(path)); } export function normalizeSafeRedirect( value: unknown, fallback = DEFAULT_AUTHENTICATED_LANDING_PATH, ) { if (typeof value !== "string") { return fallback; } const candidate = value.trim(); if (!candidate || !candidate.startsWith("/") || candidate.startsWith("//")) { return fallback; } const lower = candidate.toLowerCase(); if ( lower.startsWith("/http:") || lower.startsWith("/https:") || lower.startsWith("/javascript:") ) { return fallback; } return candidate; }