@underpostnet/underpost 2.8.5 → 2.8.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.
Files changed (109) hide show
  1. package/.github/workflows/ghpkg.yml +1 -1
  2. package/.github/workflows/npmpkg.yml +1 -1
  3. package/.github/workflows/pwa-microservices-template.page.yml +1 -1
  4. package/.vscode/extensions.json +3 -2
  5. package/.vscode/settings.json +6 -0
  6. package/CHANGELOG.md +44 -0
  7. package/Dockerfile +9 -10
  8. package/README.md +39 -2
  9. package/bin/build.js +31 -6
  10. package/bin/deploy.js +1404 -202
  11. package/bin/file.js +8 -0
  12. package/bin/hwt.js +0 -10
  13. package/bin/index.js +1 -187
  14. package/bin/util.js +0 -7
  15. package/bin/vs.js +1 -0
  16. package/cli.md +451 -0
  17. package/conf.js +0 -2
  18. package/docker-compose.yml +1 -1
  19. package/jsdoc.json +1 -1
  20. package/manifests/calico-custom-resources.yaml +25 -0
  21. package/manifests/deployment/adminer/deployment.yaml +32 -0
  22. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  23. package/manifests/deployment/adminer/service.yaml +13 -0
  24. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  25. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  26. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  27. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  28. package/manifests/deployment/kafka/deployment.yaml +69 -0
  29. package/manifests/kind-config-dev.yaml +12 -0
  30. package/manifests/kubeadm-calico-config.yaml +119 -0
  31. package/manifests/mongodb/kustomization.yaml +2 -2
  32. package/manifests/mongodb-4.4/kustomization.yaml +7 -0
  33. package/manifests/mongodb-4.4/service-deployment.yaml +63 -0
  34. package/manifests/postgresql/configmap.yaml +9 -0
  35. package/manifests/postgresql/kustomization.yaml +10 -0
  36. package/manifests/postgresql/pv.yaml +15 -0
  37. package/manifests/postgresql/pvc.yaml +13 -0
  38. package/manifests/{core/underpost-engine-headless-service.yaml → postgresql/service.yaml} +3 -3
  39. package/manifests/postgresql/statefulset.yaml +37 -0
  40. package/manifests/valkey/statefulset.yaml +6 -4
  41. package/package.json +10 -14
  42. package/src/api/default/default.service.js +1 -1
  43. package/src/api/user/user.service.js +14 -11
  44. package/src/cli/cluster.js +298 -63
  45. package/src/cli/cron.js +39 -8
  46. package/src/cli/db.js +118 -44
  47. package/src/cli/deploy.js +312 -102
  48. package/src/cli/env.js +9 -3
  49. package/src/cli/fs.js +161 -0
  50. package/src/cli/image.js +45 -104
  51. package/src/cli/index.js +312 -0
  52. package/src/cli/monitor.js +236 -0
  53. package/src/cli/repository.js +26 -2
  54. package/src/cli/script.js +25 -1
  55. package/src/cli/test.js +39 -4
  56. package/src/client/components/core/Account.js +28 -24
  57. package/src/client/components/core/Blockchain.js +1 -1
  58. package/src/client/components/core/CalendarCore.js +14 -73
  59. package/src/client/components/core/CommonJs.js +54 -2
  60. package/src/client/components/core/Css.js +0 -1
  61. package/src/client/components/core/CssCore.js +10 -4
  62. package/src/client/components/core/Docs.js +1 -2
  63. package/src/client/components/core/EventsUI.js +3 -3
  64. package/src/client/components/core/FileExplorer.js +86 -78
  65. package/src/client/components/core/Input.js +4 -2
  66. package/src/client/components/core/JoyStick.js +2 -2
  67. package/src/client/components/core/LoadingAnimation.js +3 -12
  68. package/src/client/components/core/LogIn.js +3 -3
  69. package/src/client/components/core/LogOut.js +1 -1
  70. package/src/client/components/core/Modal.js +44 -14
  71. package/src/client/components/core/Panel.js +26 -66
  72. package/src/client/components/core/PanelForm.js +22 -15
  73. package/src/client/components/core/Recover.js +3 -3
  74. package/src/client/components/core/RichText.js +1 -11
  75. package/src/client/components/core/Router.js +3 -1
  76. package/src/client/components/core/SignUp.js +2 -2
  77. package/src/client/components/default/RoutesDefault.js +3 -2
  78. package/src/client/services/core/core.service.js +15 -10
  79. package/src/client/services/default/default.management.js +45 -38
  80. package/src/client/ssr/Render.js +6 -1
  81. package/src/client/ssr/body/CacheControl.js +2 -3
  82. package/src/client/sw/default.sw.js +3 -3
  83. package/src/db/mongo/MongooseDB.js +17 -1
  84. package/src/index.js +25 -1
  85. package/src/mailer/MailerProvider.js +3 -0
  86. package/src/runtime/lampp/Dockerfile +65 -0
  87. package/src/server/backup.js +3 -3
  88. package/src/server/client-build.js +45 -23
  89. package/src/server/client-formatted.js +2 -1
  90. package/src/server/conf.js +110 -16
  91. package/src/server/dns.js +74 -43
  92. package/src/server/downloader.js +0 -8
  93. package/src/server/json-schema.js +77 -0
  94. package/src/server/network.js +7 -122
  95. package/src/server/peer.js +2 -2
  96. package/src/server/proxy.js +4 -4
  97. package/src/server/runtime.js +40 -12
  98. package/src/server/start.js +122 -0
  99. package/src/server/valkey.js +25 -11
  100. package/test/api.test.js +0 -8
  101. package/manifests/core/kustomization.yaml +0 -11
  102. package/manifests/core/underpost-engine-backup-access.yaml +0 -16
  103. package/manifests/core/underpost-engine-backup-pv-pvc.yaml +0 -22
  104. package/manifests/core/underpost-engine-mongodb-backup-cronjob.yaml +0 -40
  105. package/manifests/core/underpost-engine-mongodb-configmap.yaml +0 -26
  106. package/manifests/core/underpost-engine-statefulset.yaml +0 -91
  107. package/manifests/valkey/underpost-engine-valkey-service.yaml +0 -17
  108. package/manifests/valkey/underpost-engine-valkey-statefulset.yaml +0 -39
  109. /package/manifests/{core/underpost-engine-pv-pvc.yaml → mongodb-4.4/pv-pvc.yaml} +0 -0
package/src/index.js CHANGED
@@ -9,11 +9,14 @@ import UnderpostCron from './cli/cron.js';
9
9
  import UnderpostDB from './cli/db.js';
10
10
  import UnderpostDeploy from './cli/deploy.js';
11
11
  import UnderpostRootEnv from './cli/env.js';
12
+ import UnderpostFileStorage from './cli/fs.js';
12
13
  import UnderpostImage from './cli/image.js';
14
+ import UnderpostMonitor from './cli/monitor.js';
13
15
  import UnderpostRepository from './cli/repository.js';
14
16
  import UnderpostScript from './cli/script.js';
15
17
  import UnderpostSecret from './cli/secrets.js';
16
18
  import UnderpostTest from './cli/test.js';
19
+ import UnderpostStartUp from './server/start.js';
17
20
 
18
21
  /**
19
22
  * Underpost main module methods
@@ -27,7 +30,7 @@ class Underpost {
27
30
  * @type {String}
28
31
  * @memberof Underpost
29
32
  */
30
- static version = 'v2.8.5';
33
+ static version = 'v2.8.7';
31
34
  /**
32
35
  * Repository cli API
33
36
  * @static
@@ -49,6 +52,13 @@ class Underpost {
49
52
  * @memberof Underpost
50
53
  */
51
54
  static test = UnderpostTest.API;
55
+ /**
56
+ * Underpost Start Up cli API
57
+ * @static
58
+ * @type {UnderpostStartUp.API}
59
+ * @memberof Underpost
60
+ */
61
+ static start = UnderpostStartUp.API;
52
62
  /**
53
63
  * Cluster cli API
54
64
  * @static
@@ -98,6 +108,20 @@ class Underpost {
98
108
  * @memberof Underpost
99
109
  */
100
110
  static cron = UnderpostCron.API;
111
+ /**
112
+ * File Storage cli API
113
+ * @static
114
+ * @type {UnderpostFileStorage.API}
115
+ * @memberof Underpost
116
+ */
117
+ static fs = UnderpostFileStorage.API;
118
+ /**
119
+ * Monitor cli API
120
+ * @static
121
+ * @type {UnderpostMonitor.API}
122
+ * @memberof Underpost
123
+ */
124
+ static monitor = UnderpostMonitor.API;
101
125
  }
102
126
 
103
127
  const up = Underpost;
@@ -32,6 +32,9 @@ const MailerProvider = {
32
32
  },
33
33
  ) {
34
34
  try {
35
+ options.transport.tls = {
36
+ rejectUnauthorized: false,
37
+ };
35
38
  const { id } = options;
36
39
  // Generate test SMTP service account from ethereal.email
37
40
  // Only needed if you don't have a real mail account for testing
@@ -0,0 +1,65 @@
1
+ ARG BASE_DEBIAN=buster
2
+
3
+ USER root
4
+
5
+ FROM debian:${BASE_DEBIAN}
6
+
7
+ ENV DEBIAN_FRONTEND=noninteractive
8
+
9
+ # Set root password to root, format is 'user:password'.
10
+ RUN echo 'root:root' | chpasswd
11
+
12
+ RUN apt-get update --fix-missing
13
+ RUN apt-get upgrade -y
14
+ # install sudo
15
+ RUN apt-get -y install sudo
16
+ # net-tools provides netstat commands
17
+ RUN apt-get -y install curl net-tools
18
+ RUN apt-get -yq install openssh-server supervisor
19
+ # Few handy utilities which are nice to have
20
+ RUN apt-get -y install nano vim less --no-install-recommends
21
+ RUN apt-get clean
22
+
23
+ # install ssh
24
+ RUN mkdir -p /var/run/sshd
25
+ # Allow root login via password
26
+ RUN sed -ri 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
27
+
28
+ # install open ssl git and others tools
29
+ RUN apt-get install -yq --no-install-recommends libssl-dev curl wget git gnupg
30
+
31
+ # install lampp
32
+ RUN curl -Lo xampp-linux-installer.run https://sourceforge.net/projects/xampp/files/XAMPP%20Linux/7.4.33/xampp-linux-x64-7.4.33-0-installer.run?from_af=true
33
+ RUN chmod +x xampp-linux-installer.run
34
+ RUN bash -c './xampp-linux-installer.run'
35
+ RUN ln -sf /opt/lampp/lampp /usr/bin/lampp
36
+ # Enable XAMPP web interface(remove security checks)
37
+ RUN sed -i.bak s'/Require local/Require all granted/g' /opt/lampp/etc/extra/httpd-xampp.conf
38
+ # Enable error display in php
39
+ RUN sed -i.bak s'/display_errors=Off/display_errors=On/g' /opt/lampp/etc/php.ini
40
+ # Enable includes of several configuration files
41
+ RUN mkdir /opt/lampp/apache2/conf.d
42
+ RUN echo "IncludeOptional /opt/lampp/apache2/conf.d/*.conf" >>/opt/lampp/etc/httpd.conf
43
+ # Create a /www folder and a symbolic link to it in /opt/lampp/htdocs. It'll be accessible via http://localhost:[port]/www/
44
+ # This is convenient because it doesn't interfere with xampp, phpmyadmin or other tools in /opt/lampp/htdocs
45
+ # /opt/lampp/etc/httpd.conf
46
+ RUN mkdir /www
47
+ RUN ln -s /www /opt/lampp/htdocs
48
+
49
+ # install nodejs https://github.com/nodesource/distributions/blob/master/README.md#deb
50
+ RUN curl -fsSL https://deb.nodesource.com/setup_23.x | bash -
51
+ RUN apt-get install -y nodejs build-essential
52
+ RUN node --version
53
+ RUN npm --version
54
+
55
+ WORKDIR /home/dd
56
+
57
+ EXPOSE 22
58
+
59
+ EXPOSE 80
60
+
61
+ EXPOSE 443
62
+
63
+ EXPOSE 3000-3100
64
+
65
+ EXPOSE 4000-4100
@@ -9,7 +9,7 @@ dotenv.config();
9
9
  const logger = loggerFactory(import.meta);
10
10
 
11
11
  class BackUp {
12
- static callback = async function (deployList, options = { disableKindCluster: false }) {
12
+ static callback = async function (deployList, options = { itc: false, git: false }) {
13
13
  if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
14
14
  deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
15
15
 
@@ -25,8 +25,8 @@ class BackUp {
25
25
  const deployId = _deployId.trim();
26
26
  if (!deployId) continue;
27
27
 
28
- if (options.disableKindCluster !== true) {
29
- shellExec(`underpost db --export ${deployId}`);
28
+ if (!(options.itc === true)) {
29
+ shellExec(`node bin db ${options.git ? '--git ' : ''}--export ${deployId}`);
30
30
  continue;
31
31
  }
32
32
 
@@ -21,6 +21,7 @@ import swaggerAutoGen from 'swagger-autogen';
21
21
  import { SitemapStream, streamToPromise } from 'sitemap';
22
22
  import { Readable } from 'stream';
23
23
  import { buildIcons, buildTextImg, getBufferPngText } from './client-icons.js';
24
+ import Underpost from '../index.js';
24
25
 
25
26
  dotenv.config();
26
27
 
@@ -250,20 +251,6 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
250
251
  'services',
251
252
  baseHost,
252
253
  );
253
- if (module === 'core' && (process.env.NODE_ENV === 'production' || process.argv.includes('static'))) {
254
- if (apiBaseHost)
255
- jsSrc = jsSrc.replace(
256
- 'const getBaseHost = () => location.host;',
257
- `const getBaseHost = () => '${apiBaseHost}';`,
258
- );
259
- if (apiBaseProxyPath) {
260
- jsSrc = jsSrc.replace('${getProxyPath()}api/', `${apiBaseProxyPath}${process.env.BASE_API}/`);
261
- jsSrc = jsSrc.replace(
262
- "const getWsBasePath = () => (getProxyPath() !== '/' ? `${getProxyPath()}socket.io/` : undefined);",
263
- `const getWsBasePath = () => '${apiBaseProxyPath}socket.io/';`,
264
- );
265
- }
266
- }
267
254
  fs.writeFileSync(
268
255
  jsPublicPath,
269
256
  minifyBuild || process.env.NODE_ENV === 'production' ? UglifyJS.minify(jsSrc).code : jsSrc,
@@ -487,7 +474,13 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
487
474
  }
488
475
 
489
476
  default:
490
- ssrBodyComponents += SrrComponent({ ssrPath, host, path, ttiLoadTimeLimit });
477
+ ssrBodyComponents += SrrComponent({
478
+ ssrPath,
479
+ host,
480
+ path,
481
+ ttiLoadTimeLimit,
482
+ version: Underpost.version,
483
+ });
491
484
  break;
492
485
  }
493
486
  }
@@ -507,6 +500,15 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
507
500
  ssrPath,
508
501
  ssrHeadComponents,
509
502
  ssrBodyComponents,
503
+ renderPayload: {
504
+ apiBaseProxyPath,
505
+ apiBaseHost,
506
+ apiBasePath: process.env.BASE_API,
507
+ version: Underpost.version,
508
+ },
509
+ renderApi: {
510
+ JSONweb,
511
+ },
510
512
  });
511
513
 
512
514
  fs.writeFileSync(
@@ -681,6 +683,19 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
681
683
  root file where the route starts, such as index.js, app.js, routes.js, etc ... */
682
684
 
683
685
  await swaggerAutoGen({ openapi: '3.0.0' })(outputFile, routes, doc);
686
+
687
+ const htmlFiles = await fs.readdir(`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}`);
688
+ for (const htmlFile of htmlFiles) {
689
+ if (htmlFile.match('.html')) {
690
+ fs.writeFileSync(
691
+ `./public/${host}/docs/engine/${Underpost.version.replace('v', '')}/${htmlFile}`,
692
+ fs
693
+ .readFileSync(`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}/${htmlFile}`, 'utf8')
694
+ .replaceAll('Tutorials', 'References'),
695
+ 'utf8',
696
+ );
697
+ }
698
+ }
684
699
  }
685
700
 
686
701
  if (client) {
@@ -707,6 +722,15 @@ root file where the route starts, such as index.js, app.js, routes.js, etc ... *
707
722
  ssrPath,
708
723
  ssrHeadComponents: '',
709
724
  ssrBodyComponents: SsrComponent(),
725
+ renderPayload: {
726
+ apiBaseProxyPath,
727
+ apiBaseHost,
728
+ apiBasePath: process.env.BASE_API,
729
+ version: Underpost.version,
730
+ },
731
+ renderApi: {
732
+ JSONweb,
733
+ },
710
734
  });
711
735
 
712
736
  const buildPath = `${
@@ -739,16 +763,14 @@ root file where the route starts, such as index.js, app.js, routes.js, etc ... *
739
763
  }
740
764
 
741
765
  {
742
- const PRE_CACHED_JSON = `PRE_CACHED_RESOURCES = ${JSONweb(uniqueArray(PRE_CACHED_RESOURCES))}`;
743
- const PROXY_PATH = `PROXY_PATH = '${path}'`;
766
+ const renderPayload = {
767
+ PRE_CACHED_RESOURCES: uniqueArray(PRE_CACHED_RESOURCES),
768
+ PROXY_PATH: path,
769
+ };
744
770
  fs.writeFileSync(
745
771
  `${rootClientPath}/sw.js`,
746
- fs
747
- .readFileSync(`${rootClientPath}/sw.js`, 'utf8')
748
- .replaceAll(`PRE_CACHED_RESOURCES = []`, PRE_CACHED_JSON)
749
- .replaceAll(`PRE_CACHED_RESOURCES=[]`, PRE_CACHED_JSON)
750
- .replaceAll(`PROXY_PATH = '/'`, PROXY_PATH)
751
- .replaceAll(`PROXY_PATH='/'`, PROXY_PATH),
772
+ `self.renderPayload = ${JSONweb(renderPayload)};
773
+ ${fs.readFileSync(`${rootClientPath}/sw.js`, 'utf8')}`,
752
774
  'utf8',
753
775
  );
754
776
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import fs from 'fs-extra';
4
4
  import vm from 'node:vm';
5
+ import Underpost from '../index.js';
5
6
 
6
7
  const srcFormatted = (src) =>
7
8
  src
@@ -49,7 +50,7 @@ const viewFormatted = (src, dists, proxyPath, baseHost = '') => {
49
50
  };
50
51
 
51
52
  const ssrFactory = async (componentPath = `./src/client/ssr/Render.js`) => {
52
- const context = { SrrComponent: () => {}, npm_package_version: process.env.npm_package_version };
53
+ const context = { SrrComponent: () => {}, npm_package_version: Underpost.version };
53
54
  vm.createContext(context);
54
55
  vm.runInContext(await srcFormatted(fs.readFileSync(componentPath, 'utf8')), context);
55
56
  return context.SrrComponent;
@@ -19,17 +19,8 @@ import { DefaultConf } from '../../conf.js';
19
19
  import read from 'read';
20
20
  import splitFile from 'split-file';
21
21
  import axios from 'axios';
22
- import https from 'https';
23
22
  import { ssrFactory } from './client-formatted.js';
24
23
 
25
- // axios.defaults.baseURL = BASE_URL;
26
-
27
- // const httpsAgent = new https.Agent({
28
- // rejectUnauthorized: false,
29
- // });
30
-
31
- // axios.defaults.httpsAgent = httpsAgent;
32
-
33
24
  colors.enable();
34
25
 
35
26
  dotenv.config();
@@ -508,6 +499,40 @@ const buildProxyRouter = () => {
508
499
  return proxyRouter;
509
500
  };
510
501
 
502
+ const pathPortAssignmentFactory = (router, confServer) => {
503
+ const pathPortAssignmentData = {};
504
+ for (const host of Object.keys(confServer)) {
505
+ const pathPortAssignment = [];
506
+ for (const path of Object.keys(confServer[host])) {
507
+ const { peer } = confServer[host][path];
508
+ if (!router[`${host}${path === '/' ? '' : path}`]) continue;
509
+ const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
510
+ // logger.info('', { host, port, path });
511
+ pathPortAssignment.push({
512
+ port,
513
+ path,
514
+ });
515
+
516
+ if (peer) {
517
+ // logger.info('', { host, port: port + 1, path: '/peer' });
518
+ pathPortAssignment.push({
519
+ port: port + 1,
520
+ path: '/peer',
521
+ });
522
+ }
523
+ }
524
+ pathPortAssignmentData[host] = pathPortAssignment;
525
+ }
526
+ return pathPortAssignmentData;
527
+ };
528
+
529
+ const deployRangePortFactory = (router) => {
530
+ const ports = Object.values(router).map((p) => parseInt(p.split(':')[2]));
531
+ const fromPort = Math.min(...ports);
532
+ const toPort = Math.max(...ports);
533
+ return { ports, fromPort, toPort };
534
+ };
535
+
511
536
  const buildKindPorts = (from, to) =>
512
537
  range(parseInt(from), parseInt(to))
513
538
  .map(
@@ -598,7 +623,13 @@ const cliSpinner = async (time = 5000, message0, message1, color, type = 'dots')
598
623
  const buildReplicaId = ({ deployId, replica }) => `${deployId}-${replica.slice(1)}`;
599
624
 
600
625
  const getDataDeploy = (
601
- options = { buildSingleReplica: false, deployGroupId: '', deployId: '', disableSyncEnvPort: false },
626
+ options = {
627
+ buildSingleReplica: false,
628
+ deployGroupId: '',
629
+ deployId: '',
630
+ disableSyncEnvPort: false,
631
+ deployIdConcat: [],
632
+ },
602
633
  ) => {
603
634
  let dataDeploy =
604
635
  options.deployGroupId === 'dd'
@@ -610,6 +641,8 @@ const getDataDeploy = (
610
641
  .map((deployId) => deployId.trim())
611
642
  .filter((deployId) => deployId);
612
643
 
644
+ if (options.deployIdConcat) dataDeploy = dataDeploy.concat(options.deployIdConcat);
645
+
613
646
  if (options.deployId) dataDeploy = dataDeploy.filter((d) => d === options.deployId);
614
647
 
615
648
  dataDeploy = dataDeploy.map((deployId) => {
@@ -730,7 +763,7 @@ const validateTemplatePath = (absolutePath = '') => {
730
763
  return true;
731
764
  };
732
765
 
733
- const deployTest = async (dataDeploy) => {
766
+ const deployTest = async (dataDeploy = [{ deployId: 'default' }]) => {
734
767
  const failed = [];
735
768
  for (const deploy of dataDeploy) {
736
769
  const deployServerConfPath = fs.existsSync(`./engine-private/replica/${deploy.deployId}/conf.server.json`)
@@ -774,6 +807,12 @@ const deployTest = async (dataDeploy) => {
774
807
  return { failed };
775
808
  };
776
809
 
810
+ const awaitDeployMonitor = async (init = false, deltaMs = 1000) => {
811
+ if (init) fs.writeFileSync(`./tmp/await-deploy`, '', 'utf8');
812
+ await timer(deltaMs);
813
+ if (fs.existsSync(`./tmp/await-deploy`)) return await awaitDeployMonitor();
814
+ };
815
+
777
816
  const getDeployGroupId = () => {
778
817
  const deployGroupIndexArg = process.argv.findIndex((a) => a.match(`deploy-group:`));
779
818
  if (deployGroupIndexArg > -1) return process.argv[deployGroupIndexArg].split(':')[1].trim();
@@ -843,7 +882,7 @@ const deployRun = async (dataDeploy, currentAttempt = 1) => {
843
882
  if (failed.length > 0) {
844
883
  for (const deploy of failed) logger.error(deploy.deployId, Cmd.run(deploy.deployId));
845
884
  if (currentAttempt === maxAttempts) return logger.error(`max deploy attempts exceeded`);
846
- if (process.argv.includes('manual')) await read({ prompt: 'Press enter to retry failed processes\n' });
885
+ await read({ prompt: 'Press enter to retry failed processes\n' });
847
886
  currentAttempt++;
848
887
  await deployRun(failed, currentAttempt);
849
888
  } else logger.info(`Deploy process successfully`);
@@ -884,6 +923,53 @@ const mergeFile = async (parts = [], outputFilePath) => {
884
923
  });
885
924
  };
886
925
 
926
+ const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
927
+ const confServer = loadReplicas(
928
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
929
+ );
930
+ const hosts = {};
931
+ for (const host of Object.keys(confServer)) {
932
+ hosts[host] = {};
933
+ for (const path of Object.keys(confServer[host])) {
934
+ if (!confServer[host][path].db) continue;
935
+ const { singleReplica, replicas, db } = confServer[host][path];
936
+ const { provider } = db;
937
+ if (singleReplica) {
938
+ for (const replica of replicas) {
939
+ const deployIdReplica = buildReplicaId({ replica, deployId });
940
+ const confServerReplica = JSON.parse(
941
+ fs.readFileSync(`./engine-private/replica/${deployIdReplica}/conf.server.json`, 'utf8'),
942
+ );
943
+ for (const _host of Object.keys(confServerReplica)) {
944
+ for (const _path of Object.keys(confServerReplica[_host])) {
945
+ hosts[host][_path] = { replica: { host, path } };
946
+ confServerReplica[_host][_path].valkey = valkey;
947
+ switch (provider) {
948
+ case 'mongoose':
949
+ confServerReplica[_host][_path].db.host = mongo.host;
950
+ break;
951
+ }
952
+ }
953
+ }
954
+ fs.writeFileSync(
955
+ `./engine-private/replica/${deployIdReplica}/conf.server.json`,
956
+ JSON.stringify(confServerReplica, null, 4),
957
+ 'utf8',
958
+ );
959
+ }
960
+ } else hosts[host][path] = {};
961
+ confServer[host][path].valkey = valkey;
962
+ switch (provider) {
963
+ case 'mongoose':
964
+ confServer[host][path].db.host = mongo.host;
965
+ break;
966
+ }
967
+ }
968
+ }
969
+ fs.writeFileSync(`./engine-private/conf/${deployId}/conf.server.json`, JSON.stringify(confServer, null, 4), 'utf8');
970
+ return { hosts };
971
+ };
972
+
887
973
  const getRestoreCronCmd = async (options = { host: '', path: '', conf: {}, deployId: '' }) => {
888
974
  const { host, path, conf, deployId } = options;
889
975
  const { runtime, db, git, directory } = conf[host][path];
@@ -989,15 +1075,15 @@ const getPathsSSR = (conf) => {
989
1075
 
990
1076
  const Cmd = {
991
1077
  delete: (deployId) => `pm2 delete ${deployId}`,
992
- run: (deployId) => `node bin/deploy run ${deployId}`,
1078
+ run: () => `npm start`,
993
1079
  build: (deployId) => `node bin/deploy build-full-client ${deployId}${process.argv.includes('l') ? ' l' : ''}`,
994
1080
  conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
995
1081
  replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
996
1082
  syncPorts: (deployGroupId) => `node bin/deploy sync-env-port ${deployGroupId}`,
997
1083
  cron: (deployList, jobList, name, expression, options) =>
998
1084
  `pm2 start ./bin/index.js --no-autorestart --instances 1 --cron "${expression}" --name ${name} -- cron ${
999
- options?.disableKindCluster ? `--disable-kind-cluster ` : ''
1000
- }${deployList} ${jobList}`,
1085
+ options?.itc ? `--itc ` : ''
1086
+ }${options?.git ? `--git ` : ''}${deployList} ${jobList}`,
1001
1087
  };
1002
1088
 
1003
1089
  const fixDependencies = async () => {
@@ -1064,7 +1150,7 @@ const setUpProxyMaintenanceServer = ({ deployGroupId }) => {
1064
1150
  shellExec(`node bin/deploy valkey-service`);
1065
1151
  const proxyDeployId = fs.readFileSync(`./engine-private/deploy/${deployGroupId}.proxy`, 'utf8').trim();
1066
1152
  shellExec(`node bin/deploy conf ${proxyDeployId} production`);
1067
- shellExec(`node bin/deploy run ${proxyDeployId} maintenance`);
1153
+ shellExec(`npm start ${proxyDeployId} maintenance`);
1068
1154
  };
1069
1155
 
1070
1156
  const getNpmRootPath = () =>
@@ -1074,6 +1160,8 @@ const getNpmRootPath = () =>
1074
1160
  silent: true,
1075
1161
  }).trim();
1076
1162
 
1163
+ const getUnderpostRootPath = () => `${getNpmRootPath()}/underpost`;
1164
+
1077
1165
  const writeEnv = (envPath, envObj) =>
1078
1166
  fs.writeFileSync(
1079
1167
  envPath,
@@ -1119,5 +1207,11 @@ export {
1119
1207
  buildPortProxyRouter,
1120
1208
  splitFileFactory,
1121
1209
  getNpmRootPath,
1210
+ getUnderpostRootPath,
1122
1211
  writeEnv,
1212
+ deployTest,
1213
+ pathPortAssignmentFactory,
1214
+ deployRangePortFactory,
1215
+ awaitDeployMonitor,
1216
+ rebuildConfFactory,
1123
1217
  };
package/src/server/dns.js CHANGED
@@ -1,38 +1,59 @@
1
1
  import axios from 'axios';
2
2
  import dotenv from 'dotenv';
3
3
  import fs from 'fs';
4
- import https from 'https';
5
4
  import validator from 'validator';
6
- import { ip } from './network.js';
5
+ import { publicIp, publicIpv4, publicIpv6 } from 'public-ip';
7
6
  import { loggerFactory } from './logger.js';
7
+ import UnderpostRootEnv from '../cli/env.js';
8
+ import dns from 'node:dns';
9
+ import os from 'node:os';
8
10
  import { shellExec } from './process.js';
9
11
 
10
- const httpsAgent = new https.Agent({
11
- rejectUnauthorized: false,
12
- });
13
-
14
- axios.defaults.httpsAgent = httpsAgent;
15
-
16
12
  dotenv.config();
17
13
 
18
14
  const logger = loggerFactory(import.meta);
19
15
 
16
+ const ip = {
17
+ public: {
18
+ get: async () => await publicIp(), // => 'fe80::200:f8ff:fe21:67cf'
19
+ ipv4: async () => await publicIpv4(), // => '46.5.21.123'
20
+ ipv6: async () => await publicIpv6(), // => 'fe80::200:f8ff:fe21:67cf'
21
+ },
22
+ };
23
+
24
+ const isInternetConnection = (domain = 'google.com') =>
25
+ new Promise((resolve) => dns.lookup(domain, {}, (err) => resolve(err ? false : true)));
26
+
27
+ // export INTERFACE=$(ip route | grep default | cut -d ' ' -f 5)
28
+ // export IP_ADDRESS=$(ip -4 addr show dev $INTERFACE | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
29
+ const getLocalIPv4Address = () =>
30
+ os.networkInterfaces()[
31
+ shellExec(`ip route | grep default | cut -d ' ' -f 5`, {
32
+ stdout: true,
33
+ silent: true,
34
+ disableLog: true,
35
+ }).trim()
36
+ ].find((i) => i.family === 'IPv4').address;
37
+
20
38
  class Dns {
21
39
  static callback = async function (deployList) {
22
- // NAT-VPS modem/router device configuration:
23
- // LAN --> [NAT-VPS] --> WAN
40
+ // Network topology configuration:
41
+ // LAN -> [NAT-VPS](modem/router device) -> WAN
24
42
  // enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
25
- // disabled local red DHCP
43
+
44
+ // Enabling DHCP
45
+ // Navigate to Subnets > VLAN > Configure DHCP.
46
+ // Select the appropriate DHCP options (Managed or Relay).
47
+ // Save and apply changes.
48
+
26
49
  // verify inet ip proxy server address
27
50
  // DHCP (Dynamic Host Configuration Protocol) LAN reserver IP -> MAC ID
28
51
  // LAN server or device's local servers port -> 3000-3100 (2999-3101)
29
52
  // DNS Records: [ANAME](Address Dynamic) -> [A](ipv4) host | [AAAA](ipv6) host -> [public-ip]
30
53
  // Forward the router's TCP/UDP ports to the LAN device's IP address
31
- for (const _deployId of deployList.split(',')) {
32
- const deployId = _deployId.trim();
33
- const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
34
- const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
35
- const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
54
+ const isOnline = await isInternetConnection();
55
+
56
+ if (!isOnline) return;
36
57
 
37
58
  let testIp;
38
59
 
@@ -41,36 +62,44 @@ class Dns {
41
62
  } catch (error) {
42
63
  logger.error(error, { testIp, stack: error.stack });
43
64
  }
44
- const ipFileName = `${deployId}.ip`;
45
- const currentIp = fs.existsSync(`./engine-private/deploy/${ipFileName}`)
46
- ? fs.readFileSync(`./engine-private/deploy/${ipFileName}`, 'utf8')
47
- : undefined;
48
65
 
49
- if (testIp && typeof testIp === 'string' && validator.isIP(testIp) && currentIp !== testIp) {
66
+ const currentIp = UnderpostRootEnv.API.get('ip');
67
+
68
+ if (validator.isIP(testIp) && currentIp !== testIp) {
50
69
  logger.info(`new ip`, testIp);
51
- for (const recordType of Object.keys(confCronData.records)) {
52
- switch (recordType) {
53
- case 'A':
54
- for (const dnsProvider of confCronData.records[recordType]) {
55
- if (typeof Dns.services.updateIp[dnsProvider.dns] === 'function')
56
- await Dns.services.updateIp[dnsProvider.dns]({ ...dnsProvider, ip: testIp });
57
- }
58
- break;
59
-
60
- default:
61
- break;
70
+ UnderpostRootEnv.API.set('monitor-input', 'pause');
71
+
72
+ for (const _deployId of deployList.split(',')) {
73
+ const deployId = _deployId.trim();
74
+ const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
75
+ const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
76
+ const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
77
+ for (const recordType of Object.keys(confCronData.records)) {
78
+ switch (recordType) {
79
+ case 'A':
80
+ for (const dnsProvider of confCronData.records[recordType]) {
81
+ if (typeof Dns.services.updateIp[dnsProvider.dns] === 'function')
82
+ await Dns.services.updateIp[dnsProvider.dns]({ ...dnsProvider, ip: testIp });
83
+ }
84
+ break;
85
+
86
+ default:
87
+ break;
88
+ }
62
89
  }
63
- }
64
- try {
65
- const ipUrlTest = `https://${process.env.DEFAULT_DEPLOY_HOST}`;
66
- const response = await axios.get(ipUrlTest);
67
- const verifyIp = response.request.socket.remoteAddress;
68
- logger.info(ipUrlTest + ' IP', verifyIp);
69
- if (verifyIp === testIp) {
70
- fs.writeFileSync(`./engine-private/deploy/${ipFileName}`, testIp, 'utf8');
71
- } else logger.error('ip not updated');
72
- } catch (error) {
73
- logger.error(error), 'ip not updated';
90
+ try {
91
+ const ipUrlTest = `https://${process.env.DEFAULT_DEPLOY_HOST}`;
92
+ const response = await axios.get(ipUrlTest);
93
+ const verifyIp = response.request.socket.remoteAddress;
94
+ logger.info(ipUrlTest + ' verify ip', verifyIp);
95
+ if (verifyIp === testIp) {
96
+ logger.info('ip updated successfully', testIp);
97
+ UnderpostRootEnv.API.set('ip', testIp);
98
+ UnderpostRootEnv.API.delete('monitor-input');
99
+ } else logger.error('ip not updated', testIp);
100
+ } catch (error) {
101
+ logger.error(error, error.stack);
102
+ logger.error('ip not updated', testIp);
74
103
  }
75
104
  }
76
105
  }
@@ -101,3 +130,5 @@ class Dns {
101
130
  }
102
131
 
103
132
  export default Dns;
133
+
134
+ export { Dns, ip, isInternetConnection, getLocalIPv4Address };
@@ -2,16 +2,8 @@ import axios from 'axios';
2
2
  import fs from 'fs';
3
3
  import { loggerFactory } from './logger.js';
4
4
  import dotenv from 'dotenv';
5
- import https from 'https';
6
-
7
5
  dotenv.config();
8
6
 
9
- const httpsAgent = new https.Agent({
10
- rejectUnauthorized: false,
11
- });
12
-
13
- axios.defaults.httpsAgent = httpsAgent;
14
-
15
7
  const logger = loggerFactory(import.meta);
16
8
 
17
9
  const Downloader = (url, fullPath, options = { method: 'get', responseType: 'stream' }) =>