inceptum
Version:
hipages take on the foundational library for enterprise-grade apps written in NodeJS
156 lines • 6.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.errorMiddleware = exports.clientErrorMiddleware = exports.RouteRegisterUtil = void 0;
const e = require("express");
const onFinished = require("on-finished");
const prometheus_extended_gauge_1 = require("prometheus-extended-gauge");
const promBundle = require("express-prom-bundle");
const xmlparser = require("express-xml-bodyparser");
const BaseSingletonDefinition_1 = require("../ioc/objectdefinition/BaseSingletonDefinition");
const NewrelicUtil_1 = require("../newrelic/NewrelicUtil");
const LogManager_1 = require("../log/LogManager");
const WebRoutingInspector_1 = require("./WebRoutingInspector");
const HttpError_1 = require("./HttpError");
const ContentNegotiationMiddleware_1 = require("./ContentNegotiationMiddleware");
const logger = LogManager_1.LogManager.getLogger(__filename);
const activeRequestsGauge = new prometheus_extended_gauge_1.ExtendedGauge({
max: true,
min: false,
average: true,
bucketSizeMillis: 1000,
numBuckets: 60,
help: 'Number of active http requests being processed',
name: 'http_active_requests',
});
class RouteRegisterUtil {
constructor() {
this.routesToRegister = [];
}
doRegister() {
this.routesToRegister.forEach((route) => {
logger.info(`Registering route from ${route.objectName}: ${route.verb.toUpperCase()} ${route.path} -> ${route.methodName}`);
this.express[route.verb](route.path, (req, res) => {
return this[route.instanceProperty][route.methodName](req, res);
});
});
}
}
exports.RouteRegisterUtil = RouteRegisterUtil;
/**
* Client errors do not need to be sent to NewRelic so create clientErrorMiddleware
* to send response if the error is 4xx. clientErrorMiddleware needs to be registered
* before errorMiddleware.
* @param err
* @param req
* @param res
* @param next
*/
const clientErrorMiddleware = (err, req, res, next) => {
let more = [];
if (err instanceof HttpError_1.default && err.statusCode && err.statusCode >= 400 && err.statusCode < 500) {
res.status(err.getStatusCode()).send({ message: err.message });
}
else if (err.failedValidation) { // swagger-tools validation
more = err.results && err.results.errors;
res.status(400).send({ message: err.message });
}
else {
return next(err); // Give back to express to handle
}
logger.error({ err, more }); // log 4xx errors and swagger errors
};
exports.clientErrorMiddleware = clientErrorMiddleware;
const errorMiddleware = (err, req, res, next) => {
if (res.headersSent) {
return next(err); // Give back to express to handle
}
logger.error(err);
const data = { message: err.message };
if (err instanceof HttpError_1.default && err.statusCode) {
if (err.statusCode >= 500) {
NewrelicUtil_1.NewrelicUtil.noticeError(err);
}
res.status(err.getStatusCode()).send(data);
}
else {
NewrelicUtil_1.NewrelicUtil.noticeError(err);
res.status(500).send(data);
}
};
exports.errorMiddleware = errorMiddleware;
class WebPlugin {
// private expressProvider = () => new e();
constructor(options = {}) {
this.options = options;
this.name = 'WebPlugin';
}
willStart(app, pluginContext) {
const express = e();
express.disable('x-powered-by');
express.set('trust proxy', true); // stop redirecting to http internally https://expressjs.com/en/guide/behind-proxies.html
pluginContext.set(WebPlugin.CONTEXT_APP_KEY, express);
const context = app.getContext();
express.use(promBundle({
includeMethod: true,
buckets: [0.003, 0.03, 0.1, 0.3, 0.5, 1.5, 10],
autoregister: false,
}));
// Add middleware for stats on active requests
express.use((req, res, next) => {
activeRequestsGauge.inc();
onFinished(res, () => activeRequestsGauge.dec());
next();
});
this.registerXmlBodyParser(express);
this.registerXmlContentNegotiationMiddleware(express, app.getConfig('app.xmlRoot', ''));
// move from didStart to willStart
express.use(e.json({ limit: app.getConfig('app.server.maxRequestSize', '10mb') }));
express.use(e.urlencoded({ extended: true, limit: app.getConfig('app.server.maxRequestSize', '10mb') }));
if (this.options && this.options.staticRoots) {
this.options.staticRoots.forEach((root) => {
express.use(e.static(root));
});
}
const definition = new BaseSingletonDefinition_1.BaseSingletonDefinition(RouteRegisterUtil);
definition.withLazyLoading(false);
definition.startFunction('doRegister');
definition.setPropertyByValue('express', express);
context.registerDefinition(definition);
context.addObjectDefinitionInspector(new WebRoutingInspector_1.WebRoutingInspector(definition));
}
didStart(app, pluginContext) {
const express = pluginContext.get(WebPlugin.CONTEXT_APP_KEY);
const port = app.getConfig('app.server.port', 10010);
express.use(exports.clientErrorMiddleware);
// Add error handling middleware as the final middleware.
express.use(exports.errorMiddleware);
// Start the server
const server = express.listen(port, () => {
app.logger.info(`Server started at http://localhost:${port}`);
});
pluginContext.set(WebPlugin.CONTEXT_SERVER_KEY, server);
}
didStop(app, pluginContext) {
const express = pluginContext.get(WebPlugin.CONTEXT_SERVER_KEY);
app.logger.info('Shutting down server');
if (express) {
express.close();
}
}
registerXmlBodyParser(express) {
if (this.options && this.options.xmlBodyParserOptions) {
express.use(xmlparser(this.options.xmlBodyParserOptions));
}
}
registerXmlContentNegotiationMiddleware(express, xmlRoot) {
const negoContentMiddleware = new ContentNegotiationMiddleware_1.ContentNegotiationMiddleware(xmlRoot);
const xmlMiddleware = negoContentMiddleware.getMiddleware();
if (xmlMiddleware) {
express.use(xmlMiddleware);
}
}
}
exports.default = WebPlugin;
WebPlugin.CONTEXT_APP_KEY = 'WebPlugin/APP';
WebPlugin.CONTEXT_SERVER_KEY = 'WebPlugin/SERVER';
//# sourceMappingURL=WebPlugin.js.map