83 lines
2.6 KiB
TypeScript
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);
|
|
}
|
|
}
|