This commit is contained in:
2026-06-02 10:23:09 +03:00
parent e08d555b23
commit 0c87eaef46
136 changed files with 16069 additions and 94 deletions

91
lib/auth.ts Normal file
View File

@@ -0,0 +1,91 @@
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<string> {
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<void> {
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;
}