import { notFound } from 'next/navigation'; import Link from 'next/link'; import { getSupabaseAdmin } from '@/lib/supabase/admin'; import { createSupabaseServerClient } from '@/lib/supabase/server'; import { isUuid } from '@/lib/admin/validators'; import { formatBytes, formatDate } from '@/lib/format'; import { UserActions } from './user-actions'; export const dynamic = 'force-dynamic'; type TunnelRow = { subdomain: string; is_active: boolean; bytes_used: number; quota_bytes: number; last_seen_at: string | null; created_at: string; }; type AuditRow = { id: number; actor_email: string | null; action: string; target_type: string | null; target_id: string | null; details: Record; created_at: string; }; export default async function AdminUserDetailPage({ params, }: { params: { id: string }; }) { if (!isUuid(params.id)) notFound(); const admin = getSupabaseAdmin(); const supabase = createSupabaseServerClient(); const { data: { user: currentUser }, } = await supabase.auth.getUser(); const { data: userRes, error } = await admin.auth.admin.getUserById( params.id, ); if (error || !userRes.user) notFound(); const u = userRes.user; const role = (u.app_metadata?.role as string | undefined) ?? 'user'; const bannedUntil = (u as unknown as { banned_until?: string | null }).banned_until ?? null; const banned = !!bannedUntil && new Date(bannedUntil).getTime() > Date.now(); const { data: tunnel } = await admin .from('tunnels') .select( 'subdomain, is_active, bytes_used, quota_bytes, last_seen_at, created_at', ) .eq('user_id', params.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', params.id) .order('created_at', { ascending: false }) .limit(25); const isSelf = currentUser?.id === params.id; return (

← Users

{u.email ?? u.id}

Account

User ID
{u.id}
Role
{role === 'admin' ? ( admin ) : ( user )}
Status
{banned ? ( banned ) : u.email_confirmed_at ? ( confirmed ) : ( unconfirmed )}
Created
{formatDate(u.created_at)}
Last sign-in
{formatDate(u.last_sign_in_at)}

Tunnel

{tunnel ? (
Subdomain
{tunnel.subdomain}.linumiq.net
Status
{tunnel.is_active ? 'Active' : 'Inactive'}
Usage
{formatBytes(tunnel.bytes_used)} /{' '} {formatBytes(tunnel.quota_bytes)}
Last seen
{formatDate(tunnel.last_seen_at)}
Created
{formatDate(tunnel.created_at)}
Manage
Go to tunnels →
) : (

No tunnel claimed.

)}

Audit history

{audit && audit.length > 0 ? (
{(audit as AuditRow[]).map((a) => ( ))}
When Action By Details
{formatDate(a.created_at)} {a.action} {a.actor_email ?? '—'} {JSON.stringify(a.details ?? {})}
) : (

No audit entries.

)}
); }