'use strict'; // Custom session store backed by the same better-sqlite3 instance. // Avoids pulling in a second SQLite driver (connect-sqlite3 / sqlite3). const session = require('express-session'); const TTL_SECONDS = 8 * 60 * 60; // 8 hours — matches cookie maxAge function createSessionStore(db) { class SQLiteStore extends session.Store { constructor() { super(); // Purge expired sessions every 15 minutes setInterval(() => { db.prepare('DELETE FROM sessions WHERE expired < ?').run(Math.floor(Date.now() / 1000)); }, 15 * 60 * 1000).unref(); } get(sid, cb) { try { const row = db.prepare('SELECT sess, expired FROM sessions WHERE sid = ?').get(sid); if (!row) return cb(null, null); if (row.expired < Math.floor(Date.now() / 1000)) { db.prepare('DELETE FROM sessions WHERE sid = ?').run(sid); return cb(null, null); } cb(null, JSON.parse(row.sess)); } catch (e) { cb(e); } } set(sid, sessionData, cb) { try { const ttl = sessionData.cookie?.maxAge ? Math.floor(sessionData.cookie.maxAge / 1000) : TTL_SECONDS; const expired = Math.floor(Date.now() / 1000) + ttl; db.prepare('INSERT OR REPLACE INTO sessions (sid, sess, expired) VALUES (?, ?, ?)') .run(sid, JSON.stringify(sessionData), expired); cb(null); } catch (e) { cb(e); } } destroy(sid, cb) { try { db.prepare('DELETE FROM sessions WHERE sid = ?').run(sid); cb(null); } catch (e) { cb(e); } } touch(sid, sessionData, cb) { try { const ttl = sessionData.cookie?.maxAge ? Math.floor(sessionData.cookie.maxAge / 1000) : TTL_SECONDS; const expired = Math.floor(Date.now() / 1000) + ttl; db.prepare('UPDATE sessions SET expired = ? WHERE sid = ?').run(expired, sid); cb(null); } catch (e) { cb(e); } } } return new SQLiteStore(); } module.exports = { createSessionStore };