import { headers } from "next/headers" import { redirect } from "next/navigation" import { eq } from "drizzle-orm" import { auth } from "@/lib/auth" import { db } from "@/lib/db" import { profiles } from "@/lib/db/schema" /** * Returns the authenticated Better Auth user for the current request, or null. * * Usage in a route / server component: * const user = await getSessionUser() * if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) */ export async function getSessionUser() { const session = await auth.api.getSession({ headers: await headers() }) return session?.user ?? null } export async function getSession() { return auth.api.getSession({ headers: await headers() }) } // ── Admin gating ───────────────────────────────────────────────────────────── // Admins come from the Better Auth `user.role === "admin"` field (set via the // admin plugin / bootstrap env). ADMIN_USER_IDS / ADMIN_EMAILS act as an // env-level fallback so the first admin can be bootstrapped without DB access. const ADMIN_USER_IDS = (process.env.ADMIN_USER_IDS ?? "") .split(",") .map((s) => s.trim()) .filter(Boolean) const ADMIN_EMAILS = (process.env.ADMIN_EMAILS ?? "") .split(",") .map((s) => s.trim().toLowerCase()) .filter(Boolean) export function isAdminUser( u: { id?: string; email?: string; role?: string | null } | null | undefined ): boolean { if (!u) return false if (u.role === "admin") return true if (u.id && ADMIN_USER_IDS.includes(u.id)) return true if (u.email && ADMIN_EMAILS.includes(u.email.toLowerCase())) return true return false } /** * For API routes / server actions: returns `{ user, profile }` if the caller is * an admin, otherwise null (caller returns 401/403). NEVER skip this — admin * queries bypass user_id scoping, so this gate is the only data protection. */ export async function getAdminSession() { const session = await auth.api.getSession({ headers: await headers() }) const user = session?.user ?? null if (!isAdminUser(user as { role?: string | null })) return null const profile = await db.query.profiles.findFirst({ where: eq(profiles.id, user!.id) }) return { user: user!, profile: profile ?? null } } /** * For server components / the (admin) layout: redirects non-admins * (anonymous → /login, logged-in non-admin → /dashboard). */ export async function requireAdmin() { const session = await auth.api.getSession({ headers: await headers() }) const user = session?.user ?? null if (!user) redirect("/login") if (!isAdminUser(user as { role?: string | null })) redirect("/dashboard") const profile = await db.query.profiles.findFirst({ where: eq(profiles.id, user.id) }) return { user, profile: profile ?? null } }