"""Shared FastAPI dependencies and helpers for the v1 API.""" from __future__ import annotations from collections.abc import Awaitable, Callable from fastapi import Depends, HTTPException, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from pydantic import BaseModel from redis.asyncio import Redis from sqlalchemy.ext.asyncio import AsyncSession from app.core.auth import InvalidTokenError, decode_access_token from app.core.database import get_db from app.models.user import User from app.repositories.user_repository import get_user_by_id from app.services.cache import cache_get_json, cache_set_json _bearer_scheme = HTTPBearer(auto_error=False) def _unauthorized() -> HTTPException: return HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail={"code": "not_authenticated", "message": "Missing or invalid access token."}, headers={"WWW-Authenticate": "Bearer"}, ) async def get_current_user( credentials: HTTPAuthorizationCredentials | None = Depends(_bearer_scheme), session: AsyncSession = Depends(get_db), ) -> User: if credentials is None: raise _unauthorized() try: user_id = decode_access_token(credentials.credentials) except InvalidTokenError as exc: raise _unauthorized() from exc user = await get_user_by_id(session, user_id) if user is None or not user.is_active: raise _unauthorized() return user def not_found(entity_type: str, aurora_id_str: str) -> HTTPException: return HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail={"code": f"{entity_type}_not_found", "message": f"No such {entity_type}: {aurora_id_str}"}, ) async def cached_response[T: BaseModel]( redis: Redis, key: str, model: type[T], loader: Callable[[], Awaitable[T]], ttl: int | None = None, ) -> T: """Serve `model` from Redis if present, otherwise call `loader`, cache the result, and return it. Fails open to `loader` on any cache error.""" cached = await cache_get_json(redis, key) if cached is not None: return model.model_validate(cached) result = await loader() await cache_set_json(redis, key, result.model_dump(mode="json"), ttl=ttl) return result