import { createHash, randomBytes } from 'node:crypto'; import db from './db'; function getSecretKey(): string { const key = process.env.SECRET_KEY; if (!key) throw new Error('SECRET_KEY environment variable is required'); return key; } export function hashIP(ip: string): string { return createHash('sha256').update(getSecretKey() + ':' + ip).digest('hex').slice(0, 16); } export function generateId(): string { return randomBytes(32).toString('hex'); } export interface AdminSession { id: string; user_id: string; created_at: string; expires_at: string; } export function createSession(userId: string): string { const sessionId = generateId(); const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); db.prepare( `INSERT INTO admin_sessions (id, user_id, expires_at) VALUES (?, ?, ?)` ).run(sessionId, userId, expiresAt); return sessionId; } export function getSession(sessionId: string): AdminSession | undefined { return db .prepare( `SELECT * FROM admin_sessions WHERE id = ? AND expires_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now')` ) .get(sessionId) as AdminSession | undefined; } export function deleteSession(sessionId: string): void { db.prepare(`DELETE FROM admin_sessions WHERE id = ?`).run(sessionId); } export function cleanExpiredSessions(): void { db.prepare( `DELETE FROM admin_sessions WHERE expires_at <= strftime('%Y-%m-%dT%H:%M:%SZ', 'now')` ).run(); db.prepare( `DELETE FROM webauthn_challenges WHERE expires_at <= strftime('%Y-%m-%dT%H:%M:%SZ', 'now')` ).run(); } export const SESSION_COOKIE = 'admin_session'; export const CHALLENGE_COOKIE = 'webauthn_challenge'; export function sessionCookieOptions(maxAge: number) { return { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict' as const, path: '/', maxAge, }; }