import "server-only"; import { createClient } from "@supabase/supabase-js"; import { createServerClient } from "@supabase/ssr"; import { cookies } from "next/headers"; import { cache } from "react"; import type { Database } from "@/shared/database"; import { ServiceError, checkServiceError } from "./service-error"; let singleton: ReturnType> | undefined; export function admin() { if (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.SUPABASE_SECRET_KEY) throw new ServiceError( "MineGeo is not configured yet. Please try again later.", ); return (singleton ??= createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.SUPABASE_SECRET_KEY, { auth: { persistSession: false, autoRefreshToken: false } }, )); } export async function authClient() { const jar = await cookies(); return createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!, { cookies: { getAll: () => jar.getAll(), setAll: (values) => { try { values.forEach(({ name, value, options }) => jar.set(name, value, options), ); } catch { /* Server components refresh through proxy. */ } }, }, }, ); } /** Authentication identity only. Restricted accounts use this solely for appeals/auth. */ export const rawUser = cache(async function rawUser() { if ( !process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY ) return null; const client = await authClient(); const { data: { user }, } = await client.auth.getUser(); return user; }); export const accountState = cache(async function accountState(id: string) { const result = await admin() .from("profiles") .select("account_status") .eq("id", id) .maybeSingle(); checkServiceError(result.error); return result.data?.account_status ?? "active"; }); export async function assertAccountActive(id: string) { const status = await accountState(id); if (status !== "active") { const { restrictionMessage } = await import("@/shared/account-controls"); throw new ServiceError(restrictionMessage(status), 403); } } export const user = cache(async function user() { const account = await rawUser(); if (account) await assertAccountActive(account.id); return account; }); export function required(result: { data: T; error: { message: string; code?: string } | null; }): NonNullable { checkServiceError(result.error); if (result.data === null || result.data === undefined) throw new Error("Record not found"); return result.data as NonNullable; }