mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-25 01:31:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
401
PHASE_2_COMPLETE_SUPABASE_REMOVAL_PLAN.md
Normal file
401
PHASE_2_COMPLETE_SUPABASE_REMOVAL_PLAN.md
Normal file
@@ -0,0 +1,401 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user