metis-desk/modules/identity/src/api/infrastructure/persistence/sequelize-session.store.ts
2026-10-10 16:40:51 +02:00

83 lines
2.6 KiB
TypeScript

import session from "express-session";
import type { Sequelize } from "sequelize";
import { QueryTypes } from "sequelize";
import { restoreSession, sessionRow, writeSession } from "./session-normalization";
type Callback = (error?: Error | null) => void;
function failure(error: unknown) {
return error instanceof Error ? error : new Error("Fallo del almacén de sesiones.");
}
/** Almacén propio: persistencia relacional del contrato de sesión usado por Metis Desk. */
export class SequelizeSessionStore extends session.Store {
constructor(private readonly database: Sequelize) {
super();
}
async get(
sid: string,
callback: (error: Error | null, data?: session.SessionData | null) => void
) {
let result: session.SessionData | null = null;
let error: Error | null = null;
try {
const rows = await this.database.query<Record<string, unknown>>(
"SELECT * FROM http_sessions WHERE sid=:sid AND expires>NOW()",
{ replacements: { sid }, type: QueryTypes.SELECT }
);
result = rows[0] ? restoreSession(rows[0]) : null;
} catch (caught) {
error = failure(caught);
}
callback(error, result);
}
set(sid: string, data: session.SessionData, callback?: Callback) {
return this.complete(async () => {
await writeSession(this.database, sid, data);
await this.database.query("DELETE FROM http_sessions WHERE expires<=NOW()");
}, callback);
}
destroy(sid: string, callback?: Callback) {
return this.complete(async () => {
await this.database.query("DELETE FROM http_sessions WHERE sid=:sid", {
replacements: { sid },
});
}, callback);
}
touch(sid: string, data: session.SessionData, callback?: Callback) {
return this.complete(async () => {
const cookie = sessionRow(data);
const expires = cookie.cookieExpires
? new Date(cookie.cookieExpires)
: new Date(Date.now() + 8 * 60 * 60 * 1000);
// Tocar la sesión renueva la cookie; no sobrescribe la autenticación de otra petición.
await this.database.query(
"UPDATE http_sessions SET expires=:expires,cookieExpires=:cookieExpires,updatedAt=NOW() WHERE sid=:sid",
{ replacements: { sid, expires, cookieExpires: cookie.cookieExpires } }
);
}, callback);
}
private async complete(operation: () => Promise<void>, callback?: Callback) {
let error: Error | null = null;
try {
await operation();
} catch (caught) {
if (!callback) {
throw caught;
}
error = failure(caught);
}
callback?.(error);
}
}