import crypto from "node:crypto"; import { cookies } from "next/headers"; import { eq } from "drizzle-orm"; import { db } from "@/lib/db/client"; import { sessions, users } from "@/lib/db/schema"; const SESSION_COOKIE = "session"; const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days function hashToken(token: string): string { return crypto.createHash("sha256").update(token).digest("hex"); } export async function createSession(userId: string): Promise { const token = crypto.randomBytes(32).toString("base64url"); const tokenHash = hashToken(token); const expiresAt = new Date(Date.now() + SESSION_TTL_MS); await db.insert(sessions).values({ tokenHash, userId, expiresAt }); return token; } export async function validateSessionToken(token: string) { const tokenHash = hashToken(token); const rows = await db .select({ session: sessions, user: users }) .from(sessions) .innerJoin(users, eq(sessions.userId, users.id)) .where(eq(sessions.tokenHash, tokenHash)) .limit(1); const row = rows[0]; if (!row) return null; if (row.session.expiresAt.getTime() < Date.now()) { await db.delete(sessions).where(eq(sessions.tokenHash, tokenHash)); return null; } return row; } export async function destroySessionToken(token: string): Promise { await db.delete(sessions).where(eq(sessions.tokenHash, hashToken(token))); } export async function setSessionCookie(token: string): Promise { const cookieStore = await cookies(); cookieStore.set(SESSION_COOKIE, token, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", path: "/", maxAge: SESSION_TTL_MS / 1000, }); } export async function clearSessionCookie(): Promise { const cookieStore = await cookies(); cookieStore.delete(SESSION_COOKIE); } export async function getSessionToken(): Promise { const cookieStore = await cookies(); return cookieStore.get(SESSION_COOKIE)?.value; } /** Reads the session cookie and validates it against the DB. Returns null if absent/invalid/expired. */ export async function getCurrentSession() { const token = await getSessionToken(); if (!token) return null; return validateSessionToken(token); } export function getSessionCookieName(): string { return SESSION_COOKIE; }