import { randomUUID } from 'node:crypto';
import type Database from 'better-sqlite3';
import { config } from '../config.js';
import { logger } from '../utils/logger.js';
export interface DashboardSession {
id: string;
user_id: string;
csrf_token: string;
expires_at: number;
created_at: number;
}
export class SessionManager {
constructor(private db: Database.Database) {}
create(userId: string): DashboardSession {
const session: DashboardSession = {
id: randomUUID(),
user_id: userId,
csrf_token: randomUUID(),
expires_at: Math.floor((Date.now() + config.dashboard.sessionTtlMs) / 1000),
created_at: Math.floor(Date.now() / 1000),
};
this.db
.prepare(
'INSERT INTO dashboard_sessions (id, user_id, csrf_token, expires_at, created_at) VALUES (?, ?, ?, ?, ?)'
)
.run(session.id, session.user_id, session.csrf_token, session.expires_at, session.created_at);
return session;
}
validate(sessionId: string): DashboardSession | null {
const session = this.db
.prepare('SELECT * FROM dashboard_sessions WHERE id = ?')
.get(sessionId) as DashboardSession | undefined;
if (!session) return null;
const now = Math.floor(Date.now() / 1000);
if (session.expires_at <= now) {
this.delete(sessionId);
return null;
}
return session;
}
delete(sessionId: string): void {
this.db.prepare('DELETE FROM dashboard_sessions WHERE id = ?').run(sessionId);
}
deleteByUserId(userId: string): void {
this.db.prepare('DELETE FROM dashboard_sessions WHERE user_id = ?').run(userId);
}
cleanExpired(): number {
const now = Math.floor(Date.now() / 1000);
const result = this.db
.prepare('DELETE FROM dashboard_sessions WHERE expires_at <= ?')
.run(now);
if (result.changes > 0) {
logger.info({ count: result.changes }, 'Cleaned expired dashboard sessions');
}
return result.changes;
}
}