underpost 2.7.83 → 2.7.92

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 (85) hide show
  1. package/.github/workflows/ghpkg.yml +41 -1
  2. package/.github/workflows/pwa-microservices-template.page.yml +54 -0
  3. package/.vscode/settings.json +7 -0
  4. package/CHANGELOG.md +64 -16
  5. package/bin/cron.js +47 -0
  6. package/bin/db.js +60 -7
  7. package/bin/deploy.js +358 -26
  8. package/bin/file.js +18 -1
  9. package/bin/hwt.js +59 -0
  10. package/bin/index.js +1 -1
  11. package/bin/util.js +31 -1
  12. package/conf.js +46 -8
  13. package/docker-compose.yml +1 -1
  14. package/package.json +133 -133
  15. package/src/api/core/core.router.js +9 -9
  16. package/src/api/core/core.service.js +12 -4
  17. package/src/api/default/default.service.js +4 -4
  18. package/src/api/file/file.service.js +3 -3
  19. package/src/api/user/user.service.js +10 -8
  20. package/src/client/components/core/404.js +20 -0
  21. package/src/client/components/core/500.js +20 -0
  22. package/src/client/{ssr/common → components/core}/Alert.js +13 -11
  23. package/src/client/components/core/CommonJs.js +3 -0
  24. package/src/client/components/core/CssCore.js +30 -3
  25. package/src/client/components/core/Docs.js +110 -10
  26. package/src/client/components/core/LoadingAnimation.js +4 -2
  27. package/src/client/components/core/Modal.js +223 -22
  28. package/src/client/components/core/Panel.js +1 -1
  29. package/src/client/components/core/PanelForm.js +2 -1
  30. package/src/client/components/core/Responsive.js +34 -5
  31. package/src/client/components/core/RichText.js +4 -2
  32. package/src/client/components/core/Translate.js +21 -5
  33. package/src/client/components/core/VanillaJs.js +2 -1
  34. package/src/client/components/core/WebComponent.js +44 -0
  35. package/src/client/components/core/Worker.js +15 -18
  36. package/src/client/components/default/MenuDefault.js +68 -0
  37. package/src/client/components/default/RoutesDefault.js +2 -0
  38. package/src/client/public/default/plantuml/client-conf.svg +1 -1
  39. package/src/client/public/default/plantuml/client-schema.svg +1 -1
  40. package/src/client/public/default/plantuml/cron-conf.svg +1 -1
  41. package/src/client/public/default/plantuml/cron-schema.svg +1 -1
  42. package/src/client/public/default/plantuml/server-conf.svg +1 -1
  43. package/src/client/public/default/plantuml/server-schema.svg +1 -1
  44. package/src/client/public/default/plantuml/ssr-conf.svg +1 -1
  45. package/src/client/public/default/plantuml/ssr-schema.svg +1 -1
  46. package/src/client/public/default/site.webmanifest +69 -0
  47. package/src/client/ssr/Render.js +1 -6
  48. package/src/client/ssr/{components/body → body}/CacheControl.js +1 -1
  49. package/src/client/ssr/head/Production.js +1 -0
  50. package/src/client/ssr/head/Pwa.js +146 -0
  51. package/src/client/ssr/head/Seo.js +14 -0
  52. package/src/client/ssr/mailer/DefaultRecoverEmail.js +21 -0
  53. package/src/client/ssr/mailer/DefaultVerifyEmail.js +17 -0
  54. package/src/client/ssr/offline/NoNetworkConnection.js +65 -0
  55. package/src/client/ssr/pages/Test.js +196 -0
  56. package/src/client/ssr/pages/maintenance.js +14 -0
  57. package/src/client/ssr/pages/offline.js +21 -0
  58. package/src/client/sw/default.sw.js +44 -165
  59. package/src/db/DataBaseProvider.js +12 -1
  60. package/src/db/mongo/MongooseDB.js +0 -1
  61. package/src/mailer/EmailRender.js +2 -4
  62. package/src/mailer/MailerProvider.js +4 -1
  63. package/src/runtime/lampp/Lampp.js +9 -9
  64. package/src/server/backup.js +82 -70
  65. package/src/server/client-build.js +133 -155
  66. package/src/server/client-formatted.js +2 -4
  67. package/src/server/conf.js +114 -23
  68. package/src/server/crypto.js +91 -0
  69. package/src/server/dns.js +48 -16
  70. package/src/server/network.js +94 -7
  71. package/src/server/proxy.js +26 -28
  72. package/src/server/runtime.js +42 -12
  73. package/src/server/ssl.js +2 -2
  74. package/src/client/ssr/common/SsrCore.js +0 -91
  75. package/src/client/ssr/common/Translate.js +0 -26
  76. package/src/client/ssr/common/Worker.js +0 -28
  77. package/src/client/ssr/components/head/PwaDefault.js +0 -60
  78. package/src/client/ssr/offline/default.index.js +0 -31
  79. package/src/cron.js +0 -30
  80. package/src/server/cron.js +0 -35
  81. /package/src/client/ssr/{components/body → body}/DefaultSplashScreen.js +0 -0
  82. /package/src/client/ssr/{components/email → email}/DefaultRecoverEmail.js +0 -0
  83. /package/src/client/ssr/{components/email → email}/DefaultVerifyEmail.js +0 -0
  84. /package/src/client/ssr/{components/head → head}/Css.js +0 -0
  85. /package/src/client/ssr/{components/head → head}/DefaultScripts.js +0 -0
@@ -1,19 +1,20 @@
1
1
  import fs from 'fs-extra';
2
2
  import dotenv from 'dotenv';
3
- import { cap, capFirst, getCapVariableName, newInstance, range, timer } from '../client/components/core/CommonJs.js';
3
+ import { capFirst, getCapVariableName, newInstance, range, timer } from '../client/components/core/CommonJs.js';
4
4
  import * as dir from 'path';
5
5
  import cliProgress from 'cli-progress';
6
6
  import cliSpinners from 'cli-spinners';
7
7
  import logUpdate from 'log-update';
8
8
  import colors from 'colors';
9
9
  import { loggerFactory } from './logger.js';
10
- import { shellExec, shellCd } from './process.js';
10
+ import { shellExec } from './process.js';
11
11
  import { DefaultConf } from '../../conf.js';
12
12
  import ncp from 'copy-paste';
13
13
  import read from 'read';
14
14
  import splitFile from 'split-file';
15
15
  import axios from 'axios';
16
16
  import https from 'https';
17
+ import { ssrFactory } from './client-formatted.js';
17
18
 
18
19
  // axios.defaults.baseURL = BASE_URL;
19
20
 
@@ -44,7 +45,9 @@ const Config = {
44
45
  this.default.server = {};
45
46
  for (const deployId of process.argv[3].split(',')) {
46
47
  let confPath = `./engine-private/conf/${deployId}/conf.server.json`;
47
- const privateConfDevPath = `./engine-private/conf/${deployId}/conf.server.dev.${process.argv[4]}.json`;
48
+ const privateConfDevPath = fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
49
+ ? `./engine-private/replica/${deployId}/conf.server.json`
50
+ : `./engine-private/conf/${deployId}/conf.server.dev.${process.argv[4]}.json`;
48
51
  const confDevPath = fs.existsSync(privateConfDevPath)
49
52
  ? privateConfDevPath
50
53
  : `./engine-private/conf/${deployId}/conf.server.dev.json`;
@@ -89,6 +92,11 @@ const loadConf = (deployId) => {
89
92
  if (!fs.existsSync(`./conf`)) fs.mkdirSync(`./conf`);
90
93
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
91
94
  const isValidDeployId = fs.existsSync(`${folder}`);
95
+ if (!isValidDeployId) {
96
+ logger.info(`Save new deploy conf: '${deployId}'`);
97
+ shellExec(`node bin/deploy save ${deployId}`);
98
+ return loadConf(deployId);
99
+ }
92
100
  for (const typeConf of Object.keys(Config.default)) {
93
101
  let srcConf = isValidDeployId
94
102
  ? fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8')
@@ -100,7 +108,6 @@ const loadConf = (deployId) => {
100
108
  if (typeConf === 'server') srcConf = JSON.stringify(loadReplicas(JSON.parse(srcConf)), null, 4);
101
109
  fs.writeFileSync(`./conf/conf.${typeConf}.json`, srcConf, 'utf8');
102
110
  }
103
- if (!isValidDeployId) return {};
104
111
  fs.writeFileSync(`./.env.production`, fs.readFileSync(`${folder}/.env.production`, 'utf8'), 'utf8');
105
112
  fs.writeFileSync(`./.env.development`, fs.readFileSync(`${folder}/.env.development`, 'utf8'), 'utf8');
106
113
  fs.writeFileSync(`./.env.test`, fs.readFileSync(`${folder}/.env.test`, 'utf8'), 'utf8');
@@ -120,10 +127,16 @@ const loadReplicas = (confServer) => {
120
127
  for (const host of Object.keys(confServer)) {
121
128
  for (const path of Object.keys(confServer[host])) {
122
129
  const { replicas, singleReplica } = confServer[host][path];
123
- if (replicas && (process.argv[2] === 'proxy' || !singleReplica))
130
+ if (
131
+ replicas &&
132
+ (process.argv[2] === 'proxy' ||
133
+ !singleReplica ||
134
+ (singleReplica && process.env.NODE_ENV === 'development' && !process.argv[3]))
135
+ )
124
136
  for (const replicaPath of replicas) {
125
137
  confServer[host][replicaPath] = newInstance(confServer[host][path]);
126
138
  delete confServer[host][replicaPath].replicas;
139
+ delete confServer[host][replicaPath].singleReplica;
127
140
  }
128
141
  }
129
142
  }
@@ -246,8 +259,8 @@ const buildClientSrc = async (
246
259
  );
247
260
 
248
261
  fs.writeFileSync(
249
- `./src/client/${toClientVariableName}.js`,
250
- formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.js`, 'utf8')),
262
+ `./src/client/${toClientVariableName}.index.js`,
263
+ formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.index.js`, 'utf8')),
251
264
  'utf8',
252
265
  );
253
266
 
@@ -454,6 +467,16 @@ const buildProxyRouter = () => {
454
467
  }
455
468
  }
456
469
  }
470
+ if (process.argv.includes('maintenance'))
471
+ (async () => {
472
+ globalThis.defaultHtmlSrcMaintenance = (await ssrFactory())({
473
+ title: 'Site in maintenance',
474
+ ssrPath: '/',
475
+ ssrHeadComponents: '',
476
+ ssrBodyComponents: (await ssrFactory(`./src/client/ssr/body/Maintenance.js`))(),
477
+ });
478
+ })();
479
+
457
480
  return proxyRouter;
458
481
  };
459
482
 
@@ -584,14 +607,32 @@ const validateTemplatePath = (absolutePath = '') => {
584
607
  return false;
585
608
  }
586
609
  if (
587
- absolutePath.match('src/client/ssr/components/body') &&
588
- !confSsr.body.find((p) => absolutePath.match(`src/client/ssr/components/body/${p}.js`))
610
+ absolutePath.match('src/client/ssr/body') &&
611
+ !confSsr.body.find((p) => absolutePath.match(`src/client/ssr/body/${p}.js`))
612
+ ) {
613
+ return false;
614
+ }
615
+ if (
616
+ absolutePath.match('src/client/ssr/head') &&
617
+ !confSsr.head.find((p) => absolutePath.match(`src/client/ssr/head/${p}.js`))
618
+ ) {
619
+ return false;
620
+ }
621
+ if (
622
+ absolutePath.match('src/client/ssr/mailer') &&
623
+ !Object.keys(confSsr.mailer).find((p) => absolutePath.match(`src/client/ssr/mailer/${confSsr.mailer[p]}.js`))
624
+ ) {
625
+ return false;
626
+ }
627
+ if (
628
+ absolutePath.match('src/client/ssr/offline') &&
629
+ !confSsr.offline.find((p) => absolutePath.match(`src/client/ssr/offline/${p.client}.js`))
589
630
  ) {
590
631
  return false;
591
632
  }
592
633
  if (
593
- absolutePath.match('src/client/ssr/components/head') &&
594
- !confSsr.head.find((p) => absolutePath.match(`src/client/ssr/components/head/${p}.js`))
634
+ absolutePath.match('src/client/ssr/pages') &&
635
+ !confSsr.pages.find((p) => absolutePath.match(`src/client/ssr/pages/${p.client}.js`))
595
636
  ) {
596
637
  return false;
597
638
  }
@@ -624,7 +665,7 @@ const deployTest = async (dataDeploy) => {
624
665
  if (singleReplica) continue;
625
666
  const urlTest = `https://${host}${path}`;
626
667
  try {
627
- const result = await axios.get(urlTest);
668
+ const result = await axios.get(urlTest, { timeout: 10000 });
628
669
  const test = result.data.split('<title>');
629
670
  if (test[1])
630
671
  logger.info('Success deploy', {
@@ -660,19 +701,29 @@ const getDeployGroupId = () => {
660
701
  return 'dd';
661
702
  };
662
703
 
704
+ const getDeployId = () => {
705
+ const deployIndexArg = process.argv.findIndex((a) => a.match(`deploy-id:`));
706
+ if (deployIndexArg > -1) return process.argv[deployIndexArg].split(':')[1].trim();
707
+ for (const deployId of process.argv) {
708
+ if (fs.existsSync(`./engine-private/conf/${deployId}`)) return deployId;
709
+ else if (fs.existsSync(`./engine-private/replica/${deployId}`)) return deployId;
710
+ }
711
+ return 'default';
712
+ };
713
+
663
714
  const getCronBackUpFolder = (host = '', path = '') => {
664
715
  return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
665
716
  };
666
717
 
667
- const execDeploy = async (options = { deployId: 'default' }) => {
718
+ const execDeploy = async (options = { deployId: 'default' }, currentAttempt = 1) => {
668
719
  const { deployId } = options;
669
720
  shellExec(Cmd.delete(deployId));
670
721
  shellExec(Cmd.conf(deployId));
671
722
  shellExec(Cmd.run(deployId));
723
+ const maxTime = 1000 * 60;
724
+ const minTime = 20 * 1000;
725
+ const intervalTime = 1000;
672
726
  return await new Promise(async (resolve) => {
673
- const maxTime = 1000 * 60 * 5;
674
- const minTime = 10000 * 2;
675
- const intervalTime = 1000;
676
727
  let currentTime = 0;
677
728
  const attempt = () => {
678
729
  if (currentTime >= minTime && !fs.existsSync(`./tmp/await-deploy`)) {
@@ -682,27 +733,40 @@ const execDeploy = async (options = { deployId: 'default' }) => {
682
733
  cliSpinner(
683
734
  intervalTime,
684
735
  `[deploy.js] `,
685
- ` Load instance | elapsed time ${currentTime / 1000}s / ${maxTime / 1000}s`,
736
+ ` Load instance | attempt:${currentAttempt} | elapsed time ${currentTime / 1000}s / ${maxTime / 1000}s`,
686
737
  'yellow',
687
738
  'material',
688
739
  );
689
740
  currentTime += intervalTime;
690
- if (currentTime >= maxTime) return resolve(false);
741
+ if (currentTime >= maxTime) {
742
+ clearInterval(processMonitor);
743
+ return resolve(false);
744
+ }
691
745
  };
692
746
  const processMonitor = setInterval(attempt, intervalTime);
693
747
  });
694
748
  };
695
749
 
696
- const deployRun = async (dataDeploy, reset) => {
750
+ const deployRun = async (dataDeploy, currentAttempt = 1) => {
697
751
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
698
- if (reset) fs.writeFileSync(`./tmp/runtime-router.json`, '{}', 'utf8');
699
752
  await fixDependencies();
700
- for (const deploy of dataDeploy) await execDeploy(deploy);
753
+ const maxAttempts = 3;
754
+ for (const deploy of dataDeploy) {
755
+ let currentAttempt = 1;
756
+ const attempt = async () => {
757
+ const success = await execDeploy(deploy, currentAttempt);
758
+ currentAttempt++;
759
+ if (!success && currentAttempt <= maxAttempts) await attempt();
760
+ };
761
+ await attempt();
762
+ }
701
763
  const { failed } = await deployTest(dataDeploy);
702
764
  if (failed.length > 0) {
703
765
  for (const deploy of failed) logger.error(deploy.deployId, Cmd.run(deploy.deployId));
704
- await read({ prompt: 'Press enter to retry failed processes\n' });
705
- await deployRun(failed);
766
+ if (currentAttempt === maxAttempts) return logger.error(`max deploy attempts exceeded`);
767
+ if (process.argv.includes('manual')) await read({ prompt: 'Press enter to retry failed processes\n' });
768
+ currentAttempt++;
769
+ await deployRun(failed, currentAttempt);
706
770
  } else logger.info(`Deploy process successfully`);
707
771
  };
708
772
 
@@ -833,6 +897,10 @@ const Cmd = {
833
897
  conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
834
898
  replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
835
899
  syncPorts: (deployGroupId) => `node bin/deploy sync-env-port ${deployGroupId}`,
900
+ cron: (deployId, job, expression) => {
901
+ shellExec(Cmd.delete(`${deployId}-${job}`));
902
+ return `env-cmd -f .env.production pm2 start bin/cron.js --no-autorestart --instances 1 --cron "${expression}" --name ${deployId}-${job} -- ${job} ${deployId}`;
903
+ },
836
904
  };
837
905
 
838
906
  const fixDependencies = async () => {
@@ -852,6 +920,26 @@ const fixDependencies = async () => {
852
920
  );
853
921
  };
854
922
 
923
+ const maintenanceMiddleware = (req, res, port, proxyRouter) => {
924
+ if (process.argv.includes('maintenance') && globalThis.defaultHtmlSrcMaintenance) {
925
+ if (req.method.toUpperCase() === 'GET') {
926
+ res.set('Content-Type', 'text/html');
927
+ return res.status(503).send(globalThis.defaultHtmlSrcMaintenance);
928
+ }
929
+ return res.status(503).json({
930
+ status: 'error',
931
+ message: 'Server is under maintenance',
932
+ });
933
+ }
934
+ };
935
+
936
+ const setUpProxyMaintenanceServer = ({ deployGroupId }) => {
937
+ shellExec(`pm2 kill`);
938
+ const proxyDeployId = fs.readFileSync(`./engine-private/deploy/${deployGroupId}.proxy`, 'utf8').trim();
939
+ shellExec(`node bin/deploy conf ${proxyDeployId} production`);
940
+ shellExec(`node bin/deploy run ${proxyDeployId} maintenance`);
941
+ };
942
+
855
943
  export {
856
944
  Cmd,
857
945
  Config,
@@ -880,4 +968,7 @@ export {
880
968
  getRestoreCronCmd,
881
969
  mergeBackUp,
882
970
  fixDependencies,
971
+ getDeployId,
972
+ maintenanceMiddleware,
973
+ setUpProxyMaintenanceServer,
883
974
  };
@@ -0,0 +1,91 @@
1
+ import crypto from 'crypto';
2
+ import fs from 'fs-extra';
3
+
4
+ const CryptoBuilder = {
5
+ symmetric: {
6
+ instance: function (options = { iv: '', encryptionKey: '' }) {
7
+ // Generate a random 32-byte encryption key
8
+ const encryptionKey = option?.encryptionKey ? options.encryptionKey : crypto.randomBytes(32);
9
+ const iv = option?.iv ? options.iv : crypto.randomBytes(16); // Generate a new Initialization Vector (IV) for each encryption
10
+
11
+ // Function to encrypt data
12
+ function encryptData(plaintext = '') {
13
+ const cipher = crypto.createCipheriv('aes-256-cbc', encryptionKey, iv);
14
+ let encrypted = cipher.update(plaintext, 'utf8', 'hex');
15
+ encrypted += cipher.final('hex');
16
+ return `${iv.toString('hex')}:${encrypted}`;
17
+ }
18
+
19
+ // Function to decrypt data
20
+ function decryptData(ciphertext = '') {
21
+ const [ivHex, encrypted] = ciphertext.split(':');
22
+ const _iv = Buffer.from(ivHex, 'hex');
23
+ const decipher = crypto.createDecipheriv('aes-256-cbc', encryptionKey, _iv);
24
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
25
+ decrypted += decipher.final('utf8');
26
+ return decrypted;
27
+ }
28
+
29
+ return {
30
+ encryptionKey,
31
+ iv,
32
+ encryptData,
33
+ decryptData,
34
+ };
35
+ },
36
+ },
37
+ asymmetric: {
38
+ instance: function (
39
+ options = {
40
+ publicKey: '', // fs.readFileSync('./key.pem', 'utf8')
41
+ privateKey: '', // fs.readFileSync('./key.pem', 'utf8')
42
+ },
43
+ ) {
44
+ // Generate a new key pair
45
+ const { privateKey, publicKey } = options
46
+ ? options
47
+ : crypto.generateKeyPairSync('rsa', {
48
+ modulusLength: 2048, // Key size in bits
49
+ publicKeyEncoding: {
50
+ type: 'spki',
51
+ format: 'pem',
52
+ },
53
+ privateKeyEncoding: {
54
+ type: 'pkcs8',
55
+ format: 'pem',
56
+ },
57
+ });
58
+
59
+ // Function to encrypt data
60
+ function encryptData(plaintext) {
61
+ const buffer = Buffer.from(plaintext, 'utf8');
62
+ const encrypted = crypto.publicEncrypt(publicKey, buffer);
63
+ return encrypted.toString('hex');
64
+ }
65
+
66
+ // Function to decrypt data
67
+ function decryptData(ciphertext) {
68
+ const buffer = Buffer.from(ciphertext, 'hex');
69
+ const decrypted = crypto.privateDecrypt(privateKey, buffer);
70
+ return decrypted.toString('utf8');
71
+ }
72
+
73
+ fs.writeFileSync('./public.pem', publicKey);
74
+ fs.writeFileSync('./private.pem', privateKey);
75
+
76
+ const result = {
77
+ privateKey: fs.readFileSync('./public.pem', 'utf8'),
78
+ publicKey: fs.readFileSync('./private.pem', 'utf8'),
79
+ encryptData,
80
+ decryptData,
81
+ };
82
+
83
+ fs.removeSync('./public.pem');
84
+ fs.removeSync('./private.pem');
85
+
86
+ return result;
87
+ },
88
+ },
89
+ };
90
+
91
+ export { CryptoBuilder };
package/src/server/dns.js CHANGED
@@ -1,38 +1,47 @@
1
1
  import axios from 'axios';
2
2
  import dotenv from 'dotenv';
3
3
  import fs from 'fs';
4
- import cron from 'node-cron';
4
+ import https from 'https';
5
5
 
6
6
  import { ip } from './network.js';
7
7
  import { loggerFactory } from './logger.js';
8
8
  import { isIPv4 } from 'is-ip';
9
+ import { shellExec } from './process.js';
10
+
11
+ const httpsAgent = new https.Agent({
12
+ rejectUnauthorized: false,
13
+ });
14
+
15
+ axios.defaults.httpsAgent = httpsAgent;
9
16
 
10
17
  dotenv.config();
11
18
 
12
19
  const logger = loggerFactory(import.meta);
13
20
 
14
21
  const Dns = {
15
- ip: null,
16
- ipDaemon: null,
22
+ repoUrl: `https://${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_USERNAME}/${process.env.GITHUB_DNS_REPO}.git`,
17
23
  callback: () => null,
18
- InitIpDaemon: async function () {
19
- // WAN | NAT-VPS | LAN
24
+ InitIpDaemon: async function ({ deployId }) {
25
+ // NAT-VPS modem/router device configuration:
26
+ // LAN --> [NAT-VPS] --> WAN
20
27
  // enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
21
- // LAN server or device's local servers port -> 3000-3100 (2999-3101)
22
- // DNS Records: [ANAME](Address Dynamic) -> [A](ipv4) host | [AAAA](ipv6) host -> [ip]
28
+ // disabled local red DHCP
29
+ // verify inet ip proxy server address
23
30
  // DHCP (Dynamic Host Configuration Protocol) LAN reserver IP -> MAC ID
31
+ // LAN server or device's local servers port -> 3000-3100 (2999-3101)
32
+ // DNS Records: [ANAME](Address Dynamic) -> [A](ipv4) host | [AAAA](ipv6) host -> [public-ip]
24
33
  // Forward the router's TCP/UDP ports to the LAN device's IP address
25
34
 
26
- const privateCronConfPath = `./engine-private/conf/${process.argv[2]}/conf.cron.json`;
35
+ const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
27
36
 
28
37
  const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
29
-
30
38
  let confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
31
39
  if (confCronData.ipDaemon.disabled) return;
32
40
  Dns.ip = confCronData.ipDaemon.ip;
33
41
  logger.info(`Current ip`, Dns.ip);
34
- if (Dns.ipDaemon) clearInterval(Dns.ipDaemon);
35
42
  const callback = async () => {
43
+ logger.info('init dns ip callback');
44
+ await logger.setUpInfo();
36
45
  let testIp;
37
46
  try {
38
47
  testIp = await ip.public.ipv4();
@@ -41,15 +50,12 @@ const Dns = {
41
50
  }
42
51
  if (testIp && typeof testIp === 'string' && isIPv4(testIp) && Dns.ip !== testIp) {
43
52
  logger.info(`New ip`, testIp);
44
- Dns.ip = testIp;
45
- confCronData.ipDaemon.ip = Dns.ip;
46
- fs.writeFileSync(confCronPath, JSON.stringify(confCronData, null, 4), 'utf8');
47
53
  for (const recordType of Object.keys(confCronData.records)) {
48
54
  switch (recordType) {
49
55
  case 'A':
50
56
  for (const dnsProvider of confCronData.records[recordType]) {
51
57
  if (typeof Dns.services.updateIp[dnsProvider.dns] === 'function')
52
- await Dns.services.updateIp[dnsProvider.dns](dnsProvider);
58
+ await Dns.services.updateIp[dnsProvider.dns]({ ...dnsProvider, ip: testIp });
53
59
  }
54
60
  break;
55
61
 
@@ -57,16 +63,28 @@ const Dns = {
57
63
  break;
58
64
  }
59
65
  }
66
+ try {
67
+ const ipUrlTest = `https://${process.env.DEFAULT_DEPLOY_HOST}`;
68
+ const response = await axios.get(ipUrlTest);
69
+ const verifyIp = response.request.socket.remoteAddress;
70
+ logger.info(ipUrlTest + ' IP', verifyIp);
71
+ if (verifyIp === testIp) {
72
+ await this.saveIp(confCronPath, confCronData, testIp);
73
+ } else logger.error('ip not updated');
74
+ } catch (error) {
75
+ logger.error(error), 'ip not updated';
76
+ }
60
77
  }
61
78
  };
79
+ await callback();
62
80
  this.callback = callback;
63
81
  return callback;
64
82
  },
65
83
  services: {
66
84
  updateIp: {
67
85
  dondominio: (options) => {
68
- const { user, api_key, host, dns } = options;
69
- const url = `https://dondns.dondominio.com/json/?user=${user}&password=${api_key}&host=${host}&ip=${Dns.ip}`;
86
+ const { user, api_key, host, dns, ip } = options;
87
+ const url = `https://dondns.dondominio.com/json/?user=${user}&password=${api_key}&host=${host}&ip=${ip}`;
70
88
  logger.info(`${dns} update ip url`, url);
71
89
  if (process.env.NODE_ENV !== 'production') return false;
72
90
  return new Promise((resolve) => {
@@ -84,6 +102,20 @@ const Dns = {
84
102
  },
85
103
  },
86
104
  },
105
+ saveIp: async (confCronPath, confCronData, ip) => {
106
+ Dns.ip = ip;
107
+ confCronData.ipDaemon.ip = ip;
108
+ fs.writeFileSync(confCronPath, JSON.stringify(confCronData, null, 4), 'utf8');
109
+ shellExec(
110
+ `cd ./engine-private` +
111
+ ` && git pull ${Dns.repoUrl}` +
112
+ ` && git add . && git commit -m "update ip ${new Date().toLocaleDateString()}"` +
113
+ ` && git push ${Dns.repoUrl}`,
114
+ {
115
+ disableLog: true,
116
+ },
117
+ );
118
+ },
87
119
  };
88
120
 
89
121
  export { Dns };
@@ -5,6 +5,8 @@ import { publicIp, publicIpv4, publicIpv6 } from 'public-ip';
5
5
  import { killPortProcess } from 'kill-port-process';
6
6
  import { loggerFactory } from './logger.js';
7
7
  import { orderArrayFromAttrInt } from '../client/components/core/CommonJs.js';
8
+ import { DataBaseProvider } from '../db/DataBaseProvider.js';
9
+ import { getDeployId } from './conf.js';
8
10
 
9
11
  // Network Address Translation Management
10
12
 
@@ -66,12 +68,86 @@ const logRuntimeRouter = () => {
66
68
  logger.info('Runtime network', displayLog);
67
69
  };
68
70
 
69
- const saveRuntimeRouter = () =>
70
- fs.writeFileSync(
71
- `./tmp/runtime-router.${process.argv[3] ? process.argv[3] : 'default'}.json`,
72
- JSON.stringify(networkRouter, null, 4),
73
- 'utf-8',
74
- );
71
+ const saveRuntimeRouter = async () => {
72
+ try {
73
+ const deployId = process.env.DEFAULT_DEPLOY_ID;
74
+ const host = process.env.DEFAULT_DEPLOY_HOST;
75
+ const path = process.env.DEFAULT_DEPLOY_PATH;
76
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
77
+ const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
78
+ const { db } = confServer[host][path];
79
+
80
+ let closeConn;
81
+ if (!DataBaseProvider.instance[`${host}${path}`]) {
82
+ await DataBaseProvider.load({ apis: ['instance'], host, path, db });
83
+ closeConn = true;
84
+ }
85
+
86
+ /** @type {import('../api/instance/instance.model.js').InstanceModel} */
87
+ const Instance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Instance;
88
+
89
+ for (const _host of Object.keys(networkRouter)) {
90
+ for (const _path of Object.keys(networkRouter[_host])) {
91
+ const body = {
92
+ host: _host,
93
+ path: _path,
94
+ deployId: getDeployId(),
95
+ client: networkRouter[_host][_path].client,
96
+ runtime: networkRouter[_host][_path].runtime,
97
+ port: networkRouter[_host][_path].port,
98
+ apis: networkRouter[_host][_path].apis,
99
+ };
100
+ const instance = await Instance.findOne({ deployId: body.deployId, port: body.port });
101
+ if (instance) {
102
+ await Instance.findByIdAndUpdate(instance._id, body);
103
+ } else {
104
+ await new Instance(body).save();
105
+ }
106
+ }
107
+ }
108
+
109
+ if (closeConn) await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
110
+ } catch (error) {
111
+ logger.error(error);
112
+ }
113
+ };
114
+
115
+ const netWorkCron = [];
116
+
117
+ const saveRuntimeCron = async () => {
118
+ try {
119
+ const deployId = process.env.DEFAULT_DEPLOY_ID;
120
+ const host = process.env.DEFAULT_DEPLOY_HOST;
121
+ const path = process.env.DEFAULT_DEPLOY_PATH;
122
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
123
+ const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
124
+ const { db } = confServer[host][path];
125
+
126
+ let closeConn;
127
+ if (!DataBaseProvider.instance[`${host}${path}`]) {
128
+ await DataBaseProvider.load({ apis: ['cron'], host, path, db });
129
+ closeConn = true;
130
+ }
131
+
132
+ /** @type {import('../api/cron/cron.model.js').CronModel} */
133
+ const Cron = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Cron;
134
+
135
+ // await Cron.insertMany(netWorkCron);
136
+
137
+ for (const cronInstance of netWorkCron) {
138
+ const cron = await Cron.findOne({ deployId: cronInstance.deployId, jobId: cronInstance.jobId });
139
+ if (cron) {
140
+ await Cron.findByIdAndUpdate(cron._id, cronInstance);
141
+ } else {
142
+ await new Cron(cronInstance).save();
143
+ }
144
+ }
145
+
146
+ if (closeConn) await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
147
+ } catch (error) {
148
+ logger.error(error);
149
+ }
150
+ };
75
151
 
76
152
  const listenServerFactory = (logic = async () => {}) => {
77
153
  return {
@@ -109,6 +185,7 @@ const listenPortController = async (server, port, metadata) =>
109
185
  ? `https://${host}${path}`
110
186
  : `http://${host}:${port}${path}`,
111
187
  local: `http://localhost:${port}${path}`,
188
+ apis: metadata.apis,
112
189
  };
113
190
 
114
191
  return resolve(true);
@@ -119,4 +196,14 @@ const listenPortController = async (server, port, metadata) =>
119
196
  }
120
197
  });
121
198
 
122
- export { ip, network, listenPortController, networkRouter, saveRuntimeRouter, logRuntimeRouter, listenServerFactory };
199
+ export {
200
+ ip,
201
+ network,
202
+ listenPortController,
203
+ networkRouter,
204
+ netWorkCron,
205
+ saveRuntimeRouter,
206
+ logRuntimeRouter,
207
+ listenServerFactory,
208
+ saveRuntimeCron,
209
+ };