|
| 1 | +// Admin API: Get review history (approved/rejected engines) |
| 2 | +// GET /api/admin/history |
| 3 | + |
| 4 | +import type { Env } from '../../types'; |
| 5 | +import { getAuthUser, isAdminEmail, json, errorResponse } from '../../utils'; |
| 6 | + |
| 7 | +interface EngineRow { |
| 8 | + id: string; |
| 9 | + owner_id: string; |
| 10 | + definition: string; |
| 11 | + visibility: string; |
| 12 | + submitted_for_review_at: string; |
| 13 | + reviewed_at: string; |
| 14 | + reviewed_by: string; |
| 15 | + rejection_reason: string | null; |
| 16 | + use_count: number; |
| 17 | + created_at: string; |
| 18 | + updated_at: string; |
| 19 | + user_email: string; |
| 20 | + user_display_name: string | null; |
| 21 | + reviewer_email: string | null; |
| 22 | + reviewer_display_name: string | null; |
| 23 | +} |
| 24 | + |
| 25 | +export const onRequestGet: PagesFunction<Env> = async (context) => { |
| 26 | + const { request, env } = context; |
| 27 | + |
| 28 | + try { |
| 29 | + // Verify admin authentication |
| 30 | + const session = await getAuthUser(request, env); |
| 31 | + if (!session) { |
| 32 | + return errorResponse('Unauthorized', 401); |
| 33 | + } |
| 34 | + |
| 35 | + if (!isAdminEmail(session.email, env)) { |
| 36 | + return errorResponse('Forbidden - Admin access required', 403); |
| 37 | + } |
| 38 | + |
| 39 | + // Get query params for filtering |
| 40 | + const url = new URL(request.url); |
| 41 | + const filter = url.searchParams.get('filter') || 'all'; // 'all', 'approved', 'rejected' |
| 42 | + const limit = Math.min(parseInt(url.searchParams.get('limit') || '50'), 100); |
| 43 | + |
| 44 | + // Build visibility filter |
| 45 | + let visibilityFilter = "e.visibility IN ('public', 'private')"; |
| 46 | + if (filter === 'approved') { |
| 47 | + visibilityFilter = "e.visibility = 'public'"; |
| 48 | + } else if (filter === 'rejected') { |
| 49 | + visibilityFilter = "e.visibility = 'private'"; |
| 50 | + } |
| 51 | + |
| 52 | + // Get reviewed engines with user and reviewer info |
| 53 | + const result = await env.DB.prepare(` |
| 54 | + SELECT |
| 55 | + e.id, |
| 56 | + e.owner_id, |
| 57 | + e.definition, |
| 58 | + e.visibility, |
| 59 | + e.submitted_for_review_at, |
| 60 | + e.reviewed_at, |
| 61 | + e.reviewed_by, |
| 62 | + e.rejection_reason, |
| 63 | + e.use_count, |
| 64 | + e.created_at, |
| 65 | + e.updated_at, |
| 66 | + p.email as user_email, |
| 67 | + p.display_name as user_display_name, |
| 68 | + r.email as reviewer_email, |
| 69 | + r.display_name as reviewer_display_name |
| 70 | + FROM engines e |
| 71 | + JOIN profiles p ON e.owner_id = p.id |
| 72 | + LEFT JOIN profiles r ON e.reviewed_by = r.id |
| 73 | + WHERE e.reviewed_at IS NOT NULL |
| 74 | + AND ${visibilityFilter} |
| 75 | + ORDER BY e.reviewed_at DESC |
| 76 | + LIMIT ? |
| 77 | + `).bind(limit).all(); |
| 78 | + |
| 79 | + const engines = (result.results as unknown as EngineRow[]).map((row) => ({ |
| 80 | + id: row.id, |
| 81 | + ownerId: row.owner_id, |
| 82 | + definition: JSON.parse(row.definition), |
| 83 | + visibility: row.visibility, |
| 84 | + submittedAt: row.submitted_for_review_at, |
| 85 | + reviewedAt: row.reviewed_at, |
| 86 | + rejectionReason: row.rejection_reason, |
| 87 | + useCount: row.use_count, |
| 88 | + createdAt: row.created_at, |
| 89 | + updatedAt: row.updated_at, |
| 90 | + user: { |
| 91 | + email: row.user_email, |
| 92 | + displayName: row.user_display_name, |
| 93 | + }, |
| 94 | + reviewer: row.reviewer_email ? { |
| 95 | + email: row.reviewer_email, |
| 96 | + displayName: row.reviewer_display_name, |
| 97 | + } : null, |
| 98 | + })); |
| 99 | + |
| 100 | + return json({ engines }); |
| 101 | + } catch (error) { |
| 102 | + console.error('Admin history error:', error); |
| 103 | + return errorResponse('Failed to fetch review history', 500); |
| 104 | + } |
| 105 | +}; |
0 commit comments