""" FastAPI Dependencies Shared dependencies for API endpoints """ from typing import Generator, Optional from fastapi import Depends, HTTPException, status, Security from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.orm import Session from config.database import get_db_session from config.settings import settings from src.database.models import User # ============================================================================= # DATABASE DEPENDENCY # ============================================================================= def get_db() -> Generator[Session, None, None]: """ Database session dependency Yields database session and ensures it's closed after request """ session = get_db_session() try: yield session finally: session.close() # ============================================================================= # AUTHENTICATION DEPENDENCIES # ============================================================================= # HTTP Bearer token scheme security = HTTPBearer(auto_error=False) def get_current_user( credentials: Optional[HTTPAuthorizationCredentials] = Security(security), db: Session = Depends(get_db) ) -> Optional[User]: """ Get current authenticated user from JWT token Args: credentials: Bearer token from Authorization header db: Database session Returns: User object if authenticated, None if auth not required Raises: HTTPException: If authentication required but invalid """ # If authentication not required (development mode) if not settings.require_auth: # Return anonymous user (ID 0) return db.query(User).filter_by(id=0).first() # Authentication required if not credentials: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required", headers={"WWW-Authenticate": "Bearer"}, ) try: # Decode JWT token (implementation in auth.py) from src.api.auth import decode_access_token payload = decode_access_token(credentials.credentials) user_id = payload.get("sub") if user_id is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication token" ) # Get user from database user = db.query(User).filter_by(id=int(user_id)).first() if user is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found" ) return user except Exception as e: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=f"Authentication failed: {str(e)}" ) def require_auth(user: User = Depends(get_current_user)) -> User: """ Require authentication (always) Use this dependency when endpoint must have authentication regardless of global settings Args: user: Current user from get_current_user Returns: Authenticated user Raises: HTTPException: If user not authenticated """ if user is None or user.id == 0: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required for this endpoint" ) return user def optional_auth(user: Optional[User] = Depends(get_current_user)) -> Optional[User]: """ Optional authentication Returns user if authenticated, None otherwise (no error) Args: user: Current user from get_current_user Returns: User if authenticated, None otherwise """ return user # ============================================================================= # PAGINATION DEPENDENCIES # ============================================================================= def get_pagination( skip: int = 0, limit: int = 100 ) -> dict: """ Pagination parameters Args: skip: Number of records to skip limit: Maximum number of records to return Returns: Dictionary with skip and limit """ # Enforce maximum limit max_limit = 1000 if limit > max_limit: limit = max_limit return { "skip": skip, "limit": limit } # ============================================================================= # STORAGE DEPENDENCY # ============================================================================= def get_storage(): """ Get storage backend Returns: Storage backend instance (LocalStorage or S3Storage) """ from src.core.storage import get_storage return get_storage()