"use strict" const FIVE_MINUTES_MS = 5 * 60 * 1000 /** * Insert a new admin session row. * @param {import('pg').Pool} pool * @param {string} tokenHash * @param {Date} expiresAt */ async function createAdminSession(pool, tokenHash, expiresAt) { await pool.query( `INSERT INTO admin_sessions (token_hash, expires_at) VALUES ($1, $2)`, [tokenHash, expiresAt] ) } /** * Validate an admin session by token hash. * Returns the session row if valid, null otherwise. * Updates last_seen_at if more than 5 minutes have passed. * @param {import('pg').Pool} pool * @param {string} tokenHash * @param {Date} [now] * @returns {Promise} */ async function validateAdminSession(pool, tokenHash, now = new Date()) { const { rows } = await pool.query( `SELECT * FROM admin_sessions WHERE token_hash = $1`, [tokenHash] ) if (rows.length === 0) return null const session = rows[0] // Reject if revoked if (session.revoked_at !== null) return null // Reject if expired if (new Date(session.expires_at) <= now) return null // Update last_seen_at only if more than 5 minutes have passed const lastSeen = session.last_seen_at ? new Date(session.last_seen_at) : null if (!lastSeen || now - lastSeen > FIVE_MINUTES_MS) { await pool.query( `UPDATE admin_sessions SET last_seen_at = $1 WHERE token_hash = $2`, [now, tokenHash] ) session.last_seen_at = now } return session } /** * Revoke an admin session by setting revoked_at to now. * @param {import('pg').Pool} pool * @param {string} tokenHash */ async function revokeAdminSession(pool, tokenHash) { await pool.query( `UPDATE admin_sessions SET revoked_at = now() WHERE token_hash = $1`, [tokenHash] ) } /** * Delete all sessions where expires_at <= now. * @param {import('pg').Pool} pool * @param {Date} [now] */ async function deleteExpiredAdminSessions(pool, now = new Date()) { await pool.query( `DELETE FROM admin_sessions WHERE expires_at <= $1`, [now] ) } module.exports = { createAdminSession, validateAdminSession, revokeAdminSession, deleteExpiredAdminSessions, }