import { prisma } from "./prisma.js"; const SESSION_DURATION_MS = 12 * 60 * 60 * 1000; const SESSION_RETENTION_DAYS = 30; export interface AuthSessionContext { id: string; userId: string; expiresAt: Date; } export function getSessionExpiryDate(now = new Date()) { return new Date(now.getTime() + SESSION_DURATION_MS); } export function getSessionRetentionCutoff(now = new Date()) { return new Date(now.getTime() - SESSION_RETENTION_DAYS * 24 * 60 * 60 * 1000); } export async function createAuthSession(input: { userId: string; ipAddress?: string | null; userAgent?: string | null }) { return prisma.authSession.create({ data: { userId: input.userId, expiresAt: getSessionExpiryDate(), ipAddress: input.ipAddress ?? null, userAgent: input.userAgent ?? null, }, }); } export async function getActiveAuthSession(sessionId: string, userId: string): Promise { const session = await prisma.authSession.findFirst({ where: { id: sessionId, userId, revokedAt: null, expiresAt: { gt: new Date(), }, }, select: { id: true, userId: true, expiresAt: true, }, }); if (!session) { return null; } return session; } export async function touchAuthSession(sessionId: string) { await prisma.authSession.update({ where: { id: sessionId }, data: { lastSeenAt: new Date(), }, }); } export async function revokeAuthSession(sessionId: string, input: { revokedById?: string | null; reason: string }) { return prisma.authSession.updateMany({ where: { id: sessionId, revokedAt: null, }, data: { revokedAt: new Date(), revokedById: input.revokedById ?? null, revokedReason: input.reason, }, }); } export async function pruneOldAuthSessions() { const cutoff = getSessionRetentionCutoff(); const result = await prisma.authSession.deleteMany({ where: { OR: [ { revokedAt: { lt: cutoff, }, }, { revokedAt: null, expiresAt: { lt: cutoff, }, }, ], }, }); return result.count; }