import crypto from "node:crypto"; import { cookies } from "next/headers"; import { redirect } from "next/navigation"; import { cache } from "react"; import { prisma } from "./prisma"; import { encryptSessionId, decryptSessionId } from "./crypto"; /** * Create a new session for the given user. * Sets an encrypted session cookie with a 7-day expiry. * Returns the encrypted cookie value. */ export async function createSession(userId: string): Promise { const token = crypto.randomBytes(32).toString("hex"); const session = await prisma.session.create({ data: { sessionToken: token, userId, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), }, }); const encryptedValue = encryptSessionId(session.id); const cookieStore = await cookies(); cookieStore.set("session", encryptedValue, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", path: "/", maxAge: 7 * 24 * 60 * 60, }); return encryptedValue; } /** * Delete the current session cookie and its database record. */ export async function deleteSession(): Promise { const cookieStore = await cookies(); const sessionCookie = cookieStore.get("session")?.value; if (sessionCookie) { const sessionId = decryptSessionId(sessionCookie); if (sessionId) { await prisma.session.delete({ where: { id: sessionId } }).catch(() => {}); } } cookieStore.delete("session"); } /** * Get the current authenticated user from the session cookie. * Memoized per-request with React cache(). */ export const getCurrentUser = cache( async (): Promise<{ id: string; email: string } | null> => { const cookieStore = await cookies(); const sessionCookie = cookieStore.get("session")?.value; if (!sessionCookie) return null; const sessionId = decryptSessionId(sessionCookie); if (!sessionId) return null; const session = await prisma.session.findUnique({ where: { id: sessionId }, include: { user: true }, }); if (!session) return null; if (session.expiresAt < new Date()) return null; return { id: session.user.id, email: session.user.email }; }, ); /** * Get the current user or redirect to /login if unauthenticated. */ export async function requireCurrentUser(): Promise<{ id: string; email: string; }> { const user = await getCurrentUser(); if (!user) redirect("/login"); return user; }