import config from "config"; import flash from "connect-flash"; import {Router} from "express"; import session from "express-session"; import ApplicationComponent from "../ApplicationComponent.js"; import SecurityError from "../SecurityError.js"; import FrontendToolsComponent from "./FrontendToolsComponent.js"; import RedisComponent from "./RedisComponent.js"; export default class SessionComponent extends ApplicationComponent { private readonly storeComponent: RedisComponent; public constructor(storeComponent: RedisComponent) { super(); this.storeComponent = storeComponent; } public async init(): Promise { const globals = this.getApp().asOptional(FrontendToolsComponent)?.getGlobals(); if (globals) { globals.set('flash', () => ''); } } public async checkSecuritySettings(): Promise { this.checkSecurityConfigField('session.secret'); if (!config.get('session.cookie.secure')) { throw new SecurityError('Cannot set cookie secure field to false.'); } } public async initRoutes(router: Router): Promise { router.use(session({ saveUninitialized: true, secret: config.get('session.secret'), store: this.storeComponent.getStore(), resave: false, cookie: { httpOnly: true, secure: config.get('session.cookie.secure'), }, rolling: true, })); router.use(flash()); router.use((req, res, next) => { // Request session getters req.getSessionOptional = () => { return req.session; }; req.getSession = () => { const session = req.getSessionOptional(); if (!session) throw new Error('Session not initialized.'); return session; }; // Session persistence const session = req.getSession(); if (session.persistent) { session.cookie.maxAge = config.get('session.cookie.maxAge'); } else { session.cookie.maxAge = session.cookie.expires = undefined; } // Views session local res.locals.session = session; // Views flash function const _flash: FlashStorage = {}; res.locals.flash = (key?: string): FlashMessages | unknown[] => { if (key !== undefined) { if (_flash[key] === undefined) _flash[key] = req.flash(key); return _flash[key] || []; } if (_flash._messages === undefined) { _flash._messages = { info: req.flash('info'), success: req.flash('success'), warning: req.flash('warning'), error: req.flash('error'), 'error-alert': req.flash('error-alert'), }; } return _flash._messages; }; next(); }); } } export type FlashMessages = { [k: string]: unknown[] | undefined }; export type DefaultFlashMessages = FlashMessages & { info?: unknown[] | undefined; success?: unknown[] | undefined; warning?: unknown[] | undefined; error?: unknown[] | undefined; }; type FlashStorage = FlashMessages & { _messages?: DefaultFlashMessages, };