swaf/src/components/ExpressAppComponent.ts

92 lines
3.1 KiB
TypeScript

import compression from "compression";
import express, {Express, Router} from "express";
import {Server} from "http";
import ApplicationComponent from "../ApplicationComponent.js";
import {logger, preventContextCorruptionMiddleware} from "../Logger.js";
import Middleware from "../Middleware.js";
import {Type} from "../Utils.js";
export default class ExpressAppComponent extends ApplicationComponent {
private readonly addr: string;
private readonly port: number;
private server?: Server;
private expressApp?: Express;
public constructor(addr: string, port: number) {
super();
this.addr = addr;
this.port = port;
}
public async start(app: Express): Promise<void> {
this.server = app.listen(this.port, this.addr, () => {
logger.info(`Web server running on http://${this.addr}:${this.port}`);
});
// Proxy
app.set('trust proxy', 'loopback');
this.expressApp = app;
}
public async initRoutes(router: Router): Promise<void> {
router.use(preventContextCorruptionMiddleware(express.json({
type: req => req.headers['content-type']?.match(/^application\/(.+\+)?json$/),
})));
router.use(preventContextCorruptionMiddleware(express.urlencoded({
extended: true,
})));
// gzip
router.use(compression());
router.use((req, res, next) => {
req.middlewares = [];
req.as = <M extends Middleware>(type: Type<M>): M => {
const middleware = req.middlewares.find(m => m.constructor === type);
if (!middleware) throw new Error('Middleware ' + type.name + ' not present in this request.');
return middleware as M;
};
res.formatViewData = function (
viewName: string,
data?: Record<string, unknown>,
callback?: (err: Error, html: string) => void,
) {
this.format({
html: () => {
this.render(viewName, data, callback);
},
json: () => {
if (typeof data === 'undefined') data = {};
const serialized = JSON.stringify({...data, viewName}, (key, value) => {
if (key.startsWith('_') || typeof value === 'function') return undefined;
else return value;
});
this.contentType('application/json');
this.send(serialized);
},
});
};
next();
});
}
public async stop(): Promise<void> {
const server = this.server;
if (server) {
await this.close('Webserver', callback => server.close(callback));
}
}
public getServer(): Server {
if (!this.server) throw 'Server was not initialized.';
return this.server;
}
public getExpressApp(): Express {
if (!this.expressApp) throw new Error('Express app not initialized.');
return this.expressApp;
}
}