import crypto from "crypto"; import type { NextRequest } from "next/server"; export const ADMIN_COOKIE_NAME = "teich_admin"; const SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 7; function getAdminPassword() { return process.env.ADMIN_PASSWORD || ""; } function sign(payload: string, secret: string) { return crypto.createHmac("sha256", secret).update(payload).digest("hex"); } export function createAdminSessionValue(): string { const secret = getAdminPassword(); const ts = Date.now(); const nonce = crypto.randomBytes(16).toString("hex"); const payload = `${ts}:${nonce}`; const sig = sign(payload, secret); return `${payload}.${sig}`; } export function isAdminSessionValue(value: string | undefined | null): boolean { if (!value) return false; const secret = getAdminPassword(); if (!secret) return false; const lastDot = value.lastIndexOf("."); if (lastDot === -1) return false; const payload = value.slice(0, lastDot); const sig = value.slice(lastDot + 1); const expected = sign(payload, secret); try { if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return false; } catch { return false; } const [tsStr] = payload.split(":"); const ts = Number(tsStr); if (!Number.isFinite(ts)) return false; const ageSeconds = (Date.now() - ts) / 1000; if (ageSeconds < 0 || ageSeconds > SESSION_MAX_AGE_SECONDS) return false; return true; } export function isAdminRequest(request: NextRequest): boolean { const value = request.cookies.get(ADMIN_COOKIE_NAME)?.value; return isAdminSessionValue(value); } export function adminCookieOptions() { return { name: ADMIN_COOKIE_NAME, httpOnly: true, sameSite: "lax" as const, secure: process.env.NODE_ENV === "production", path: "/", maxAge: SESSION_MAX_AGE_SECONDS, }; }