UNPKG

@universis/jrs-worker

Version:

JasperReports Server Worker for Universis Platform

128 lines (121 loc) 4.6 kB
import express from 'express'; import { createProxyMiddleware, responseInterceptor } from 'http-proxy-middleware'; import genericPool from 'generic-pool'; import { ConfigurationBase, Args, TraceUtils } from '@themost/common'; import { URL } from 'url'; class JrsWorker { private active: boolean = false; // eslint-disable-next-line @typescript-eslint/require-await async connect(): Promise<this> { this.active = true; return this; } // eslint-disable-next-line @typescript-eslint/require-await async disconnect() { if (this.active) { this.active = false; } } } const factory = { create: (): Promise<JrsWorker> => { return new JrsWorker().connect(); }, destroy: (worker: JrsWorker): Promise<void> => { return worker.disconnect(); } }; declare global { namespace Express { interface Request { worker: JrsWorker } } } declare module 'http' { interface IncomingMessage { worker: JrsWorker } } function jrsWorkerRouter(configuration: ConfigurationBase) { // prepare router const router = express.Router(); // create worker pool (limit to 1 worker) /** * get jasper server configuration */ const jasperServer: { target: string, workerPool: any } = configuration.getSourceAt('settings/jrsWorker'); // get worker pool options // read more about generic pool options at https://github.com/coopernurse/node-pool?tab=readme-ov-file#createpool const opts = Object.assign({}, { "max": 1, // maximum size of the pool (the default is 1) "min": 0, // minimum size of the pool }, jasperServer.workerPool); // create worker pool const pool = genericPool.createPool<JrsWorker>(factory, opts); // check if jasper server configuration is valid Args.check(jasperServer && jasperServer.target, 'Jasper Server configuration is not valid. Expected target server.'); // use jasper server target as proxy target const { pathname } = new URL(jasperServer.target); const target = new URL('/', jasperServer.target).toString() ; router.use(pathname, (req, res, next) => { void pool.acquire().then((worker) => { req.worker = worker; next(); }).catch((err) => { return next(err); }); }, createProxyMiddleware({ target, logLevel: configuration.getSourceAt('settings/jrsWorker/logLevel') || 'info', changeOrigin: true, selfHandleResponse: true, onError: (err, req, res) => { // get request as url object const requestURL = new URL(req.url, jasperServer.target); // remove client access token (for security reasons) requestURL.searchParams.delete('REPORT_CLIENT_TOKEN'); // log error TraceUtils.error(`Proxy error on ${requestURL.toString()}`); TraceUtils.error(err); // release worker // eslint-disable-next-line @typescript-eslint/no-floating-promises pool.release(req.worker).then(() => { // and send error to client res.writeHead(500); res.end('An error has occurred while proxying request to report server.'); }).catch((error: Error) => { // log error unhandled error while finalizing request TraceUtils.error(error); // write error res.writeHead(500); res.end('An error has occurred while finalizing request.'); }).finally(() => { // remove worker from request delete req.worker; }); }, // eslint-disable-next-line @typescript-eslint/no-unused-vars onClose: (res, socket, head) => { const worker = res.req && res.req.worker; if (worker == null) { return; } // eslint-disable-next-line @typescript-eslint/no-floating-promises pool.release(res.req.worker).then(() => { delete res.req.worker; }); }, // eslint-disable-next-line @typescript-eslint/no-misused-promises, @typescript-eslint/no-unused-vars onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => { await pool.release(req.worker); // remove worker from request delete req.worker; return responseBuffer; }) })); return router; } export { jrsWorkerRouter }