import { serialize, parse } from 'cookie'; import { randomBytes } from 'crypto'; import type { SessionData } from '~/utils/types'; // In-memory session store (in production, you'd use Redis or a database) const sessionStore = new Map(); // Cleanup expired sessions every 5 minutes setInterval(() => { const now = Date.now(); let cleanedCount = 0; for (const [sessionId, session] of sessionStore.entries()) { if (now > session.expiresAt) { sessionStore.delete(sessionId); cleanedCount++; } } if (cleanedCount > 0) { console.log(`๐Ÿงน Cleaned up ${cleanedCount} expired sessions`); } }, 5 * 60 * 1000); export class SessionManager { private cookieName = 'monacousa-session'; constructor() { // No encryption key needed since we're only storing session IDs } private generateSessionId(): string { return randomBytes(32).toString('hex'); } createSession(sessionData: SessionData, rememberMe: boolean = false): string { const sessionId = this.generateSessionId(); const maxAge = rememberMe ? 60 * 60 * 24 * 30 : 60 * 60 * 24 * 7; // 30 days vs 7 days const expiresAt = Date.now() + (maxAge * 1000); // Store session data server-side sessionStore.set(sessionId, { data: sessionData, expiresAt, rememberMe }); return serialize(this.cookieName, sessionId, { httpOnly: true, secure: true, sameSite: 'none', maxAge, path: '/', }); } getSession(cookieHeader?: string): SessionData | null { if (!cookieHeader) { return null; } const cookies = parse(cookieHeader); const sessionId = cookies[this.cookieName]; if (!sessionId) { return null; } const sessionEntry = sessionStore.get(sessionId); if (!sessionEntry) { return null; } // Check if session is expired if (Date.now() > sessionEntry.expiresAt) { sessionStore.delete(sessionId); return null; } // Update last activity sessionEntry.data.lastActivity = Date.now(); return sessionEntry.data; } destroySession(cookieHeader?: string): string { if (cookieHeader) { const cookies = parse(cookieHeader); const sessionId = cookies[this.cookieName]; if (sessionId && sessionStore.has(sessionId)) { sessionStore.delete(sessionId); console.log(`๐Ÿ—‘๏ธ Destroyed session: ${sessionId.substring(0, 8)}...`); } } return serialize(this.cookieName, '', { httpOnly: true, secure: true, sameSite: 'none', maxAge: 0, path: '/', }); } // Helper method to get session stats getSessionStats() { return { totalSessions: sessionStore.size, activeSessions: Array.from(sessionStore.values()).filter(s => Date.now() < s.expiresAt).length }; } } export function createSessionManager(): SessionManager { return new SessionManager(); }