underpost 2.85.1 → 2.89.0

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.
Files changed (53) hide show
  1. package/.env.development +2 -1
  2. package/.env.production +2 -1
  3. package/.env.test +2 -1
  4. package/.github/workflows/release.cd.yml +3 -3
  5. package/.vscode/zed.keymap.json +22 -0
  6. package/README.md +3 -3
  7. package/bin/build.js +8 -10
  8. package/bin/deploy.js +4 -2
  9. package/bin/file.js +4 -0
  10. package/bin/vs.js +4 -4
  11. package/cli.md +16 -11
  12. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  13. package/manifests/deployment/dd-test-development/deployment.yaml +50 -50
  14. package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
  15. package/package.json +2 -2
  16. package/src/api/file/file.service.js +29 -3
  17. package/src/cli/baremetal.js +4 -5
  18. package/src/cli/deploy.js +26 -4
  19. package/src/cli/index.js +8 -3
  20. package/src/cli/repository.js +42 -45
  21. package/src/cli/run.js +217 -48
  22. package/src/client/components/core/AgGrid.js +42 -3
  23. package/src/client/components/core/CommonJs.js +5 -0
  24. package/src/client/components/core/Css.js +95 -48
  25. package/src/client/components/core/CssCore.js +0 -1
  26. package/src/client/components/core/LoadingAnimation.js +2 -2
  27. package/src/client/components/core/Logger.js +2 -9
  28. package/src/client/components/core/Modal.js +22 -14
  29. package/src/client/components/core/ObjectLayerEngine.js +300 -9
  30. package/src/client/components/core/ObjectLayerEngineModal.js +686 -148
  31. package/src/client/components/core/ObjectLayerEngineViewer.js +1061 -0
  32. package/src/client/components/core/Pagination.js +15 -5
  33. package/src/client/components/core/Router.js +5 -1
  34. package/src/client/components/core/SocketIo.js +5 -1
  35. package/src/client/components/core/Translate.js +4 -0
  36. package/src/client/components/core/Worker.js +8 -1
  37. package/src/client/services/default/default.management.js +86 -16
  38. package/src/client/sw/default.sw.js +193 -97
  39. package/src/client.dev.js +1 -1
  40. package/src/db/mariadb/MariaDB.js +2 -2
  41. package/src/index.js +1 -1
  42. package/src/proxy.js +1 -1
  43. package/src/runtime/express/Express.js +4 -1
  44. package/src/server/auth.js +2 -1
  45. package/src/server/client-build.js +57 -2
  46. package/src/server/conf.js +132 -15
  47. package/src/server/object-layer.js +44 -0
  48. package/src/server/proxy.js +53 -26
  49. package/src/server/start.js +25 -3
  50. package/src/server/tls.js +1 -1
  51. package/src/ws/IoInterface.js +2 -3
  52. package/AUTHORS.md +0 -21
  53. package/src/server/network.js +0 -72
package/src/proxy.js CHANGED
@@ -11,7 +11,7 @@ import { Config } from './server/conf.js';
11
11
 
12
12
  dotenv.config();
13
13
 
14
- await Config.build();
14
+ await Config.build(process.argv[2], process.argv[3], process.argv[4]);
15
15
 
16
16
  const logger = loggerFactory(import.meta);
17
17
 
@@ -23,6 +23,7 @@ import { applySecurity, authMiddlewareFactory } from '../../server/auth.js';
23
23
  import { ssrMiddlewareFactory } from '../../server/ssr.js';
24
24
  import { TLS } from '../../server/tls.js';
25
25
  import { shellExec } from '../../server/process.js';
26
+ import { devProxyHostFactory, isDevProxyContext, isTlsDevProxy } from '../../server/conf.js';
26
27
 
27
28
  const logger = loggerFactory(import.meta);
28
29
 
@@ -94,7 +95,9 @@ class ExpressService {
94
95
 
95
96
  const app = express();
96
97
 
97
- if (process.env.NODE_ENV === 'production') app.set('trust proxy', true);
98
+ if (origins && isDevProxyContext())
99
+ origins.push(devProxyHostFactory({ host, includeHttp: true, tls: isTlsDevProxy() }));
100
+ app.set('trust proxy', true);
98
101
 
99
102
  app.use((req, res, next) => {
100
103
  res.on('finish', () => {
@@ -17,6 +17,7 @@ import slowDown from 'express-slow-down';
17
17
  import cors from 'cors';
18
18
  import cookieParser from 'cookie-parser';
19
19
  import { DataBaseProvider } from '../db/DataBaseProvider.js';
20
+ import { isDevProxyContext } from './conf.js';
20
21
 
21
22
  dotenv.config();
22
23
  const logger = loggerFactory(import.meta);
@@ -348,7 +349,7 @@ const cookieOptionsFactory = (req, host) => {
348
349
  secure,
349
350
  sameSite,
350
351
  path: '/',
351
- domain: process.env.NODE_ENV === 'production' ? host : 'localhost',
352
+ domain: process.env.NODE_ENV === 'production' || isDevProxyContext() ? host : 'localhost',
352
353
  maxAge,
353
354
  };
354
355
 
@@ -32,6 +32,51 @@ dotenv.config();
32
32
 
33
33
  // Static Site Generation (SSG)
34
34
 
35
+ /**
36
+ * Recursively copies files from source to destination, but only files that don't exist in destination.
37
+ * @function copyNonExistingFiles
38
+ * @param {string} src - Source directory path
39
+ * @param {string} dest - Destination directory path
40
+ * @returns {void}
41
+ * @memberof clientBuild
42
+ */
43
+ const copyNonExistingFiles = (src, dest) => {
44
+ // Ensure source exists
45
+ if (!fs.existsSync(src)) {
46
+ throw new Error(`Source directory does not exist: ${src}`);
47
+ }
48
+
49
+ // Get stats for source
50
+ const srcStats = fs.statSync(src);
51
+
52
+ // If source is a file, copy only if it doesn't exist in destination
53
+ if (srcStats.isFile()) {
54
+ if (!fs.existsSync(dest)) {
55
+ const destDir = dir.dirname(dest);
56
+ fs.mkdirSync(destDir, { recursive: true });
57
+ fs.copyFileSync(src, dest);
58
+ }
59
+ return;
60
+ }
61
+
62
+ // If source is a directory, create destination if it doesn't exist
63
+ if (srcStats.isDirectory()) {
64
+ if (!fs.existsSync(dest)) {
65
+ fs.mkdirSync(dest, { recursive: true });
66
+ }
67
+
68
+ // Read all items in source directory
69
+ const items = fs.readdirSync(src);
70
+
71
+ // Recursively process each item
72
+ for (const item of items) {
73
+ const srcPath = dir.join(src, item);
74
+ const destPath = dir.join(dest, item);
75
+ copyNonExistingFiles(srcPath, destPath);
76
+ }
77
+ }
78
+ };
79
+
35
80
  /**
36
81
  * @async
37
82
  * @function buildClient
@@ -83,6 +128,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
83
128
  * @param {string} options.publicClientId - Public client ID.
84
129
  * @param {boolean} options.iconsBuild - Whether to build icons.
85
130
  * @param {Object} options.metadata - Metadata for the client.
131
+ * @param {boolean} options.publicCopyNonExistingFiles - Whether to copy non-existing files from public directory.
86
132
  * @returns {Promise<void>} - Promise that resolves when the full build is complete.
87
133
  * @throws {Error} - If the full build fails.
88
134
  * @memberof clientBuild
@@ -98,6 +144,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
98
144
  publicClientId,
99
145
  iconsBuild,
100
146
  metadata,
147
+ publicCopyNonExistingFiles,
101
148
  }) => {
102
149
  logger.warn('Full build', rootClientPath);
103
150
 
@@ -169,11 +216,15 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
169
216
  fs.copySync(dist.styles, `${rootClientPath}${dist.public_styles_folder}`);
170
217
  }
171
218
  }
219
+
220
+ if (publicCopyNonExistingFiles)
221
+ copyNonExistingFiles(`./src/client/public/${publicCopyNonExistingFiles}`, rootClientPath);
172
222
  };
173
223
 
174
224
  // { srcBuildPath, publicBuildPath }
175
225
  const enableLiveRebuild =
176
226
  options && options.liveClientBuildPaths && options.liveClientBuildPaths.length > 0 ? true : false;
227
+ const isDevelopment = process.env.NODE_ENV === 'development';
177
228
 
178
229
  let currentPort = parseInt(process.env.PORT) + 1;
179
230
  for (const host of Object.keys(confServer)) {
@@ -205,7 +256,8 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
205
256
  } = confServer[host][path];
206
257
  if (singleReplica) continue;
207
258
  if (!confClient[client]) confClient[client] = {};
208
- const { components, dists, views, services, metadata, publicRef } = confClient[client];
259
+ const { components, dists, views, services, metadata, publicRef, publicCopyNonExistingFiles } =
260
+ confClient[client];
209
261
  let backgroundImage;
210
262
  if (metadata) {
211
263
  backgroundImage = metadata.backgroundImage;
@@ -240,6 +292,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
240
292
  publicClientId,
241
293
  iconsBuild,
242
294
  metadata,
295
+ publicCopyNonExistingFiles,
243
296
  });
244
297
 
245
298
  if (components)
@@ -520,6 +573,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
520
573
  apiBaseHost,
521
574
  apiBasePath: process.env.BASE_API,
522
575
  version: Underpost.version,
576
+ dev: isDevelopment,
523
577
  },
524
578
  renderApi: {
525
579
  JSONweb,
@@ -613,6 +667,7 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
613
667
  apiBaseHost,
614
668
  apiBasePath: process.env.BASE_API,
615
669
  version: Underpost.version,
670
+ dev: isDevelopment,
616
671
  },
617
672
  renderApi: {
618
673
  JSONweb,
@@ -687,4 +742,4 @@ ${fs.readFileSync(`${rootClientPath}/sw.js`, 'utf8')}`,
687
742
  }
688
743
  };
689
744
 
690
- export { buildClient };
745
+ export { buildClient, copyNonExistingFiles };
@@ -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
  };
@@ -192,6 +192,49 @@ export class ObjectLayerEngine {
192
192
  return objectLayerFrameDirections;
193
193
  }
194
194
 
195
+ /**
196
+ * @memberof CyberiaObjectLayer
197
+ * @static
198
+ * @description Processes an image file through frameFactory and adds the resulting frame to the render data structure.
199
+ * Updates the color palette and pushes the frame to all keyframe directions corresponding to the given direction code.
200
+ * Initializes colors array, frames object, and direction arrays if they don't exist.
201
+ * @param {Object} renderData - The render data object containing frames and colors.
202
+ * @param {string} imagePath - The path to the image file to process.
203
+ * @param {string} directionCode - The numerical direction code (e.g., '08', '14').
204
+ * @returns {Promise<Object>} - The updated render data object.
205
+ * @memberof CyberiaObjectLayer
206
+ */
207
+ static async processAndPushFrame(renderData, imagePath, directionCode) {
208
+ // Initialize colors array if it doesn't exist
209
+ if (!renderData.colors) {
210
+ renderData.colors = [];
211
+ }
212
+
213
+ // Initialize frames object if it doesn't exist
214
+ if (!renderData.frames) {
215
+ renderData.frames = {};
216
+ }
217
+
218
+ // Process the image and extract frame matrix and updated colors
219
+ const frameFactoryResult = await ObjectLayerEngine.frameFactory(imagePath, renderData.colors);
220
+
221
+ // Update the colors palette
222
+ renderData.colors = frameFactoryResult.colors;
223
+
224
+ // Get all keyframe directions for this direction code
225
+ const keyframeDirections = ObjectLayerEngine.getKeyFramesDirectionsFromNumberFolderDirection(directionCode);
226
+
227
+ // Push the frame to all corresponding directions
228
+ for (const keyframeDirection of keyframeDirections) {
229
+ if (!renderData.frames[keyframeDirection]) {
230
+ renderData.frames[keyframeDirection] = [];
231
+ }
232
+ renderData.frames[keyframeDirection].push(frameFactoryResult.frame);
233
+ }
234
+
235
+ return renderData;
236
+ }
237
+
195
238
  /**
196
239
  * @memberof CyberiaObjectLayer
197
240
  * @static
@@ -290,5 +333,6 @@ export const readPngAsync = ObjectLayerEngine.readPngAsync;
290
333
  export const frameFactory = ObjectLayerEngine.frameFactory;
291
334
  export const getKeyFramesDirectionsFromNumberFolderDirection =
292
335
  ObjectLayerEngine.getKeyFramesDirectionsFromNumberFolderDirection;
336
+ export const processAndPushFrame = ObjectLayerEngine.processAndPushFrame;
293
337
  export const buildImgFromTile = ObjectLayerEngine.buildImgFromTile;
294
338
  export const generateRandomStats = ObjectLayerEngine.generateRandomStats;
@@ -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
  };
@@ -122,6 +122,12 @@ class UnderpostStartUp {
122
122
  if (options.build === true) await UnderpostStartUp.API.build(deployId, env);
123
123
  if (options.run === true) await UnderpostStartUp.API.run(deployId, env);
124
124
  },
125
+ /**
126
+ * Run itc-scripts and builds client bundle.
127
+ * @param {string} deployId - The ID of the deployment.
128
+ * @param {string} env - The environment of the deployment.
129
+ * @memberof UnderpostStartUp
130
+ */
125
131
  async build(deployId = 'dd-default', env = 'development') {
126
132
  const buildBasePath = `/home/dd`;
127
133
  const repoName = `engine-${deployId.split('-')[1]}`;
@@ -139,6 +145,12 @@ class UnderpostStartUp {
139
145
  }
140
146
  shellExec(`node bin/deploy build-full-client ${deployId}`);
141
147
  },
148
+ /**
149
+ * Runs a deployment.
150
+ * @param {string} deployId - The ID of the deployment.
151
+ * @param {string} env - The environment of the deployment.
152
+ * @memberof UnderpostStartUp
153
+ */
142
154
  async run(deployId = 'dd-default', env = 'development') {
143
155
  const runCmd = env === 'production' ? 'run prod-img' : 'run dev-img';
144
156
  if (fs.existsSync(`./engine-private/replica`)) {
@@ -158,4 +170,14 @@ class UnderpostStartUp {
158
170
  };
159
171
  }
160
172
 
173
+ /**
174
+ * Creates a keep-alive process to maintain server activity.
175
+ * @memberof UnderpostStartUp
176
+ * @returns
177
+ */
178
+ const createKeepAliveProcess = async () =>
179
+ await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':');
180
+
161
181
  export default UnderpostStartUp;
182
+
183
+ 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 };
@@ -21,11 +21,10 @@ const logger = loggerFactory(import.meta);
21
21
  */
22
22
 
23
23
  /**
24
+ * Manages the logic, client map, and event listeners for a specific WebSocket channel,
25
+ * ensuring robust message handling and lifecycle management.
24
26
  * @class IoChannel
25
- * @alias IoChannel
26
27
  * @memberof SocketIoInterface
27
- * @classdesc Manages the logic, client map, and event listeners for a specific WebSocket channel,
28
- * ensuring robust message handling and lifecycle management.
29
28
  */
30
29
  class IoChannel {
31
30
  /**