"""Profile selection utilities for FastAPI route protection. Provides dependency functions that check the active_profile_id cookie and return the selected User profile, or redirect to /. """ from typing import Optional import structlog from fastapi import Depends, Request from sqlmodel import Session from app.database import get_db_session from app.models.user import User logger = structlog.get_logger(__name__) class NoProfileSelectedError(Exception): """Raised when a request lacks a valid profile selection.""" def get_active_profile_id(request: Request) -> Optional[int]: """Extract the active profile ID from the cookie. Args: request: The incoming HTTP request. Returns: The active profile user ID, or None if not set. """ profile_id = request.cookies.get("active_profile_id") if profile_id and profile_id.isdigit(): return int(profile_id) return None def require_active_profile(request: Request, session: Session = Depends(get_db_session)) -> User: """FastAPI dependency that requires a valid profile selection. Reads the active_profile_id cookie, loads the profile from DB, and raises NoProfileSelectedError if missing or invalid. Args: request: The incoming HTTP request. session: Database session (injected by FastAPI). Returns: The selected User profile. Raises: NoProfileSelectedError: If no valid profile is selected. """ profile_id = get_active_profile_id(request) if profile_id is None: raise NoProfileSelectedError() user = session.get(User, profile_id) if user is None: raise NoProfileSelectedError() return user