import { createHash, randomBytes } from "node:crypto" import { cookies } from "next/headers" import { fetchMutation, fetchQuery } from "convex/nextjs" import { NextResponse } from "next/server" import { api } from "@/convex/_generated/api" import { hasConvexUrl } from "@/lib/convex-config" export const ADMIN_SESSION_COOKIE = "rmv_admin_session" const ADMIN_SESSION_TTL_MS = 1000 * 60 * 60 * 24 * 7 function getProvidedToken(request: Request) { const authHeader = request.headers.get("authorization") || "" const bearerToken = authHeader.startsWith("Bearer ") ? authHeader.slice("Bearer ".length).trim() : "" return request.headers.get("x-admin-token") || bearerToken } export function requireAdminToken(request: Request) { const configuredToken = process.env.ADMIN_API_TOKEN if (!configuredToken) { return NextResponse.json( { error: "Admin API is disabled." }, { status: 503 } ) } const providedToken = getProvidedToken(request) if (providedToken !== configuredToken) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) } return null } export function isAdminUiEnabled() { return process.env.ADMIN_UI_ENABLED === "true" } export function getConfiguredAdminEmail() { return String(process.env.ADMIN_EMAIL || "") .trim() .toLowerCase() } function getConfiguredAdminPassword() { return String(process.env.ADMIN_PASSWORD || "") } function hashAdminSessionToken(token: string) { return createHash("sha256").update(token).digest("hex") } export function isAdminCredentialLoginConfigured() { return Boolean( isAdminUiEnabled() && hasConvexUrl() && getConfiguredAdminEmail() && getConfiguredAdminPassword() ) } export function isAdminCredentialMatch(email: string, password: string) { return ( email.trim().toLowerCase() === getConfiguredAdminEmail() && password === getConfiguredAdminPassword() ) } export async function createAdminSession(email: string) { if (!hasConvexUrl()) { throw new Error("Convex is not configured for admin sessions.") } const normalizedEmail = email.trim().toLowerCase() const rawToken = randomBytes(32).toString("hex") const tokenHash = hashAdminSessionToken(rawToken) const expiresAt = Date.now() + ADMIN_SESSION_TTL_MS await fetchMutation(api.admin.ensureAdminUser, { email: normalizedEmail, name: normalizedEmail.split("@")[0], }) await fetchMutation(api.admin.createSession, { email: normalizedEmail, tokenHash, expiresAt, }) return { token: rawToken, expiresAt, } } export async function destroyAdminSession(rawToken?: string | null) { if (!rawToken || !hasConvexUrl()) { return } try { await fetchMutation(api.admin.destroySession, { tokenHash: hashAdminSessionToken(rawToken), }) } catch (error) { console.error("Failed to destroy admin session:", error) } } export async function validateAdminSession(rawToken?: string | null) { if (!rawToken || !hasConvexUrl()) { return null } try { return await fetchQuery(api.admin.validateSession, { tokenHash: hashAdminSessionToken(rawToken), }) } catch (error) { console.error("Failed to validate admin session:", error) return null } } export async function getAdminUserFromCookies() { if (!isAdminUiEnabled()) { return null } const cookieStore = await cookies() const rawToken = cookieStore.get(ADMIN_SESSION_COOKIE)?.value const session = await validateAdminSession(rawToken) return session?.user || null }