Git history was lost when the previous Gitea instance was wiped and reinstalled due to an unresolved corruption bug — this commit is the last known-good file content, exported before the reinstall. Prior commit history is not recoverable through this path.
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
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<string> {
|
|
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<void> {
|
|
await db.delete(sessions).where(eq(sessions.tokenHash, hashToken(token)));
|
|
}
|
|
|
|
export async function setSessionCookie(token: string): Promise<void> {
|
|
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<void> {
|
|
const cookieStore = await cookies();
|
|
cookieStore.delete(SESSION_COOKIE);
|
|
}
|
|
|
|
export async function getSessionToken(): Promise<string | undefined> {
|
|
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;
|
|
}
|