import { redirect } from 'next/navigation'; import { createSupabaseServerClient } from '@/lib/supabase/server'; import { getSupabaseAdmin } from '@/lib/supabase/admin'; import { ClaimForm } from './claim-form'; import { TokenReveal } from './token-reveal'; export const dynamic = 'force-dynamic'; type Tunnel = { subdomain: string; token: string; is_active: boolean; bytes_used: number; quota_bytes: number; last_seen_at: string | null; created_at: string; }; function formatBytes(n: number): string { if (n < 1024) return `${n} B`; const units = ['KiB', 'MiB', 'GiB', 'TiB']; let v = n / 1024; let i = 0; while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; } return `${v.toFixed(2)} ${units[i]}`; } export default async function DashboardPage() { const supabase = createSupabaseServerClient(); const { data: { user }, } = await supabase.auth.getUser(); if (!user) redirect('/login'); // Use service role so RLS doesn't surprise us here either. const admin = getSupabaseAdmin(); const { data: tunnel } = await admin .from('tunnels') .select( 'subdomain, token, is_active, bytes_used, quota_bytes, last_seen_at, created_at', ) .eq('user_id', user.id) .maybeSingle(); return (

Dashboard

Signed in as {user.email}

{tunnel ? (

Your tunnel

Subdomain
{tunnel.subdomain}.linumiq.net
Token
Status
{tunnel.is_active ? 'Active' : 'Inactive'}
Usage
{formatBytes(tunnel.bytes_used)} /{' '} {formatBytes(tunnel.quota_bytes)}
Last seen
{tunnel.last_seen_at ? new Date(tunnel.last_seen_at).toLocaleString() : 'never'}

Setup Home Assistant add-on →

) : (

Claim a subdomain

Pick a subdomain like smith to get{' '} smith.linumiq.net. 3–32 chars, lowercase letters, digits, hyphens. One per account.

)}
); }