audio-processor / interfaces /api /dependencies.py
tedowski's picture
n8n-improvements (#1)
dbe78dd verified
raw
history blame
4.46 kB
# 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 <token>",
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)]