underpost 2.85.1 → 2.85.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,11 +66,11 @@ const Config = {
66
66
  /**
67
67
  * @method deployIdFactory
68
68
  * @description Creates a new deploy ID.
69
- * @param {string} [deployId='dd-default'] - The deploy ID.
70
- * @param {object} [options={ cluster: false }] - The options.
69
+ * @param {string} [deployId='dd-default
70
+ * @param {object} [options={ subConf: '', cluster: false }] - The options.
71
71
  * @memberof ServerConfBuilder
72
72
  */
73
- deployIdFactory: function (deployId = 'dd-default', options = { cluster: false }) {
73
+ deployIdFactory: function (deployId = 'dd-default', options = { subConf: '', cluster: false }) {
74
74
  if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
75
75
 
76
76
  logger.info('Build deployId', deployId);
@@ -102,6 +102,17 @@ const Config = {
102
102
 
103
103
  this.buildTmpConf(folder);
104
104
 
105
+ if (options.subConf) {
106
+ logger.info('Creating sub conf', {
107
+ deployId: deployId,
108
+ subConf: options.subConf,
109
+ });
110
+ fs.copySync(
111
+ `./engine-private/conf/${deployId}/conf.server.json`,
112
+ `./engine-private/conf/${deployId}/conf.server.dev.${options.subConf}.json`,
113
+ );
114
+ }
115
+
105
116
  if (options.cluster === true) {
106
117
  fs.writeFileSync(
107
118
  `./.github/workflows/${repoName}.cd.yml`,
@@ -640,6 +651,8 @@ const buildProxyRouter = () => {
640
651
  for (const path of Object.keys(confServer[host])) {
641
652
  if (confServer[host][path].singleReplica) continue;
642
653
 
654
+ if (isDevProxyContext()) confServer[host][path].proxy = [isTlsDevProxy() ? 443 : 80];
655
+
643
656
  confServer[host][path].port = newInstance(currentPort);
644
657
  for (const port of confServer[host][path].proxy) {
645
658
  if (!(port in proxyRouter)) proxyRouter[port] = {};
@@ -784,14 +797,21 @@ const buildKindPorts = (from, to) =>
784
797
  /**
785
798
  * @method buildPortProxyRouter
786
799
  * @description Builds the port proxy router.
787
- * @param {number} port - The port.
788
- * @param {object} proxyRouter - The proxy router.
789
- * @param {object} [options={ orderByPathLength: false }] - The options.
800
+ * @param {object} options - The options.
801
+ * @param {number} [options.port=4000] - The port.
802
+ * @param {object} options.proxyRouter - The proxy router.
803
+ * @param {object} [options.hosts] - The hosts.
804
+ * @param {boolean} [options.orderByPathLength=false] - Whether to order by path length.
805
+ * @param {boolean} [options.devProxyContext=false] - Whether to use dev proxy context.
790
806
  * @returns {object} - The port proxy router.
791
807
  * @memberof ServerConfBuilder
792
808
  */
793
- const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength: false }) => {
794
- const hosts = proxyRouter[port];
809
+ const buildPortProxyRouter = (
810
+ options = { port: 4000, proxyRouter, hosts, orderByPathLength: false, devProxyContext: false },
811
+ ) => {
812
+ let { port, proxyRouter, hosts, orderByPathLength } = options;
813
+ hosts = hosts || proxyRouter[port] || {};
814
+
795
815
  const router = {};
796
816
  // build router
797
817
  Object.keys(hosts).map((hostKey) => {
@@ -804,7 +824,7 @@ const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength:
804
824
  return;
805
825
  }
806
826
  }
807
-
827
+ // ${process.env.NODE_ENV === 'development' && !isDevProxyContext() ? `:${port}` : ''}
808
828
  const absoluteHost = [80, 443].includes(port)
809
829
  ? `${host}${path === '/' ? '' : path}`
810
830
  : `${host}:${port}${path === '/' ? '' : path}`;
@@ -812,12 +832,48 @@ const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength:
812
832
  if (absoluteHost in router)
813
833
  logger.warn('Overwrite: Absolute host already exists on router', { absoluteHost, target });
814
834
 
815
- router[absoluteHost] = target;
835
+ if (options.devProxyContext === true) {
836
+ const appDevPort = parseInt(target.split(':')[2]) - process.env.DEV_PROXY_PORT_OFFSET;
837
+ router[absoluteHost] = `http://localhost:${appDevPort}`;
838
+ } else router[absoluteHost] = target;
816
839
  }); // order router
817
840
 
818
841
  if (Object.keys(router).length === 0) return router;
819
842
 
820
- if (options.orderByPathLength === true) {
843
+ if (options.devProxyContext === true && process.env.NODE_ENV === 'development') {
844
+ const confDevApiServer = JSON.parse(
845
+ fs.readFileSync(`./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`),
846
+ 'utf8',
847
+ );
848
+ let devApiHosts = [];
849
+ let origins = [];
850
+ for (const _host of Object.keys(confDevApiServer))
851
+ for (const _path of Object.keys(confDevApiServer[_host])) {
852
+ if (confDevApiServer[_host][_path].origins && confDevApiServer[_host][_path].origins.length) {
853
+ origins.push(...confDevApiServer[_host][_path].origins);
854
+ if (_path !== 'peer' && devApiHosts.length === 0)
855
+ devApiHosts.push(
856
+ `${_host}${[80, 443].includes(port) && isDevProxyContext() ? '' : `:${port}`}${_path == '/' ? '' : _path}`,
857
+ );
858
+ }
859
+ }
860
+ origins = Array.from(new Set(origins));
861
+ console.log({
862
+ origins,
863
+ devApiHosts,
864
+ });
865
+ for (const devApiHost of devApiHosts) {
866
+ if (devApiHost in router) {
867
+ const target = router[devApiHost];
868
+ delete router[devApiHost];
869
+ router[`${devApiHost}/${process.env.BASE_API}`] = target;
870
+ router[`${devApiHost}/socket.io`] = target;
871
+ for (const origin of origins) router[devApiHost] = origin;
872
+ }
873
+ }
874
+ }
875
+
876
+ if (orderByPathLength === true) {
821
877
  const reOrderRouter = {};
822
878
  for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(router), 'length'))
823
879
  reOrderRouter[absoluteHostKey] = router[absoluteHostKey];
@@ -1409,11 +1465,14 @@ const buildApiConf = async (options = { deployId: '', subConf: '', host: '', pat
1409
1465
  * @param {string} options.apiBaseHost - The API base host.
1410
1466
  * @param {string} options.host - The host.
1411
1467
  * @param {string} options.path - The path.
1468
+ * @param {boolean} options.devProxy - The dev proxy flag.
1412
1469
  * @returns {void}
1413
1470
  * @memberof ServerConfBuilder
1414
1471
  */
1415
- const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiBaseHost: '', host: '', path: '' }) => {
1416
- let { deployId, subConf, host, path } = options;
1472
+ const buildClientStaticConf = async (
1473
+ options = { deployId: '', subConf: '', apiBaseHost: '', host: '', path: '', devProxy: false },
1474
+ ) => {
1475
+ let { deployId, subConf, host, path, devProxy } = options;
1417
1476
  if (!deployId) deployId = process.argv[2].trim();
1418
1477
  if (!subConf) subConf = process.argv[3].trim();
1419
1478
  if (!host) host = process.argv[4].trim();
@@ -1425,10 +1484,14 @@ const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiB
1425
1484
  fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`, 'utf8'),
1426
1485
  );
1427
1486
  envObj.PORT = parseInt(envObj.PORT);
1428
- const apiBaseHost = options?.apiBaseHost ? options.apiBaseHost : `localhost:${envObj.PORT + 1}`;
1487
+ const apiBaseHost = devProxy
1488
+ ? devProxyHostFactory({ host, tls: isTlsDevProxy() })
1489
+ : options?.apiBaseHost
1490
+ ? options.apiBaseHost
1491
+ : `localhost:${envObj.PORT + 1}`;
1429
1492
  confServer[host][path].apiBaseHost = apiBaseHost;
1430
1493
  confServer[host][path].apiBaseProxyPath = path;
1431
- logger.info('Build client static conf', { host, path, apiBaseHost });
1494
+ logger.warn('Build client static conf', { host, path, apiBaseHost });
1432
1495
  envObj.PORT = parseInt(confServer[host][path].origins[0].split(':')[2]) - 1;
1433
1496
  writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client`, envObj);
1434
1497
  fs.writeFileSync(
@@ -1448,6 +1511,56 @@ const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiB
1448
1511
  */
1449
1512
  const isDeployRunnerContext = (path, options) => !options.build && path && path !== 'template-deploy';
1450
1513
 
1514
+ /**
1515
+ * @method isDevProxyContext
1516
+ * @description Checks if the dev proxy context is valid.
1517
+ * @returns {boolean} - The dev proxy context.
1518
+ * @memberof ServerConfBuilder
1519
+ */
1520
+ const isDevProxyContext = () => {
1521
+ try {
1522
+ if (process.argv[2] === 'proxy') return true;
1523
+ if (!process.argv[6].startsWith('localhost')) return false;
1524
+ return new URL('http://' + process.argv[6]).hostname ? true : false;
1525
+ } catch {
1526
+ return false;
1527
+ }
1528
+ };
1529
+
1530
+ /**
1531
+ * @method devProxyHostFactory
1532
+ * @description Creates the dev proxy host.
1533
+ * @param {object} options - The options.
1534
+ * @param {string} [options.host='default.net'] - The host.
1535
+ * @param {boolean} [options.includeHttp=false] - Whether to include HTTP.
1536
+ * @param {number} [options.port=443] - The port.
1537
+ * @param {boolean} [options.tls=false] - Whether to use TLS.
1538
+ * @returns {string} - The dev proxy host.
1539
+ * @memberof ServerConfBuilder
1540
+ */
1541
+ const devProxyHostFactory = (options = { host: 'default.net', includeHttp: false, port: 80, tls: false }) =>
1542
+ `${options.includeHttp ? (options.tls ? 'https://' : 'http://') : ''}${options.host ? options.host : 'localhost'}:${
1543
+ (options.port ? options.port : options.tls ? 443 : 80) + parseInt(process.env.DEV_PROXY_PORT_OFFSET)
1544
+ }`;
1545
+
1546
+ /**
1547
+ * @method isTlsDevProxy
1548
+ * @description Checks if TLS is used in the dev proxy.
1549
+ * @returns {boolean} - The TLS dev proxy status.
1550
+ * @memberof ServerConfBuilder
1551
+ */
1552
+ const isTlsDevProxy = () => process.env.NODE_ENV !== 'production' && process.argv[7] === 'tls';
1553
+
1554
+ /**
1555
+ * @method getTlsHosts
1556
+ * @description Gets the TLS hosts.
1557
+ * @param {object} confServer - The server configuration.
1558
+ * @returns {Array} - The TLS hosts.
1559
+ * @memberof ServerConfBuilder
1560
+ */
1561
+ const getTlsHosts = (confServer) =>
1562
+ Array.from(new Set(Object.keys(confServer).map((h) => new URL('https://' + h).hostname)));
1563
+
1451
1564
  export {
1452
1565
  Cmd,
1453
1566
  Config,
@@ -1484,4 +1597,8 @@ export {
1484
1597
  buildApiConf,
1485
1598
  buildClientStaticConf,
1486
1599
  isDeployRunnerContext,
1600
+ isDevProxyContext,
1601
+ devProxyHostFactory,
1602
+ isTlsDevProxy,
1603
+ getTlsHosts,
1487
1604
  };
@@ -11,9 +11,12 @@ import dotenv from 'dotenv';
11
11
 
12
12
  import { createProxyMiddleware } from 'http-proxy-middleware';
13
13
  import { loggerFactory, loggerMiddleware } from './logger.js';
14
- import { TLS } from './tls.js';
15
- import { buildPortProxyRouter, buildProxyRouter } from './conf.js';
14
+ import { buildPortProxyRouter, buildProxyRouter, getTlsHosts, isDevProxyContext, isTlsDevProxy } from './conf.js';
16
15
  import UnderpostStartUp from './start.js';
16
+ import UnderpostDeploy from '../cli/deploy.js';
17
+ import { SSL_BASE, TLS } from './tls.js';
18
+ import { shellExec } from './process.js';
19
+ import fs from 'fs-extra';
17
20
 
18
21
  dotenv.config();
19
22
 
@@ -22,28 +25,27 @@ const logger = loggerFactory(import.meta);
22
25
  /**
23
26
  * Main class for building and running the proxy server.
24
27
  * All utility methods are implemented as static to serve as a namespace container.
25
- * @class Proxy
26
- * @augments Proxy
28
+ * @class ProxyService
27
29
  * @memberof ProxyService
28
30
  */
29
- class Proxy {
31
+ class ProxyService {
30
32
  /**
31
- * Initializes and starts the reverse proxy server for all configured ports and hosts.
32
- * @async
33
+ * Builds and starts the proxy server with appropriate routing and SSL configuration.
33
34
  * @static
34
- * @memberof ProxyService
35
- * @returns {Promise<void>}
36
- * @memberof ProxyService
35
+ * @returns {Promise<void>} Resolves when the server is successfully started.
37
36
  */
38
- static async buildProxy() {
37
+ static async build() {
38
+ if (process.env.NODE_ENV === 'production') process.env.DEV_PROXY_PORT_OFFSET = 0;
39
+
39
40
  // Start a default Express listener on process.env.PORT (potentially unused, but ensures Express is initialized)
41
+ process.env.PORT = parseInt(process.env.PORT) + parseInt(process.env.DEV_PROXY_PORT_OFFSET);
40
42
  express().listen(process.env.PORT);
41
43
 
42
44
  const proxyRouter = buildProxyRouter();
43
45
 
44
46
  for (let port of Object.keys(proxyRouter)) {
45
- port = parseInt(port);
46
47
  const hosts = proxyRouter[port];
48
+ port = parseInt(port) + parseInt(process.env.DEV_PROXY_PORT_OFFSET);
47
49
  const proxyPath = '/';
48
50
  const proxyHost = 'localhost';
49
51
  const runningData = { host: proxyHost, path: proxyPath, client: null, runtime: 'nodejs', meta: import.meta };
@@ -56,19 +58,22 @@ class Proxy {
56
58
  /** @type {import('http-proxy-middleware/dist/types').Options} */
57
59
  const options = {
58
60
  ws: true, // Enable websocket proxying
59
- target: `http://localhost:${process.env.PORT}`, // Default target (should be overridden by router)
61
+ target: `http://localhost:${parseInt(process.env.PORT - 1)}`, // Default target (should be overridden by router)
60
62
  router: {},
63
+ // changeOrigin: true,
64
+ logLevel: 'debug',
61
65
  xfwd: true, // Adds x-forward headers (Host, Proto, etc.)
62
- onProxyReq: (proxyReq, req, res, options) => {
63
- // Use the static method from the TLS class for redirection logic
64
- TLS.sslRedirectMiddleware(req, res, port, proxyRouter);
65
- },
66
- pathRewrite: {
67
- // Add path rewrite rules here if necessary
68
- },
66
+ onProxyReq: (proxyReq, req, res, options) => {},
67
+ pathRewrite: {},
69
68
  };
70
69
 
71
- options.router = buildPortProxyRouter(port, proxyRouter, { orderByPathLength: true });
70
+ options.router = buildPortProxyRouter({
71
+ port,
72
+ proxyRouter,
73
+ hosts,
74
+ orderByPathLength: true,
75
+ devProxyContext: process.env.NODE_ENV !== 'production',
76
+ });
72
77
 
73
78
  const filter = proxyPath; // Use '/' as the general filter
74
79
  app.use(proxyPath, createProxyMiddleware(filter, options));
@@ -91,11 +96,33 @@ class Proxy {
91
96
  break;
92
97
 
93
98
  default:
94
- // In non-production, always use standard HTTP listener
95
- await UnderpostStartUp.API.listenPortController(app, port, runningData);
96
- break;
99
+ switch (port) {
100
+ case 443: {
101
+ let tlsHosts = hosts;
102
+ if (isDevProxyContext() && isTlsDevProxy()) {
103
+ tlsHosts = {};
104
+ for (const tlsHost of getTlsHosts(hosts)) {
105
+ if (fs.existsSync(SSL_BASE(tlsHost))) fs.removeSync(SSL_BASE(tlsHost));
106
+ if (!TLS.validateSecureContext(tlsHost)) shellExec(`node bin/deploy tls "${tlsHost}"`);
107
+ tlsHosts[tlsHost] = {};
108
+ }
109
+ }
110
+ const { ServerSSL } = await TLS.createSslServer(app, tlsHosts);
111
+ await UnderpostStartUp.API.listenPortController(ServerSSL, port, runningData);
112
+ break;
113
+ }
114
+ default: // In non-production, always use standard HTTP listener
115
+ await UnderpostStartUp.API.listenPortController(app, port, runningData);
116
+ break;
117
+ }
97
118
  }
98
119
  logger.info('Proxy running', { port, options });
120
+ if (process.env.NODE_ENV === 'development')
121
+ logger.info(
122
+ UnderpostDeploy.API.etcHostFactory(Object.keys(options.router), {
123
+ append: true,
124
+ }).renderHosts,
125
+ );
99
126
  }
100
127
  }
101
128
  }
@@ -105,6 +132,6 @@ class Proxy {
105
132
  * @type {function(): Promise<void>}
106
133
  * @memberof ProxyService
107
134
  */
108
- const buildProxy = Proxy.buildProxy;
135
+ const buildProxy = ProxyService.build;
109
136
 
110
- export { Proxy, buildProxy };
137
+ export { ProxyService, buildProxy };
@@ -53,7 +53,7 @@ class UnderpostStartUp {
53
53
  throw new Error(message);
54
54
  }
55
55
  }, msDelta);
56
- return logic ? await logic(...args) : undefined, args[1]();
56
+ return (logic ? await logic(...args) : undefined, args[1]());
57
57
  },
58
58
  };
59
59
  },
@@ -95,8 +95,8 @@ class UnderpostStartUp {
95
95
  port === 80
96
96
  ? `http://${host}${path}`
97
97
  : port === 443
98
- ? `https://${host}${path}`
99
- : `http://${host}:${port}${path}`,
98
+ ? `https://${host}${path}`
99
+ : `http://${host}:${port}${path}`,
100
100
  local: `http://localhost:${port}${path}`,
101
101
  apis: metadata.apis,
102
102
  };
@@ -158,4 +158,14 @@ class UnderpostStartUp {
158
158
  };
159
159
  }
160
160
 
161
+ /**
162
+ * Creates a keep-alive process to maintain server activity.
163
+ * @memberof UnderpostStartUp
164
+ * @returns
165
+ */
166
+ const createKeepAliveProcess = async () =>
167
+ await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':');
168
+
161
169
  export default UnderpostStartUp;
170
+
171
+ export { createKeepAliveProcess, UnderpostStartUp };
package/src/server/tls.js CHANGED
@@ -248,4 +248,4 @@ const validateSecureContext = TLS.validateSecureContext;
248
248
  const createSslServer = TLS.createSslServer;
249
249
  const sslRedirectMiddleware = TLS.sslRedirectMiddleware;
250
250
 
251
- export { TLS, buildSSL, buildSecureContext, validateSecureContext, createSslServer, sslRedirectMiddleware };
251
+ export { TLS, SSL_BASE, buildSSL, buildSecureContext, validateSecureContext, createSslServer, sslRedirectMiddleware };