"""FastAPI dependency injection configuration.""" from typing import Annotated, Optional from fastapi import Depends, UploadFile, Form, HTTPException, Request, Header from fastapi.security import HTTPBearer from pydantic import BaseModel, Field, validator import re from application.use_cases.container import UseCaseContainer from infrastructure.services.container import ServiceContainer from infrastructure.services.jwt_validation_service import JWTValidationService # Create HTTPBearer scheme that will be recognized by OpenAPI bearer_scheme = HTTPBearer( scheme_name="BearerAuth", bearerFormat="JWT", description="JWT Bearer token authentication" ) class ExtractionRequest(BaseModel): """Request model for audio extraction.""" output_format: str = Field(default="mp3", description="Output audio format") quality: str = Field(default="medium", description="Audio quality level") @validator('output_format') def validate_format(cls, v): allowed = ['mp3', 'aac', 'wav', 'flac', 'm4a', 'ogg'] if v.lower() not in allowed: raise ValueError(f"Format must be one of: {', '.join(allowed)}") return v.lower() @validator('quality') def validate_quality(cls, v): allowed = ['high', 'medium', 'low'] if v.lower() not in allowed: raise ValueError(f"Quality must be one of: {', '.join(allowed)}") return v.lower() def extraction_params( output_format: str = Form("mp3"), quality: str = Form("medium") ) -> ExtractionRequest: """Parse and validate extraction parameters.""" return ExtractionRequest(output_format=output_format, quality=quality) async def validate_video_file(video: UploadFile) -> UploadFile: """Validate uploaded video file.""" if not video.filename: raise HTTPException(400, "No filename provided") # Check file extension allowed_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv', '.m4v'] file_ext = '.' + video.filename.lower().split('.')[-1] if '.' in video.filename else '' if file_ext not in allowed_extensions: raise HTTPException( 400, f"Unsupported video format. Allowed: {', '.join(allowed_extensions)}" ) # Check content type (basic validation) if video.content_type and not video.content_type.startswith(('video/', 'application/octet-stream')): raise HTTPException(400, "File must be a video") return video async def get_validated_token( credentials = Depends(bearer_scheme) ) -> str: """ Extract and validate bearer token from HTTPBearer scheme. Args: credentials: HTTPAuthorizationCredentials from HTTPBearer scheme Returns: str: The validated bearer token Raises: HTTPException: 401 if token is missing or invalid """ from infrastructure.config.settings import settings if not settings.enforce_authentication: return "authentication-disabled" if not credentials: raise HTTPException( status_code=401, detail="Missing Authorization header", headers={"WWW-Authenticate": "Bearer"} ) # Extract token from credentials token = credentials.credentials if not token: raise HTTPException( status_code=401, detail="Empty bearer token", headers={"WWW-Authenticate": "Bearer"} ) # Validate JWT structure jwt_service = JWTValidationService() if not jwt_service.validate_structure(token): raise HTTPException( status_code=401, detail="Invalid JWT token structure", headers={"WWW-Authenticate": "Bearer"} ) return token def get_services(request: Request) -> ServiceContainer: """Get service container from app state.""" return request.app.state.get_services() def get_use_cases(request: Request) -> UseCaseContainer: """Get use case container from app state.""" return request.app.state.get_use_cases() # Type aliases for dependency injection ValidatedVideo = Annotated[UploadFile, Depends(validate_video_file)] ExtractionParams = Annotated[ExtractionRequest, Depends(extraction_params)] Services = Annotated[ServiceContainer, Depends(get_services)] UseCases = Annotated[UseCaseContainer, Depends(get_use_cases)] BearerToken = Annotated[str, Depends(get_validated_token)]