import { NextRequest, NextResponse } from "next/server"; import { createClient } from "@/lib/supabase/server"; // No hardcoded emails — admin access is determined by profiles.role in the database async function checkAdmin() { const supabase = await createClient(); const { data: { user } } = await supabase.auth.getUser(); if (!user) return { supabase: null, user: null, error: true }; // Check role from database const { data: profile } = await supabase .from("profiles") .select("role") .eq("id", user.id) .single(); if (profile?.role !== "admin") { return { supabase: null, user: null, error: true }; } return { supabase, user, error: false }; } // GET — admin stats + data export async function GET(req: NextRequest) { const { supabase, user, error } = await checkAdmin(); if (error || !supabase || !user) return NextResponse.json({ error: "Forbidden" }, { status: 403 }); const url = new URL(req.url); const action = url.searchParams.get("action"); switch (action) { case "stats": { const { count: totalUsers } = await supabase.from("profiles").select("id", { count: "exact", head: true }); const { count: proUsers } = await supabase.from("profiles").select("id", { count: "exact", head: true }).eq("plan", "pro"); const { count: teamUsers } = await supabase.from("profiles").select("id", { count: "exact", head: true }).eq("plan", "team"); const { count: totalScans } = await supabase.from("analyses").select("id", { count: "exact", head: true }); const { count: totalTeams } = await supabase.from("teams").select("id", { count: "exact", head: true }); const { count: totalApiKeys } = await supabase.from("api_keys").select("id", { count: "exact", head: true }).eq("is_active", true); const { count: bannedUsers } = await supabase.from("profiles").select("id", { count: "exact", head: true }).eq("is_banned", true); const today = new Date(); today.setHours(0, 0, 0, 0); const { count: scansToday } = await supabase.from("analyses").select("id", { count: "exact", head: true }).gte("created_at", today.toISOString()); return NextResponse.json({ total_users: totalUsers || 0, pro_users: proUsers || 0, team_users: teamUsers || 0, free_users: (totalUsers || 0) - (proUsers || 0) - (teamUsers || 0), total_scans: totalScans || 0, scans_today: scansToday || 0, total_teams: totalTeams || 0, total_api_keys: totalApiKeys || 0, banned_users: bannedUsers || 0, }); } case "users": { const limit = parseInt(url.searchParams.get("limit") || "50"); const offset = parseInt(url.searchParams.get("offset") || "0"); const search = url.searchParams.get("search") || ""; let query = supabase.from("profiles") .select("id, email, full_name, plan, role, is_banned, analyses_this_month, team_id, razorpay_subscription_id, created_at", { count: "exact" }) .order("created_at", { ascending: false }) .range(offset, offset + limit - 1); if (search) { query = query.or(`email.ilike.%${search}%,full_name.ilike.%${search}%`); } const { data: users, count } = await query; return NextResponse.json({ users: users || [], total: count || 0 }); } case "scans": { const limit = parseInt(url.searchParams.get("limit") || "50"); const offset = parseInt(url.searchParams.get("offset") || "0"); const { data: scans, count } = await supabase.from("analyses") .select("id, user_id, source_url, risk_score, grade, flagged_count, total_clauses, created_at", { count: "exact" }) .order("created_at", { ascending: false }) .range(offset, offset + limit - 1); return NextResponse.json({ scans: scans || [], total: count || 0 }); } case "teams": { const { data: teams } = await supabase.from("teams").select("*").order("created_at", { ascending: false }); return NextResponse.json({ teams: teams || [] }); } case "api-keys": { const { data: keys } = await supabase.from("api_keys") .select("id, user_id, name, key_prefix, calls_this_month, calls_limit, is_active, created_at") .order("created_at", { ascending: false }); return NextResponse.json({ keys: keys || [] }); } case "logs": { const { data: logs } = await supabase.from("admin_logs") .select("*").order("created_at", { ascending: false }).limit(100); return NextResponse.json({ logs: logs || [] }); } default: return NextResponse.json({ error: "Invalid action" }, { status: 400 }); } } // POST — admin actions export async function POST(req: NextRequest) { const { supabase, user, error } = await checkAdmin(); if (error || !supabase || !user) return NextResponse.json({ error: "Forbidden" }, { status: 403 }); const body = await req.json(); switch (body.action) { case "update_plan": { const { userId, plan } = body; await supabase.from("profiles").update({ plan, updated_at: new Date().toISOString() }).eq("id", userId); await supabase.from("admin_logs").insert({ admin_id: user.id, action: "update_plan", target_type: "user", target_id: userId, details: { plan }, }); return NextResponse.json({ success: true }); } case "ban_user": { const { userId, banned } = body; await supabase.from("profiles").update({ is_banned: banned, updated_at: new Date().toISOString() }).eq("id", userId); await supabase.from("admin_logs").insert({ admin_id: user.id, action: banned ? "ban_user" : "unban_user", target_type: "user", target_id: userId, }); return NextResponse.json({ success: true }); } case "delete_scan": { const { scanId } = body; await supabase.from("analyses").delete().eq("id", scanId); await supabase.from("admin_logs").insert({ admin_id: user.id, action: "delete_scan", target_type: "scan", target_id: scanId, }); return NextResponse.json({ success: true }); } case "revoke_api_key": { const { keyId } = body; await supabase.from("api_keys").update({ is_active: false }).eq("id", keyId); await supabase.from("admin_logs").insert({ admin_id: user.id, action: "revoke_api_key", target_type: "api_key", target_id: keyId, }); return NextResponse.json({ success: true }); } case "delete_team": { const { teamId } = body; await supabase.from("profiles").update({ team_id: null }).eq("team_id", teamId); await supabase.from("teams").delete().eq("id", teamId); await supabase.from("admin_logs").insert({ admin_id: user.id, action: "delete_team", target_type: "team", target_id: teamId, }); return NextResponse.json({ success: true }); } default: return NextResponse.json({ error: "Invalid action" }, { status: 400 }); } }