# interfaces/api/dependencies.py """FastAPI dependency injection configuration.""" from typing import Annotated, Optional from fastapi import Depends, UploadFile, Form, HTTPException, Request, Header 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 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 validate_bearer_token( authorization: Optional[str] = Header(None, description="Bearer token for authentication") ) -> str: """ Extract and validate bearer token from Authorization header. Args: authorization: Authorization header value Returns: str: The validated bearer token Raises: HTTPException: 401 if token is missing or invalid """ if not authorization: raise HTTPException( status_code=401, detail="Missing Authorization header", headers={"WWW-Authenticate": "Bearer"} ) # Check if it starts with "Bearer " if not authorization.startswith("Bearer "): raise HTTPException( status_code=401, detail="Invalid Authorization header format. Expected: Bearer ", headers={"WWW-Authenticate": "Bearer"} ) # Extract token token = authorization[7:] # Remove "Bearer " prefix 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(validate_bearer_token)]