import logging from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ValidationError as DjangoValidationError from django.db import IntegrityError from django.http import JsonResponse from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework import status from rest_framework.exceptions import APIException from rest_framework.response import Response from rest_framework.views import exception_handler as drf_exception_handler logger = logging.getLogger(__name__) def _make_error_response(status_code: int, message, error_code: str) -> Response: return Response( { "success": False, "status_code": status_code, "status_message": message, "details": { "message": message, "error": error_code, "timestamp": timezone.now().isoformat(), }, }, status=status_code, ) def exception_handler(exc, context): logger.exception(exc) response = drf_exception_handler(exc, context) if response is not None: response_data = { "success": False, "status_code": response.status_code, "status_message": str(getattr(exc, "default_detail", exc)), "details": getattr(exc, "detail", None), } if isinstance(exc, APIException): try: error = ( getattr(getattr(exc, "detail", None), "code", None) or getattr(exc, "code", None) or getattr(exc, "default_code", "") ) except Exception: error = "" response_data["details"] = { "message": exc.detail, "error": error, "timestamp": timezone.now().isoformat(), } response.data = response_data return response # ── Django / DB exceptions not caught by DRF ──────────────────────────── if isinstance(exc, ObjectDoesNotExist): return _make_error_response( status.HTTP_404_NOT_FOUND, "مورد درخواستی یافت نشد.", "not_found", ) if isinstance(exc, IntegrityError): return _make_error_response( status.HTTP_409_CONFLICT, "این اطلاعات قبلاً در سیستم ثبت شده است.", "conflict", ) if isinstance(exc, DjangoValidationError): messages = list(exc.messages) if hasattr(exc, "messages") else [str(exc)] detail = messages[0] if len(messages) == 1 else messages return _make_error_response( status.HTTP_400_BAD_REQUEST, detail, "validation_error", ) if isinstance(exc, ValueError): return _make_error_response( status.HTTP_400_BAD_REQUEST, "مقدار ورودی نامعتبر است.", "invalid_value", ) return None class ErrorMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): return self.get_response(request) def process_exception(self, request, exception): logger.exception(exception) return JsonResponse( { "success": False, "status_code": 500, "status_message": "خطای داخلی سرور. لطفاً با پشتیبانی تماس بگیرید.", "details": { "message": "خطای داخلی سرور.", "error": "internal_server_error", }, }, status=500, ) class UnprocessableEntity(APIException): status_code = 422 default_detail = "درخواست معتبر است اما به دلیل خطای منطقی قابل پردازش نیست." default_code = "unprocessable_entity" class ServiceUnavailable(APIException): status_code = status.HTTP_503_SERVICE_UNAVAILABLE default_detail = "سرویس در حال حاضر در دسترس نیست. لطفاً دوباره تلاش کنید." default_code = "service_unavailable" class Conflict(APIException): status_code = status.HTTP_409_CONFLICT default_detail = "تعارض در داده‌ها." default_code = "conflict"