mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-22 17:51:08 -05:00
feat: Refactor rides app with unique constraints, mixins, and enhanced documentation
- Added migration to convert unique_together constraints to UniqueConstraint for RideModel. - Introduced RideFormMixin for handling entity suggestions in ride forms. - Created comprehensive code standards documentation outlining formatting, docstring requirements, complexity guidelines, and testing requirements. - Established error handling guidelines with a structured exception hierarchy and best practices for API and view error handling. - Documented view pattern guidelines, emphasizing the use of CBVs, FBVs, and ViewSets with examples. - Implemented a benchmarking script for query performance analysis and optimization. - Developed security documentation detailing measures, configurations, and a security checklist. - Compiled a database optimization guide covering indexing strategies, query optimization patterns, and computed fields.
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
Photo upload and management views for ThrillWiki.
|
||||
|
||||
Security Note:
|
||||
All uploads are validated for file type, size, and content before being saved.
|
||||
Rate limiting is enforced to prevent abuse.
|
||||
"""
|
||||
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.contrib.auth.decorators import login_required
|
||||
@@ -7,6 +15,12 @@ import json
|
||||
import logging
|
||||
|
||||
from .models import Photo
|
||||
from apps.core.utils.file_scanner import (
|
||||
validate_image_upload,
|
||||
FileValidationError,
|
||||
check_upload_rate_limit,
|
||||
increment_upload_count,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,18 +28,37 @@ logger = logging.getLogger(__name__)
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def upload_photo(request):
|
||||
"""Handle photo upload for any model"""
|
||||
"""
|
||||
Handle photo upload for any model.
|
||||
|
||||
Security measures:
|
||||
- Rate limiting to prevent abuse
|
||||
- File type validation (extension, MIME type, magic number)
|
||||
- File size validation
|
||||
- Image integrity validation
|
||||
- Permission checks
|
||||
"""
|
||||
try:
|
||||
# Security: Check rate limiting before processing upload
|
||||
is_allowed, rate_limit_message = check_upload_rate_limit(request.user.id)
|
||||
if not is_allowed:
|
||||
logger.warning(
|
||||
f"User {request.user} exceeded upload rate limit"
|
||||
)
|
||||
return JsonResponse(
|
||||
{"error": rate_limit_message},
|
||||
status=429, # Too Many Requests
|
||||
)
|
||||
|
||||
# Get app label, model, and object ID
|
||||
app_label = request.POST.get("app_label")
|
||||
model = request.POST.get("model")
|
||||
object_id = request.POST.get("object_id")
|
||||
|
||||
# Log received data
|
||||
# Log received data (don't log file contents for security)
|
||||
logger.debug(
|
||||
f"Received upload request - app_label: {app_label}, model: {model}, object_id: {object_id}"
|
||||
)
|
||||
logger.debug(f"Files in request: {request.FILES}")
|
||||
|
||||
# Validate required fields
|
||||
missing_fields = []
|
||||
@@ -44,6 +77,19 @@ def upload_photo(request):
|
||||
status=400,
|
||||
)
|
||||
|
||||
# Security: Validate uploaded file before processing
|
||||
uploaded_file = request.FILES["image"]
|
||||
try:
|
||||
validate_image_upload(uploaded_file)
|
||||
except FileValidationError as e:
|
||||
logger.warning(
|
||||
f"User {request.user} attempted to upload invalid file: {str(e)}"
|
||||
)
|
||||
return JsonResponse(
|
||||
{"error": str(e)},
|
||||
status=400,
|
||||
)
|
||||
|
||||
# Get content type
|
||||
try:
|
||||
content_type = ContentType.objects.get(
|
||||
@@ -87,17 +133,19 @@ def upload_photo(request):
|
||||
|
||||
# Create the photo
|
||||
photo = Photo.objects.create(
|
||||
image=request.FILES["image"],
|
||||
image=uploaded_file,
|
||||
content_type=content_type,
|
||||
object_id=obj.pk,
|
||||
uploaded_by=request.user, # Add the user who uploaded the photo
|
||||
uploaded_by=request.user,
|
||||
is_primary=not Photo.objects.filter(
|
||||
content_type=content_type, object_id=obj.pk
|
||||
).exists(),
|
||||
is_approved=is_approved,
|
||||
# Auto-approve if the user is a moderator, admin, or superuser
|
||||
)
|
||||
|
||||
# Security: Increment upload count for rate limiting
|
||||
increment_upload_count(request.user.id)
|
||||
|
||||
return JsonResponse(
|
||||
{
|
||||
"id": photo.pk,
|
||||
@@ -111,7 +159,7 @@ def upload_photo(request):
|
||||
except Exception as e:
|
||||
logger.error(f"Error in upload_photo: {str(e)}", exc_info=True)
|
||||
return JsonResponse(
|
||||
{"error": f"An error occurred while uploading the photo: {str(e)}"},
|
||||
{"error": "An error occurred while uploading the photo"},
|
||||
status=400,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user