mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 11:25:19 -05:00
feat: Implement centralized error capture and handling with new middleware, services, and API endpoints, and add new admin and statistics API views.
This commit is contained in:
@@ -30,6 +30,7 @@ from apps.api.v1.rides.serializers import (
|
||||
RidePhotoStatsOutputSerializer,
|
||||
RidePhotoUpdateInputSerializer,
|
||||
)
|
||||
from apps.core.utils import capture_and_log
|
||||
from apps.parks.models import Park
|
||||
from apps.rides.models import Ride
|
||||
from apps.rides.models.media import RidePhoto
|
||||
@@ -184,7 +185,7 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
logger.info(f"Created ride photo {photo.id} for ride {ride.name} by user {self.request.user.username}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating ride photo: {e}")
|
||||
capture_and_log(e, 'Create ride photo', source='api', request=self.request)
|
||||
raise ValidationError(f"Failed to create photo: {str(e)}") from None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
@@ -203,14 +204,14 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
if "is_primary" in serializer.validated_data:
|
||||
del serializer.validated_data["is_primary"]
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting primary photo: {e}")
|
||||
capture_and_log(e, 'Set primary photo', source='api', request=self.request)
|
||||
raise ValidationError(f"Failed to set primary photo: {str(e)}") from None
|
||||
|
||||
try:
|
||||
serializer.save()
|
||||
logger.info(f"Updated ride photo {instance.id} by user {self.request.user.username}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating ride photo: {e}")
|
||||
capture_and_log(e, 'Update ride photo', source='api', request=self.request)
|
||||
raise ValidationError(f"Failed to update photo: {str(e)}") from None
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
@@ -229,14 +230,14 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
service.delete_image(instance.image)
|
||||
logger.info(f"Successfully deleted ride photo from Cloudflare: {instance.image.cloudflare_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete ride photo from Cloudflare: {str(e)}")
|
||||
capture_and_log(e, 'Delete ride photo from Cloudflare', source='api', request=self.request, severity='low')
|
||||
# Continue with database deletion even if Cloudflare deletion fails
|
||||
|
||||
RideMediaService.delete_photo(instance, deleted_by=self.request.user)
|
||||
|
||||
logger.info(f"Deleted ride photo {instance.id} by user {self.request.user.username}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting ride photo: {e}")
|
||||
capture_and_log(e, 'Delete ride photo', source='api', request=self.request)
|
||||
raise ValidationError(f"Failed to delete photo: {str(e)}") from None
|
||||
|
||||
@extend_schema(
|
||||
@@ -281,7 +282,7 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting primary photo: {e}")
|
||||
capture_and_log(e, 'Set primary photo', source='api', request=request)
|
||||
return Response(
|
||||
{"detail": f"Failed to set primary photo: {str(e)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -339,7 +340,7 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in bulk photo approval: {e}")
|
||||
capture_and_log(e, 'Bulk photo approval', source='api', request=request)
|
||||
return Response(
|
||||
{"detail": f"Failed to update photos: {str(e)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -387,7 +388,7 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting ride photo stats: {e}")
|
||||
capture_and_log(e, 'Get ride photo stats', source='api', request=request)
|
||||
return Response(
|
||||
{"detail": f"Failed to get photo statistics: {str(e)}"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -491,7 +492,7 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
)
|
||||
|
||||
except Exception as api_error:
|
||||
logger.error(f"Error fetching image from Cloudflare API: {str(api_error)}", exc_info=True)
|
||||
capture_and_log(api_error, 'Fetch image from Cloudflare API', source='api', request=request)
|
||||
return Response(
|
||||
{"detail": f"Failed to fetch image from Cloudflare: {str(api_error)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -514,14 +515,14 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
try:
|
||||
RideMediaService.set_primary_photo(ride=ride, photo=photo)
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting primary photo: {e}")
|
||||
capture_and_log(e, 'Set primary photo for saved image', source='api', request=request, severity='low')
|
||||
# Don't fail the entire operation, just log the error
|
||||
|
||||
serializer = RidePhotoOutputSerializer(photo, context={"request": request})
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving ride photo: {e}")
|
||||
capture_and_log(e, 'Save ride photo', source='api', request=request)
|
||||
return Response(
|
||||
{"detail": f"Failed to save photo: {str(e)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
|
||||
@@ -31,6 +31,7 @@ from apps.api.v1.serializers.ride_reviews import (
|
||||
RideReviewStatsOutputSerializer,
|
||||
RideReviewUpdateInputSerializer,
|
||||
)
|
||||
from apps.core.utils import capture_and_log
|
||||
from apps.parks.models import Park
|
||||
from apps.rides.models import Ride
|
||||
from apps.rides.models.reviews import RideReview
|
||||
@@ -181,7 +182,7 @@ class RideReviewViewSet(ModelViewSet):
|
||||
logger.info(f"Created ride review {review.id} for ride {ride.name} by user {self.request.user.username}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating ride review: {e}")
|
||||
capture_and_log(e, 'Create ride review', source='api', request=self.request)
|
||||
raise ValidationError(f"Failed to create review: {str(e)}") from None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
@@ -196,7 +197,7 @@ class RideReviewViewSet(ModelViewSet):
|
||||
serializer.save()
|
||||
logger.info(f"Updated ride review {instance.id} by user {self.request.user.username}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating ride review: {e}")
|
||||
capture_and_log(e, 'Update ride review', source='api', request=self.request)
|
||||
raise ValidationError(f"Failed to update review: {str(e)}") from None
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
@@ -209,7 +210,7 @@ class RideReviewViewSet(ModelViewSet):
|
||||
logger.info(f"Deleting ride review {instance.id} by user {self.request.user.username}")
|
||||
instance.delete()
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting ride review: {e}")
|
||||
capture_and_log(e, 'Delete ride review', source='api', request=self.request)
|
||||
raise ValidationError(f"Failed to delete review: {str(e)}") from None
|
||||
|
||||
@extend_schema(
|
||||
@@ -283,7 +284,7 @@ class RideReviewViewSet(ModelViewSet):
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting ride review stats: {e}")
|
||||
capture_and_log(e, 'Get ride review stats', source='api', request=request)
|
||||
return Response(
|
||||
{"detail": f"Failed to get review statistics: {str(e)}"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -360,7 +361,7 @@ class RideReviewViewSet(ModelViewSet):
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in bulk review moderation: {e}")
|
||||
capture_and_log(e, 'Bulk review moderation', source='api', request=request)
|
||||
return Response(
|
||||
{"detail": f"Failed to moderate reviews: {str(e)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
|
||||
@@ -32,6 +32,7 @@ from apps.core.exceptions import (
|
||||
ServiceError,
|
||||
ValidationException,
|
||||
)
|
||||
from apps.core.utils import capture_and_log
|
||||
from apps.core.utils.error_handling import ErrorHandler
|
||||
from apps.parks.models import Park, ParkPhoto
|
||||
from apps.parks.services import ParkMediaService
|
||||
@@ -188,7 +189,7 @@ class ParkPhotoViewSet(ModelViewSet):
|
||||
logger.warning(f"Validation error creating park photo: {e}")
|
||||
raise ValidationError(str(e)) from None
|
||||
except ServiceError as e:
|
||||
logger.error(f"Service error creating park photo: {e}")
|
||||
capture_and_log(e, 'Create park photo', source='api')
|
||||
raise ValidationError(f"Failed to create photo: {str(e)}") from None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
@@ -210,7 +211,7 @@ class ParkPhotoViewSet(ModelViewSet):
|
||||
logger.warning(f"Validation error setting primary photo: {e}")
|
||||
raise ValidationError(str(e)) from None
|
||||
except ServiceError as e:
|
||||
logger.error(f"Service error setting primary photo: {e}")
|
||||
capture_and_log(e, 'Set primary park photo', source='api')
|
||||
raise ValidationError(f"Failed to set primary photo: {str(e)}") from None
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
@@ -232,13 +233,13 @@ class ParkPhotoViewSet(ModelViewSet):
|
||||
except ImportError:
|
||||
logger.warning("CloudflareImagesService not available")
|
||||
except ServiceError as e:
|
||||
logger.error(f"Service error deleting from Cloudflare: {str(e)}")
|
||||
capture_and_log(e, 'Delete park photo from Cloudflare', source='api', severity='low')
|
||||
# Continue with database deletion even if Cloudflare deletion fails
|
||||
|
||||
try:
|
||||
ParkMediaService().delete_photo(instance.id, deleted_by=cast(UserModel, self.request.user))
|
||||
except ServiceError as e:
|
||||
logger.error(f"Service error deleting park photo: {e}")
|
||||
capture_and_log(e, 'Delete park photo', source='api')
|
||||
raise ValidationError(f"Failed to delete photo: {str(e)}") from None
|
||||
|
||||
@extend_schema(
|
||||
@@ -539,14 +540,14 @@ class ParkPhotoViewSet(ModelViewSet):
|
||||
try:
|
||||
ParkMediaService().set_primary_photo(park_id=park.id, photo_id=photo.id)
|
||||
except ServiceError as e:
|
||||
logger.error(f"Error setting primary photo: {e}")
|
||||
capture_and_log(e, 'Set primary park photo for saved image', source='api', severity='low')
|
||||
# Don't fail the entire operation, just log the error
|
||||
|
||||
serializer = ParkPhotoOutputSerializer(photo, context={"request": request})
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
except ImportError:
|
||||
logger.error("CloudflareImagesService not available")
|
||||
except ImportError as e:
|
||||
capture_and_log(e, 'Cloudflare service import', source='api')
|
||||
return ErrorHandler.handle_api_error(
|
||||
ServiceError("Cloudflare Images service not available"),
|
||||
user_message="Image upload service not available",
|
||||
|
||||
Reference in New Issue
Block a user