| 1 | from fastapi import HTTPException |
| 2 | from fastapi import Request |
| 3 | from fastapi.exceptions import RequestValidationError |
| 4 | from fastapi.responses import JSONResponse |
| 5 | from sqlalchemy.ext.asyncio import AsyncSession |
| 6 | |
| 7 | from app.auth.utils import AuthHandler |
| 8 | from app.db.db_session import async_engine # Make sure to import the async engine |
| 9 | from app.utils import ErrorType |
| 10 | from app.utils import Logger |
| 11 | from app.utils import ValidationErrorItem |
| 12 | from app.utils import ValidationErrorResponse |
| 13 | |
| 14 | |
| 15 | # Utility function to get user_id from request |
| 16 | async def get_user_id_from_request(request: Request, logger_instance): |
| 17 | """ |
| 18 | Retrieves the user ID from the given request using the provided logger instance. |
| 19 | |
| 20 | Args: |
| 21 | request (Request): The request object. |
| 22 | logger_instance: The logger instance used to retrieve the user ID. |
| 23 | |
| 24 | Returns: |
| 25 | The user ID extracted from the request. |
| 26 | """ |
| 27 | return await logger_instance.get_user_id_from_request(request) |
| 28 | |
| 29 | |
| 30 | async def custom_http_exception_handler(request: Request, exc: HTTPException): |
| 31 | """ |
| 32 | Custom exception handler for handling HTTP exceptions. |
| 33 | |
| 34 | Args: |
| 35 | request (Request): The incoming request object. |
| 36 | exc (HTTPException): The raised HTTP exception. |
| 37 | |
| 38 | Returns: |
| 39 | JSONResponse: The JSON response containing the error details. |
| 40 | """ |
| 41 | async with AsyncSession(async_engine) as session: # Use AsyncSession |
| 42 | logger_instance = Logger(session, AuthHandler()) |
| 43 | user_id = await get_user_id_from_request(request, logger_instance) |
| 44 | await logger_instance.log_error(user_id, request, exc.detail) |
| 45 | await session.commit() # Make sure to commit the session |
| 46 | |
| 47 | return JSONResponse( |
| 48 | status_code=exc.status_code, |
| 49 | content={ |
| 50 | "success": False, |
| 51 | "message": exc.detail, |
| 52 | }, |
| 53 | ) |
| 54 | |
| 55 | |
| 56 | async def validation_exception_handler(request: Request, exc: RequestValidationError): |
| 57 | """ |
| 58 | Handles validation exceptions and logs the error. |
| 59 | |
| 60 | Args: |
| 61 | request (Request): The incoming request. |
| 62 | exc (RequestValidationError): The validation exception. |
| 63 | |
| 64 | Returns: |
| 65 | JSONResponse: The JSON response with the validation error details. |
| 66 | """ |
| 67 | errors = exc.errors() |
| 68 | details = [] |
| 69 | |
| 70 | for error in errors: |
| 71 | field = error["loc"][-1] |
| 72 | try: |
| 73 | error_type = ErrorType(error["type"]) |
| 74 | except ValueError: |
| 75 | # Pydantic 2 emits codes the v1-era ErrorType enum doesn't list |
| 76 | # (string_too_long, string_pattern_mismatch, etc.) — fall back to |
| 77 | # GENERAL so unknown codes still produce a 422 with a sensible |
| 78 | # message instead of crashing the handler. |
| 79 | error_type = ErrorType.GENERAL |
| 80 | details.append(ValidationErrorItem(field=field, error_type=error_type)) |
| 81 | |
| 82 | main_message = details[0].message if details else "Validation Error" |
| 83 | |
| 84 | async with AsyncSession(async_engine) as session: # Use AsyncSession |
| 85 | logger_instance = Logger(session, AuthHandler()) |
| 86 | user_id = await get_user_id_from_request(request, logger_instance) |
| 87 | await logger_instance.log_error(user_id, request, main_message) |
| 88 | await session.commit() # Make sure to commit the session |
| 89 | |
| 90 | return JSONResponse( |
| 91 | status_code=422, |
| 92 | content=ValidationErrorResponse(message=main_message, details=details).model_dump(), |
| 93 | ) |
| 94 | |
| 95 | |
| 96 | async def value_error_handler(request: Request, exc: ValueError): |
| 97 | """ |
| 98 | Handles the ValueError exception and logs the error message. |
| 99 | |
| 100 | Args: |
| 101 | request (Request): The incoming request object. |
| 102 | exc (ValueError): The ValueError exception object. |
| 103 | |
| 104 | Returns: |
| 105 | JSONResponse: The response containing the error message. |
| 106 | """ |
| 107 | error_message = str(exc) |
| 108 | |
| 109 | async with AsyncSession(async_engine) as session: |
| 110 | logger_instance = Logger(session, AuthHandler()) |
| 111 | user_id = await get_user_id_from_request(request, logger_instance) |
| 112 | await logger_instance.log_error(user_id, request, error_message) |
| 113 | await session.commit() |
| 114 | |
| 115 | return JSONResponse( |
| 116 | status_code=400, # Bad Request |
| 117 | content={ |
| 118 | "success": False, |
| 119 | "message": error_message, |
| 120 | }, |
| 121 | ) |