mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 07:45:18 -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:
@@ -31,6 +31,7 @@ from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.core.utils import capture_and_log, capture_errors
|
||||
from apps.rides.models import Ride, RidePhoto
|
||||
from apps.rides.services.media_service import RideMediaService
|
||||
|
||||
@@ -39,6 +40,7 @@ UserModel = get_user_model()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
list=extend_schema(
|
||||
summary="List ride photos",
|
||||
@@ -166,7 +168,7 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
serializer.instance = photo
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating ride photo: {e}")
|
||||
capture_and_log(e, 'Creating ride photo', source='api', severity='high', entity_type='RidePhoto')
|
||||
raise ValidationError(f"Failed to create photo: {str(e)}") from None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
@@ -185,7 +187,7 @@ 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, 'Setting primary photo', source='api', severity='medium', entity_type='RidePhoto')
|
||||
raise ValidationError(f"Failed to set primary photo: {str(e)}") from None
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
@@ -204,12 +206,12 @@ 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', severity='low')
|
||||
# Continue with database deletion even if Cloudflare deletion fails
|
||||
|
||||
RideMediaService.delete_photo(instance, deleted_by=self.request.user) # type: ignore
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting ride photo: {e}")
|
||||
capture_and_log(e, 'Deleting ride photo', source='api', severity='high', entity_type='RidePhoto')
|
||||
raise ValidationError(f"Failed to delete photo: {str(e)}") from None
|
||||
|
||||
@extend_schema(
|
||||
@@ -254,7 +256,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', severity='medium', entity_type='RidePhoto')
|
||||
return Response(
|
||||
{"detail": f"Failed to set primary photo: {str(e)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -308,7 +310,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', severity='medium', entity_type='RidePhoto')
|
||||
return Response(
|
||||
{"detail": f"Failed to update photos: {str(e)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -356,7 +358,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, 'Getting ride photo stats', source='api', severity='low', entity_type='RidePhoto')
|
||||
return Response(
|
||||
{"detail": f"Failed to get photo statistics: {str(e)}"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -392,7 +394,7 @@ class RidePhotoViewSet(ModelViewSet):
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in set_primary_photo: {str(e)}", exc_info=True)
|
||||
capture_and_log(e, 'Set primary photo', source='api')
|
||||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@extend_schema(
|
||||
@@ -486,7 +488,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')
|
||||
return Response(
|
||||
{"detail": f"Failed to fetch image from Cloudflare: {str(api_error)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
@@ -509,14 +511,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', 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')
|
||||
return Response(
|
||||
{"detail": f"Failed to save photo: {str(e)}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
|
||||
@@ -46,6 +46,7 @@ from apps.api.v1.serializers.rides import (
|
||||
RideUpdateInputSerializer,
|
||||
)
|
||||
from apps.core.decorators.cache_decorators import cache_api_response
|
||||
from apps.core.utils import capture_and_log
|
||||
from apps.rides.services.hybrid_loader import SmartRideLoader
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -2059,7 +2060,7 @@ class HybridRideAPIView(APIView):
|
||||
return Response(response_data, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in HybridRideAPIView: {e}")
|
||||
capture_and_log(e, 'Get hybrid rides', source='api')
|
||||
return Response(
|
||||
{"detail": "Internal server error"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@@ -2358,7 +2359,7 @@ class RideFilterMetadataAPIView(APIView):
|
||||
return Response(metadata, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in RideFilterMetadataAPIView: {e}")
|
||||
capture_and_log(e, 'Get ride filter metadata', source='api')
|
||||
return Response(
|
||||
{"detail": "Internal server error"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
|
||||
Reference in New Issue
Block a user