import { NextResponse, type NextRequest } from 'next/server'; import { requireAdminApi } from '@/lib/auth/admin-guard'; import { getSupabaseAdmin } from '@/lib/supabase/admin'; import { logAdminAction } from '@/lib/auth/audit'; import { isUuid } from '@/lib/admin/validators'; export const runtime = 'nodejs'; export const dynamic = 'force-dynamic'; type TunnelRow = { user_id: string; subdomain: string; token: string; is_active: boolean; bytes_used: number; quota_bytes: number; last_seen_at: string | null; created_at: string; }; export async function GET( _req: NextRequest, { params }: { params: { id: string } }, ) { const auth = await requireAdminApi(); if (!auth.ok) return auth.response; const { id } = params; if (!isUuid(id)) { return NextResponse.json({ error: 'invalid user id' }, { status: 400 }); } const admin = getSupabaseAdmin(); const { data: userRes, error: userErr } = await admin.auth.admin.getUserById(id); if (userErr || !userRes.user) { return NextResponse.json({ error: 'user not found' }, { status: 404 }); } const u = userRes.user; const { data: tunnel } = await admin .from('tunnels') .select( 'user_id, subdomain, token, is_active, bytes_used, quota_bytes, last_seen_at, created_at', ) .eq('user_id', id) .maybeSingle(); const { data: audit } = await admin .from('admin_audit_log') .select('id, actor_email, action, target_type, target_id, details, created_at') .eq('target_id', id) .order('created_at', { ascending: false }) .limit(25); return NextResponse.json({ user: { id: u.id, email: u.email ?? null, role: (u.app_metadata?.role as string | undefined) ?? 'user', banned_until: (u as unknown as { banned_until?: string | null }).banned_until ?? null, email_confirmed_at: u.email_confirmed_at ?? null, created_at: u.created_at, last_sign_in_at: u.last_sign_in_at ?? null, }, tunnel: tunnel ? { subdomain: tunnel.subdomain, is_active: tunnel.is_active, bytes_used: tunnel.bytes_used, quota_bytes: tunnel.quota_bytes, last_seen_at: tunnel.last_seen_at, created_at: tunnel.created_at, } : null, audit: audit ?? [], }); } export async function DELETE( _req: NextRequest, { params }: { params: { id: string } }, ) { const auth = await requireAdminApi(); if (!auth.ok) return auth.response; const { id } = params; if (!isUuid(id)) { return NextResponse.json({ error: 'invalid user id' }, { status: 400 }); } if (id === auth.user.id) { return NextResponse.json( { error: 'you cannot delete your own account' }, { status: 400 }, ); } const admin = getSupabaseAdmin(); // Remove the tunnel row first (FK to auth.users). await admin.from('tunnels').delete().eq('user_id', id); const { error } = await admin.auth.admin.deleteUser(id); if (error) { return NextResponse.json({ error: error.message }, { status: 500 }); } await logAdminAction(auth.user, { action: 'user.delete', target_type: 'user', target_id: id, }); return NextResponse.json({ ok: true }); }