ac865ba725
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
86 lines
2.1 KiB
JavaScript
86 lines
2.1 KiB
JavaScript
"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<object|null>}
|
|
*/
|
|
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,
|
|
}
|