# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Per-request frontend base URL (same idea as auth emails: frontendUrl from the client). Set from the incoming HTTP request (X-Frontend-Url, Origin, or Referer) in app middleware. Read when building billing alert links during that request (e.g. pool exhausted email). """ from __future__ import annotations import logging import re from contextvars import ContextVar from typing import Optional from urllib.parse import urlparse logger = logging.getLogger(__name__) _CURRENT_FRONTEND_URL: ContextVar[str] = ContextVar("request_frontend_url", default="") _CORS_ORIGIN_REGEX = re.compile( r"^https://.*\.(poweron\.swiss|poweron-center\.net)$", re.IGNORECASE, ) def setRequestFrontendUrl(url: str) -> None: """Store the frontend base URL for the current request (no trailing slash).""" _CURRENT_FRONTEND_URL.set((url or "").strip().rstrip("/")) def getRequestFrontendUrl() -> str: """Return the frontend base URL for the current request, or empty if unset.""" return _CURRENT_FRONTEND_URL.get() def _allowedOrigins() -> list[str]: try: from modules.shared.configuration import APP_CONFIG raw = APP_CONFIG.get("APP_ALLOWED_ORIGINS") or "" return [o.strip().rstrip("/") for o in raw.split(",") if o.strip()] except Exception: return [] def _isAllowedFrontendOrigin(origin: str) -> bool: if not origin: return False normalized = origin.strip().rstrip("/") if normalized in _allowedOrigins(): return True return bool(_CORS_ORIGIN_REGEX.match(normalized)) def resolveFrontendUrlFromRequest(request) -> str: """ Resolve frontend base URL from the current HTTP request. Priority: X-Frontend-Url (explicit, like auth body frontendUrl) → Origin → Referer origin. Only returns origins that match APP_ALLOWED_ORIGINS or the CORS subdomain regex. """ explicit = (request.headers.get("X-Frontend-Url") or "").strip().rstrip("/") if explicit and _isAllowedFrontendOrigin(explicit): return explicit if explicit: logger.debug("Ignoring X-Frontend-Url not in allowed origins: %s", explicit) origin = (request.headers.get("Origin") or "").strip().rstrip("/") if origin and _isAllowedFrontendOrigin(origin): return origin referer = (request.headers.get("Referer") or "").strip() if referer: try: ref_origin = f"{urlparse(referer).scheme}://{urlparse(referer).netloc}".rstrip("/") if ref_origin and _isAllowedFrontendOrigin(ref_origin): return ref_origin except Exception: pass return ""