128 lines
3.8 KiB
Python
128 lines
3.8 KiB
Python
from fastapi import Request, status, HTTPException
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.exceptions import RequestValidationError
|
|
from sqlalchemy.exc import IntegrityError
|
|
from jose.exceptions import JWTError
|
|
import os
|
|
import traceback
|
|
|
|
|
|
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
|
"""
|
|
Handle validation errors
|
|
"""
|
|
errors = []
|
|
for error in exc.errors():
|
|
field = ".".join(str(loc) for loc in error["loc"] if loc != "body")
|
|
errors.append({
|
|
"field": field,
|
|
"message": error["msg"]
|
|
})
|
|
|
|
# Get the first error message for the main message
|
|
first_error = errors[0]["message"] if errors else "Validation error"
|
|
|
|
return JSONResponse(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
content={
|
|
"status": "error",
|
|
"message": first_error,
|
|
"errors": errors
|
|
}
|
|
)
|
|
|
|
|
|
async def integrity_error_handler(request: Request, exc: IntegrityError):
|
|
"""
|
|
Handle database integrity errors (unique constraints, etc.)
|
|
"""
|
|
error_msg = str(exc.orig) if hasattr(exc, 'orig') else str(exc)
|
|
|
|
# Check for duplicate entry
|
|
if "Duplicate entry" in error_msg or "UNIQUE constraint" in error_msg:
|
|
return JSONResponse(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
content={
|
|
"status": "error",
|
|
"message": "Duplicate entry",
|
|
"errors": [{"message": "This record already exists"}]
|
|
}
|
|
)
|
|
|
|
return JSONResponse(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
content={
|
|
"status": "error",
|
|
"message": "Database integrity error"
|
|
}
|
|
)
|
|
|
|
|
|
async def jwt_error_handler(request: Request, exc: JWTError):
|
|
"""
|
|
Handle JWT errors
|
|
"""
|
|
return JSONResponse(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
content={
|
|
"status": "error",
|
|
"message": "Invalid token"
|
|
}
|
|
)
|
|
|
|
|
|
async def http_exception_handler(request: Request, exc: HTTPException):
|
|
"""
|
|
Handle HTTPException errors
|
|
"""
|
|
# If detail is already a dict with status/message, return it directly
|
|
if isinstance(exc.detail, dict):
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content=exc.detail
|
|
)
|
|
|
|
# Otherwise format as standard error response
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content={
|
|
"status": "error",
|
|
"message": str(exc.detail) if exc.detail else "An error occurred"
|
|
}
|
|
)
|
|
|
|
|
|
async def general_exception_handler(request: Request, exc: Exception):
|
|
"""
|
|
Handle all other exceptions
|
|
"""
|
|
# Log error
|
|
print(f"Error: {exc}")
|
|
if os.getenv("NODE_ENV") == "development":
|
|
traceback.print_exc()
|
|
|
|
# Handle HTTPException with dict detail
|
|
if isinstance(exc, Exception) and hasattr(exc, "status_code"):
|
|
status_code = exc.status_code
|
|
if hasattr(exc, "detail"):
|
|
detail = exc.detail
|
|
if isinstance(detail, dict):
|
|
# If detail is already a dict with status/message, return it directly
|
|
return JSONResponse(status_code=status_code, content=detail)
|
|
message = str(detail) if detail else "An error occurred"
|
|
else:
|
|
message = str(exc) if str(exc) else "Internal server error"
|
|
else:
|
|
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
message = str(exc) if str(exc) else "Internal server error"
|
|
|
|
return JSONResponse(
|
|
status_code=status_code,
|
|
content={
|
|
"status": "error",
|
|
"message": message,
|
|
**({"stack": traceback.format_exc()} if os.getenv("NODE_ENV") == "development" else {})
|
|
}
|
|
)
|
|
|