|
|
| from fastapi import HTTPException, Request, status
|
| from fastapi.responses import JSONResponse
|
|
|
|
|
| class AppException(HTTPException):
|
| """Base exception — كل الاستثناءات ترث منها."""
|
| def __init__(self, status_code: int, detail: str, code: str = "ERROR"):
|
| super().__init__(status_code=status_code, detail=detail)
|
| self.code = code
|
|
|
|
|
| class NotFoundError(AppException):
|
| def __init__(self, resource: str = "Resource"):
|
| super().__init__(404, f"{resource} not found", "NOT_FOUND")
|
|
|
|
|
| class ConflictError(AppException):
|
| def __init__(self, detail: str = "Resource already exists"):
|
| super().__init__(409, detail, "CONFLICT")
|
|
|
|
|
| class ForbiddenError(AppException):
|
| def __init__(self, detail: str = "Permission denied"):
|
| super().__init__(403, detail, "FORBIDDEN")
|
|
|
|
|
| class UnauthorizedError(AppException):
|
| def __init__(self, detail: str = "Authentication required"):
|
| super().__init__(
|
| 401, detail, "UNAUTHORIZED",
|
|
|
| )
|
| self.headers = {"WWW-Authenticate": "Bearer"}
|
|
|
|
|
|
|
|
|
| async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
|
| return JSONResponse(
|
| status_code=exc.status_code,
|
| content={
|
| "error": exc.code,
|
| "detail": exc.detail,
|
| "path": request.url.path,
|
| },
|
| headers=getattr(exc, "headers", None),
|
| )
|
|
|
|
|
| async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
| return JSONResponse(
|
| status_code=500,
|
| content={
|
| "error": "INTERNAL_ERROR",
|
| "detail": "An unexpected error occurred.",
|
| "path": request.url.path,
|
| },
|
| ) |