@underpostnet/underpost
Version:
Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orc
140 lines (123 loc) • 4.94 kB
JavaScript
/**
* Manages the creation and configuration of the reverse proxy server,
* including handling HTTP/HTTPS listeners and routing based on host configuration.
* @module src/server/proxy.js
* @namespace ProxyService
*/
;
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { loggerFactory, loggerMiddleware } from './logger.js';
import {
buildPortProxyRouter,
buildProxyRouter,
etcHostFactory,
getTlsHosts,
isDevProxyContext,
isTlsDevProxy,
} from './conf.js';
import { shellExec } from './process.js';
import fs from 'fs-extra';
import Underpost from '../index.js';
const logger = loggerFactory(import.meta);
/**
* Main class for building and running the proxy server.
* All utility methods are implemented as static to serve as a namespace container.
* @class ProxyService
* @memberof ProxyService
*/
class ProxyService {
/**
* Builds and starts the proxy server with appropriate routing and SSL configuration.
* @static
* @returns {Promise<void>} Resolves when the server is successfully started.
*/
static async build() {
if (process.env.NODE_ENV === 'production') process.env.DEV_PROXY_PORT_OFFSET = 0;
// Start a default Express listener on process.env.PORT (potentially unused, but ensures Express is initialized)
process.env.PORT = parseInt(process.env.PORT) + parseInt(process.env.DEV_PROXY_PORT_OFFSET);
express().listen(process.env.PORT);
const proxyRouter = buildProxyRouter();
for (let port of Object.keys(proxyRouter)) {
const hosts = proxyRouter[port];
port = parseInt(port) + parseInt(process.env.DEV_PROXY_PORT_OFFSET);
const proxyPath = '/';
const proxyHost = 'localhost';
const runningData = { host: proxyHost, path: proxyPath, client: null, runtime: 'nodejs', meta: import.meta };
const app = express();
// Set logger middleware
app.use(loggerMiddleware(import.meta));
// Proxy middleware options
/** @type {import('http-proxy-middleware/dist/types').Options} */
const options = {
pathFilter: proxyPath, // Use '/' as the general filter (v3 API)
ws: true, // Enable websocket proxying
target: `http://localhost:${parseInt(process.env.PORT - 1)}`, // Default target (should be overridden by router)
router: {},
// changeOrigin: true,
logger: logger,
xfwd: true, // Adds x-forward headers (Host, Proto, etc.)
onProxyReq: (proxyReq, req, res, options) => {},
pathRewrite: {},
};
options.router = buildPortProxyRouter({
port,
proxyRouter,
hosts,
orderByPathLength: true,
devProxyContext: process.env.NODE_ENV !== 'production',
});
app.use(proxyPath, createProxyMiddleware(options));
// Determine which server to start (HTTP or HTTPS) based on port and environment
switch (process.env.NODE_ENV) {
case 'production':
switch (port) {
case 443:
// For port 443 (HTTPS), create the SSL server
const { ServerSSL } = await Underpost.tls.createSslServer(app, hosts);
await Underpost.start.listenPortController(ServerSSL, port, runningData);
break;
default:
// For other ports in production, use standard HTTP
await Underpost.start.listenPortController(app, port, runningData);
break;
}
break;
default:
switch (port) {
case 443: {
let tlsHosts = hosts;
if (isDevProxyContext() && isTlsDevProxy()) {
tlsHosts = {};
for (const tlsHost of getTlsHosts(hosts)) {
if (fs.existsSync(Underpost.tls.SSL_BASE(tlsHost))) fs.removeSync(Underpost.tls.SSL_BASE(tlsHost));
if (!Underpost.tls.validateSecureContext(tlsHost)) shellExec(`node bin/deploy tls "${tlsHost}"`);
tlsHosts[tlsHost] = {};
}
}
const { ServerSSL } = await Underpost.tls.createSslServer(app, tlsHosts);
await Underpost.start.listenPortController(ServerSSL, port, runningData);
break;
}
default: // In non-production, always use standard HTTP listener
await Underpost.start.listenPortController(app, port, runningData);
break;
}
}
logger.info('Proxy running', { port, router: options.router });
if (process.env.NODE_ENV === 'development')
logger.info(
etcHostFactory(Object.keys(options.router), {
append: true,
}).renderHosts,
);
}
}
}
/**
* Backward compatibility export
* @type {function(): Promise<void>}
* @memberof ProxyService
*/
const buildProxy = ProxyService.build;
export { ProxyService, buildProxy };