audio-processor / interfaces /api /dependencies.py
Tadeas Kosek
fixes vol4
57c92c0
"""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)]