2021-05-03 19:29:22 +02:00
|
|
|
import compression from "compression";
|
2020-07-11 11:46:16 +02:00
|
|
|
import express, {Express, Router} from "express";
|
2020-04-22 15:52:17 +02:00
|
|
|
import {Server} from "http";
|
2021-05-03 19:29:22 +02:00
|
|
|
|
|
|
|
import ApplicationComponent from "../ApplicationComponent.js";
|
|
|
|
import {logger, preventContextCorruptionMiddleware} from "../Logger.js";
|
|
|
|
import Middleware from "../Middleware.js";
|
|
|
|
import {Type} from "../Utils.js";
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-09-25 22:03:22 +02:00
|
|
|
export default class ExpressAppComponent extends ApplicationComponent {
|
2020-09-17 21:15:37 +02:00
|
|
|
private readonly addr: string;
|
2020-04-22 15:52:17 +02:00
|
|
|
private readonly port: number;
|
|
|
|
private server?: Server;
|
2020-08-05 12:05:52 +02:00
|
|
|
private expressApp?: Express;
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-09-17 21:15:37 +02:00
|
|
|
public constructor(addr: string, port: number) {
|
2020-04-22 15:52:17 +02:00
|
|
|
super();
|
2020-09-17 21:15:37 +02:00
|
|
|
this.addr = addr;
|
2020-04-22 15:52:17 +02:00
|
|
|
this.port = port;
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:46:16 +02:00
|
|
|
public async start(app: Express): Promise<void> {
|
2020-09-17 21:15:37 +02:00
|
|
|
this.server = app.listen(this.port, this.addr, () => {
|
2021-11-28 21:24:51 +01:00
|
|
|
logger.info(`Web server running on http://${this.addr}:${this.port}`);
|
2020-04-22 15:52:17 +02:00
|
|
|
});
|
2020-07-15 12:35:14 +02:00
|
|
|
|
|
|
|
// Proxy
|
|
|
|
app.set('trust proxy', 'loopback');
|
2020-08-05 12:05:52 +02:00
|
|
|
|
|
|
|
this.expressApp = app;
|
2020-07-11 11:46:16 +02:00
|
|
|
}
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2021-05-13 16:03:59 +02:00
|
|
|
public async initRoutes(router: Router): Promise<void> {
|
2021-01-24 16:35:33 +01:00
|
|
|
router.use(preventContextCorruptionMiddleware(express.json({
|
2020-09-25 23:42:15 +02:00
|
|
|
type: req => req.headers['content-type']?.match(/^application\/(.+\+)?json$/),
|
2021-01-24 16:35:33 +01:00
|
|
|
})));
|
|
|
|
router.use(preventContextCorruptionMiddleware(express.urlencoded({
|
2020-06-14 15:08:56 +02:00
|
|
|
extended: true,
|
2021-01-24 16:35:33 +01:00
|
|
|
})));
|
2020-04-22 15:52:17 +02:00
|
|
|
|
2020-07-09 14:18:05 +02:00
|
|
|
// gzip
|
|
|
|
router.use(compression());
|
|
|
|
|
2020-04-22 15:52:17 +02:00
|
|
|
router.use((req, res, next) => {
|
2020-09-25 22:03:22 +02:00
|
|
|
req.middlewares = [];
|
2020-09-25 23:42:15 +02:00
|
|
|
req.as = <M extends Middleware>(type: Type<M>): M => {
|
2020-09-25 22:03:22 +02:00
|
|
|
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;
|
|
|
|
};
|
2021-11-28 21:26:45 +01:00
|
|
|
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);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
2020-04-22 15:52:17 +02:00
|
|
|
next();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async stop(): Promise<void> {
|
2020-09-25 23:42:15 +02:00
|
|
|
const server = this.server;
|
|
|
|
if (server) {
|
|
|
|
await this.close('Webserver', callback => server.close(callback));
|
2020-04-22 15:52:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public getServer(): Server {
|
|
|
|
if (!this.server) throw 'Server was not initialized.';
|
|
|
|
return this.server;
|
|
|
|
}
|
2020-08-05 12:05:52 +02:00
|
|
|
|
|
|
|
public getExpressApp(): Express {
|
2020-09-25 23:42:15 +02:00
|
|
|
if (!this.expressApp) throw new Error('Express app not initialized.');
|
|
|
|
return this.expressApp;
|
2020-08-05 12:05:52 +02:00
|
|
|
}
|
2020-06-14 21:47:18 +02:00
|
|
|
}
|