UNPKG

inceptum

Version:

hipages take on the foundational library for enterprise-grade apps written in NodeJS

156 lines 6.55 kB
"use strict"; 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