# Phase 2: Complete Supabase Removal Plan ## Current Status ### ✅ Completed - Reports Integration All three components now use Django API for reports operations: - `ReportButton.tsx` - Uses `reportsService.submitReport()` - `useModerationStats.ts` - Uses `reportsService.getStatistics()` for report stats - `ReportsQueue.tsx` - Uses `reportsService.listReports()` and `reportsService.updateReportStatus()` ### ⚠️ Remaining Supabase Dependencies #### ReportsQueue.tsx 1. **Line ~195-210: Reporter Profile Fetching** ```typescript await supabase.rpc('get_users_with_emails') // OR await supabase.from('profiles').select(...) ``` **Django Endpoint Exists:** ✅ `GET /api/v1/users/` (batch fetch) 2. **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) 3. **Lines ~385-401: Audit Logging** ```typescript await supabase.rpc('log_admin_action', {...}) ``` **Django Endpoint:** ❌ MISSING - needs to be created #### useModerationStats.ts 1. **Lines ~81-84: Content Submissions Stats** ```typescript await supabase.from('content_submissions') .select('id', { count: 'exact', head: true }) .eq('status', 'pending') ``` **Django Endpoint:** ⚠️ EXISTS but needs stats endpoint 2. **Lines ~86-89: Flagged Reviews Stats** ```typescript await supabase.from('reviews') .select('id', { count: 'exact', head: true }) .eq('moderation_status', 'flagged') ``` **Django Endpoint:** ⚠️ EXISTS but needs count/stats endpoint 3. **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 user - `GET /api/v1/me` - Get current user profile #### Reviews (`/api/v1/reviews/`) - `GET /api/v1/reviews/` - List reviews with filters - `GET /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 pagination - `GET /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:** ```json { "pending_submissions": 5, "reviewing_submissions": 2, "flagged_reviews": 3 } ``` #### 5. Audit Logging Endpoint **Needed:** `POST /api/v1/audit/log` **Request:** ```json { "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`** ```python @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`** ```python @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`** ```python @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`** ```python @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)** ```python 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 fetch - `getCurrentUser()` #### 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 fetch - `getReviewsByEntity(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 fetch - `getSubmissionStats()` #### 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:** 1. Replace reporter profile fetching: ```typescript // 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 : []; ``` 2. Replace related content fetching: ```typescript // 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 : []; ``` 3. Replace audit logging: ```typescript // BEFORE await supabase.rpc('log_admin_action', {...}); // AFTER import { auditService } from '@/services/audit'; await auditService.logAdminAction(action, targetUserId, details); ``` 4. Remove Supabase import #### Update useModerationStats.ts **Changes:** 1. Replace submission stats: ```typescript // 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; ``` 2. Replace flagged reviews stats: ```typescript // 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; ``` 3. Remove realtime subscriptions, rely on polling only 4. Remove Supabase import ### Phase 2D: Testing (~2 hours) 1. **Unit Tests** - Test each service layer method - Test data mappers - Test error handling 2. **Integration Tests** - Test component integration - Test data flow - Test error scenarios 3. **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: 1. **Keep individual fetches** - Less efficient but simpler 2. **Use existing filter parameters** - e.g., filter by entity_id instead of batch IDs 3. **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? 1. Full implementation with batch endpoints 2. Simplified without batch endpoints 3. Phased approach (recommended for incremental progress)