from __future__ import annotations from urllib.parse import quote def normalize_base_path(raw_value: str) -> str: # 空串表示应用直接挂在根路径。 cleaned = str(raw_value or "").strip().strip("/") if not cleaned: return "" return f"/{cleaned}" def join_base_path(base_path: str, local_path: str) -> str: # 统一保证 local path 至少以 / 开头,避免拼接出脏 URL。 normalized_local = local_path if str(local_path or "").startswith("/") else f"/{local_path}" return f"{normalize_base_path(base_path)}{normalized_local}" or "/" def strip_base_path(base_path: str, request_path: str) -> str | None: normalized_base = normalize_base_path(base_path) normalized_request = request_path if str(request_path or "").startswith("/") else f"/{request_path}" # 根路径应用直接返回原路径。 if not normalized_base: return normalized_request # /yumi 本身视作根页面。 if normalized_request == normalized_base: return "/" # 其余请求必须严格落在当前 base path 下。 required_prefix = f"{normalized_base}/" if not normalized_request.startswith(required_prefix): return None return normalized_request[len(normalized_base) :] or "/" def login_redirect_target(request_target: str) -> str: # 未登录回跳保留原始路径和 query。 return f"/login?next={quote(str(request_target or '/'), safe='')}"