swaf/src/components/SessionComponent.ts

108 lines
3.5 KiB
TypeScript

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<void> {
const globals = this.getApp().asOptional(FrontendToolsComponent)?.getGlobals();
if (globals) {
globals.set('flash', () => '');
}
}
public async checkSecuritySettings(): Promise<void> {
this.checkSecurityConfigField('session.secret');
if (!config.get<boolean>('session.cookie.secure')) {
throw new SecurityError('Cannot set cookie secure field to false.');
}
}
public async initRoutes(router: Router): Promise<void> {
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,
};