"""Exception handling middleware.""" from datetime import UTC, datetime from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from starlette.exceptions import HTTPException as StarletteHTTPException from app.domain.exceptions import ( AlreadyExistsException, DomainException, ForbiddenException, NotFoundException, UnauthorizedException, ValidationException, ) def get_status_code(exc: DomainException) -> int: """Map domain exceptions to HTTP status codes.""" match exc: case ValidationException(): return 400 case UnauthorizedException(): return 401 case ForbiddenException(): return 403 case NotFoundException(): return 404 case AlreadyExistsException(): return 409 case _: return 500 async def domain_exception_handler(request: Request, exc: DomainException) -> JSONResponse: """Handle domain exceptions.""" status_code = get_status_code(exc) return JSONResponse( status_code=status_code, content={ "error": exc.__class__.__name__, "message": exc.message, "timestamp": datetime.now(UTC).isoformat(), "path": str(request.url.path), }, ) async def http_exception_handler(request: Request, exc: StarletteHTTPException) -> JSONResponse: """Handle HTTP exceptions.""" return JSONResponse( status_code=exc.status_code, content={ "error": "HTTPException", "message": str(exc.detail), "timestamp": datetime.now(UTC).isoformat(), "path": str(request.url.path), }, ) async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse: """Handle generic exceptions.""" return JSONResponse( status_code=500, content={ "error": "InternalServerError", "message": "An unexpected error occurred", "timestamp": datetime.now(UTC).isoformat(), "path": str(request.url.path), }, ) def register_exception_handlers(app: FastAPI) -> None: """Register all exception handlers with FastAPI app.""" if not isinstance(app, FastAPI): raise TypeError("app must be a FastAPI instance") # Domain exceptions app.add_exception_handler(DomainException, domain_exception_handler) # type: ignore[arg-type] # HTTP exceptions app.add_exception_handler(StarletteHTTPException, http_exception_handler) # type: ignore[arg-type] # Generic exceptions (only in production) # In development, let FastAPI show detailed traceback # app.add_exception_handler(Exception, generic_exception_handler)