12 KiB
Phase 2: Complete Supabase Removal Plan
Current Status
✅ Completed - Reports Integration
All three components now use Django API for reports operations:
ReportButton.tsx- UsesreportsService.submitReport()useModerationStats.ts- UsesreportsService.getStatistics()for report statsReportsQueue.tsx- UsesreportsService.listReports()andreportsService.updateReportStatus()
⚠️ Remaining Supabase Dependencies
ReportsQueue.tsx
-
Line ~195-210: Reporter Profile Fetching
await supabase.rpc('get_users_with_emails') // OR await supabase.from('profiles').select(...)Django Endpoint Exists: ✅
GET /api/v1/users/(batch fetch) -
Lines ~219-258: Related Content Fetching
- Reviews:
supabase.from('reviews').select(...).in('id', reviewIds) - Profiles:
supabase.rpc('get_users_with_emails') - Submissions:
supabase.from('content_submissions').select(...).in('id', submissionIds)
Django Endpoints Exist:
- ✅
GET /api/v1/reviews/(batch fetch with filters) - ✅
GET /api/v1/users/(batch fetch) - ✅
GET /api/v1/moderation/submissions(batch fetch)
- Reviews:
-
Lines ~385-401: Audit Logging
await supabase.rpc('log_admin_action', {...})Django Endpoint: ❌ MISSING - needs to be created
useModerationStats.ts
-
Lines ~81-84: Content Submissions Stats
await supabase.from('content_submissions') .select('id', { count: 'exact', head: true }) .eq('status', 'pending')Django Endpoint: ⚠️ EXISTS but needs stats endpoint
-
Lines ~86-89: Flagged Reviews Stats
await supabase.from('reviews') .select('id', { count: 'exact', head: true }) .eq('moderation_status', 'flagged')Django Endpoint: ⚠️ EXISTS but needs count/stats endpoint
-
Lines ~143-181: Realtime Subscriptions
- Supabase realtime for submissions
- Supabase realtime for reviews Solution: Convert to polling (already have polling code)
Django API Audit
✅ Endpoints That Exist
Users/Auth (/api/v1/auth/)
GET /api/v1/users/- List users with filters (supports batch via search)GET /api/v1/users/{id}- Get single userGET /api/v1/me- Get current user profile
Reviews (/api/v1/reviews/)
GET /api/v1/reviews/- List reviews with filtersGET /api/v1/reviews/{id}- Get single review- Supports filtering by
entity_id,user_id,rating, etc.
Content Submissions (/api/v1/moderation/)
GET /api/v1/moderation/submissions- List submissions with paginationGET /api/v1/moderation/submissions/{id}- Get single submission- Supports filtering by
status
❌ Missing Django Endpoints
1. Batch User Fetch by IDs
Current: GET /api/v1/users/?search={query} (not ideal for batch by IDs)
Needed: GET /api/v1/users/batch?ids=id1,id2,id3
Alternative: Use search with IDs or fetch individually (less efficient)
2. Batch Reviews Fetch by IDs
Current: GET /api/v1/reviews/?entity_id={id} (filters by entity)
Needed: GET /api/v1/reviews/batch?ids=id1,id2,id3
Alternative: Fetch individually or filter by entity_id if all same entity
3. Batch Submissions Fetch by IDs
Current: GET /api/v1/moderation/submissions (no ID filter)
Needed: GET /api/v1/moderation/submissions/batch?ids=id1,id2,id3
Alternative: Fetch individually
4. Moderation Statistics Endpoint
Needed: GET /api/v1/moderation/stats
Response:
{
"pending_submissions": 5,
"reviewing_submissions": 2,
"flagged_reviews": 3
}
5. Audit Logging Endpoint
Needed: POST /api/v1/audit/log
Request:
{
"action": "report_resolved",
"target_user_id": "uuid",
"details": {}
}
Implementation Plan
Phase 2A: Add Missing Django Endpoints (~2-3 hours)
1. Add Batch Fetch Endpoints
File: django/api/v1/endpoints/auth.py
@router.get("/users/batch", response={200: List[UserProfileOut]})
def batch_get_users(request, ids: str = Query(..., description="Comma-separated user IDs")):
"""Batch fetch users by IDs"""
user_ids = [id.strip() for id in ids.split(',')]
users = User.objects.filter(id__in=user_ids)
return list(users)
File: django/api/v1/endpoints/reviews.py
@router.get("/batch", response={200: List[ReviewOut]})
def batch_get_reviews(request, ids: str = Query(..., description="Comma-separated review IDs")):
"""Batch fetch reviews by IDs"""
review_ids = [id.strip() for id in ids.split(',')]
reviews = Review.objects.filter(id__in=review_ids).select_related('user')
return [_serialize_review(review) for review in reviews]
File: django/api/v1/endpoints/moderation.py
@router.get('/submissions/batch', response={200: List[ContentSubmissionOut]})
def batch_get_submissions(request, ids: str = Query(..., description="Comma-separated submission IDs")):
"""Batch fetch submissions by IDs"""
submission_ids = [id.strip() for id in ids.split(',')]
submissions = ContentSubmission.objects.filter(id__in=submission_ids)
return [_submission_to_dict(sub) for sub in submissions]
2. Add Moderation Statistics Endpoint
File: django/api/v1/endpoints/moderation.py
@router.get('/stats', response={200: ModerationStatsOut})
def get_moderation_stats(request):
"""Get moderation queue statistics"""
from django.db.models import Count, Q
from apps.reviews.models import Review
pending_submissions = ContentSubmission.objects.filter(
status='pending'
).count()
reviewing_submissions = ContentSubmission.objects.filter(
status='reviewing'
).count()
flagged_reviews = Review.objects.filter(
moderation_status='flagged'
).count()
return {
'pending_submissions': pending_submissions,
'reviewing_submissions': reviewing_submissions,
'flagged_reviews': flagged_reviews
}
3. Add Audit Logging Endpoint
File: django/api/v1/endpoints/audit.py (NEW)
from ninja import Router
from apps.users.permissions import jwt_auth, require_auth
router = Router(tags=['Audit'])
@router.post("/log", auth=jwt_auth, response={201: MessageSchema})
@require_auth
def log_admin_action(request, data: AdminActionLog):
"""Log an admin action for audit trail"""
# TODO: Implement audit logging
# Could use django-auditlog or custom model
return 201, {"message": "Action logged", "success": True}
Phase 2B: Create Frontend Service Layers (~3-4 hours)
1. Users Service (src/services/users/)
Structure:
src/services/users/
├── types.ts # User interfaces
├── mappers.ts # Data transformation
├── usersService.ts # API client
└── index.ts # Exports
Key Methods:
getUserProfile(userId: string)getUserProfiles(userIds: string[])- batch fetchgetCurrentUser()
2. Reviews Service (src/services/reviews/)
Structure:
src/services/reviews/
├── types.ts
├── mappers.ts
├── reviewsService.ts
└── index.ts
Key Methods:
getReview(reviewId: string)getReviews(reviewIds: string[])- batch fetchgetReviewsByEntity(entityType, entityId)
3. Submissions Service (src/services/submissions/)
Structure:
src/services/submissions/
├── types.ts
├── mappers.ts
├── submissionsService.ts
└── index.ts
Key Methods:
getSubmission(submissionId: string)getSubmissions(submissionIds: string[])- batch fetchgetSubmissionStats()
4. Audit Service (src/services/audit/)
Structure:
src/services/audit/
├── types.ts
├── auditService.ts
└── index.ts
Key Methods:
logAdminAction(action, targetUserId, details)
Phase 2C: Update Components (~2-3 hours)
Update ReportsQueue.tsx
Changes:
- Replace reporter profile fetching:
// BEFORE
const { data: allProfiles } = await supabase.rpc('get_users_with_emails');
// AFTER
import { usersService } from '@/services/users';
const result = await usersService.getUserProfiles(reporterIds);
const profiles = result.success ? result.data : [];
- Replace related content fetching:
// BEFORE
const { data: reviewsData } = await supabase.from('reviews')
.select('id, title, content, rating')
.in('id', reviewIds);
// AFTER
import { reviewsService } from '@/services/reviews';
const result = await reviewsService.getReviews(reviewIds);
const reviewsData = result.success ? result.data : [];
- Replace audit logging:
// BEFORE
await supabase.rpc('log_admin_action', {...});
// AFTER
import { auditService } from '@/services/audit';
await auditService.logAdminAction(action, targetUserId, details);
- Remove Supabase import
Update useModerationStats.ts
Changes:
- Replace submission stats:
// BEFORE
const { count } = await supabase.from('content_submissions')
.select('*', { count: 'exact', head: true })
.eq('status', 'pending');
// AFTER
import { submissionsService } from '@/services/submissions';
const result = await submissionsService.getStats();
const pendingSubmissions = result.success ? result.data.pending_submissions : 0;
- Replace flagged reviews stats:
// BEFORE
const { count } = await supabase.from('reviews')
.select('*', { count: 'exact', head: true })
.eq('moderation_status', 'flagged');
// AFTER
// Use moderation stats endpoint from submissions service
const flaggedContent = result.success ? result.data.flagged_reviews : 0;
-
Remove realtime subscriptions, rely on polling only
-
Remove Supabase import
Phase 2D: Testing (~2 hours)
-
Unit Tests
- Test each service layer method
- Test data mappers
- Test error handling
-
Integration Tests
- Test component integration
- Test data flow
- Test error scenarios
-
E2E Tests
- Test complete report flow
- Test stats updates
- Test audit logging
Estimated Timeline
| Phase | Task | Time | Dependencies |
|---|---|---|---|
| 2A | Add Django endpoints | 2-3 hrs | None |
| 2B | Create service layers | 3-4 hrs | Phase 2A |
| 2C | Update components | 2-3 hrs | Phase 2B |
| 2D | Testing | 2 hrs | Phase 2C |
| Total | 9-12 hrs |
Alternative: Simplified Approach
If full batch endpoints are too much work, we can:
- Keep individual fetches - Less efficient but simpler
- Use existing filter parameters - e.g., filter by entity_id instead of batch IDs
- Skip audit logging - Remove audit log calls entirely for now
This would reduce Phase 2A to just adding the moderation stats endpoint (~30 min).
Recommendation
Option 1: Full Implementation (9-12 hours)
- Complete Supabase removal
- Optimal performance with batch endpoints
- Full feature parity
Option 2: Simplified (4-6 hours)
- Skip batch endpoints, fetch individually
- Add only stats endpoint
- Remove audit logging calls
- Still removes all Supabase dependencies
Option 3: Phased Approach
- Phase 2A: Stats endpoint only (30 min)
- Phase 2B: Service layers with individual fetches (2-3 hrs)
- Phase 2C: Update components (2-3 hrs)
- Phase 2D: Testing (2 hrs)
- Total: 6.5-8.5 hours
- Later: Add batch endpoints for optimization
Decision Point
Which approach would you like to proceed with?
- Full implementation with batch endpoints
- Simplified without batch endpoints
- Phased approach (recommended for incremental progress)