feat: Implement a new notifications application, add admin API views for dashboard metrics, introduce scheduled tasks, and update API routing and project configurations.

This commit is contained in:
pacnpal
2026-01-05 09:50:00 -05:00
parent 1c6e219662
commit a801813dcf
27 changed files with 3829 additions and 131 deletions

View File

@@ -1,6 +1,7 @@
import logging
import requests
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
@@ -30,3 +31,109 @@ class GenerateUploadURLView(APIView):
except Exception as e:
capture_and_log(e, 'Generate upload URL - unexpected error', source='api')
return Response({"detail": "An unexpected error occurred."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class DeleteImageView(APIView):
"""
POST /images/delete/
Delete an image from Cloudflare Images.
"""
permission_classes = [IsAuthenticated]
def post(self, request):
image_id = request.data.get("image_id")
if not image_id:
return Response(
{"detail": "image_id is required"},
status=status.HTTP_400_BAD_REQUEST,
)
try:
# Get Cloudflare credentials
account_id = getattr(settings, "CLOUDFLARE_IMAGES_ACCOUNT_ID", None)
api_token = getattr(settings, "CLOUDFLARE_IMAGES_API_TOKEN", None)
if not account_id or not api_token:
logger.warning("Cloudflare Images not configured, mock deleting image")
return Response({"success": True, "mock": True})
# Delete from Cloudflare
url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1/{image_id}"
response = requests.delete(
url,
headers={"Authorization": f"Bearer {api_token}"},
timeout=10,
)
if response.status_code in (200, 404): # 404 = already deleted
return Response({"success": True})
else:
logger.error(f"Cloudflare delete failed: {response.text}")
return Response(
{"detail": "Failed to delete image"},
status=status.HTTP_502_BAD_GATEWAY,
)
except requests.RequestException as e:
capture_and_log(e, "Delete image - Cloudflare API error", source="api")
return Response(
{"detail": "Failed to delete image"},
status=status.HTTP_502_BAD_GATEWAY,
)
except Exception as e:
capture_and_log(e, "Delete image - unexpected error", source="api")
return Response(
{"detail": "An unexpected error occurred"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
class GenerateOGImageView(APIView):
"""
POST /images/og-image/
Generate an Open Graph image for social sharing.
"""
permission_classes = [] # Public endpoint
def post(self, request):
title = request.data.get("title", "")
description = request.data.get("description", "")
entity_type = request.data.get("entity_type", "")
image_url = request.data.get("image_url", "")
if not title:
return Response(
{"detail": "title is required"},
status=status.HTTP_400_BAD_REQUEST,
)
try:
# This is a placeholder for OG image generation
# In production, you would:
# 1. Use an image generation service (Cloudinary, imgix, etc.)
# 2. Or use a headless browser service (Puppeteer, Playwright)
# 3. Or use a dedicated OG image service
# For now, return a template URL or placeholder
base_url = getattr(settings, "SITE_URL", "https://thrillwiki.com")
og_image_url = f"{base_url}/api/v1/images/og-preview/?title={title[:100]}"
return Response({
"success": True,
"og_image_url": og_image_url,
"title": title,
"description": description[:200] if description else "",
"entity_type": entity_type,
"note": "Placeholder - configure OG image service for production",
})
except Exception as e:
capture_and_log(e, "Generate OG image", source="api")
return Response(
{"detail": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)