underpost 2.7.8 → 2.7.9

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 (57) hide show
  1. package/.github/workflows/ghpkg.yml +41 -1
  2. package/.github/workflows/publish.yml +17 -18
  3. package/.github/workflows/pwa-microservices-template.page.yml +54 -0
  4. package/.vscode/settings.json +6 -0
  5. package/CHANGELOG.md +64 -16
  6. package/bin/cron.js +47 -0
  7. package/bin/db.js +9 -1
  8. package/bin/deploy.js +194 -9
  9. package/bin/file.js +17 -1
  10. package/bin/index.js +1 -1
  11. package/bin/util.js +22 -0
  12. package/conf.js +18 -4
  13. package/docker-compose.yml +1 -1
  14. package/package.json +3 -3
  15. package/src/api/core/core.router.js +9 -9
  16. package/src/api/core/core.service.js +6 -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 +7 -7
  20. package/src/client/components/core/CssCore.js +30 -3
  21. package/src/client/components/core/Docs.js +110 -10
  22. package/src/client/components/core/Modal.js +224 -22
  23. package/src/client/components/core/Panel.js +1 -1
  24. package/src/client/components/core/PanelForm.js +2 -1
  25. package/src/client/components/core/Responsive.js +15 -0
  26. package/src/client/components/core/RichText.js +4 -2
  27. package/src/client/components/core/WebComponent.js +44 -0
  28. package/src/client/components/core/Worker.js +10 -12
  29. package/src/client/public/default/plantuml/client-conf.svg +1 -1
  30. package/src/client/public/default/plantuml/client-schema.svg +1 -1
  31. package/src/client/public/default/plantuml/cron-conf.svg +1 -1
  32. package/src/client/public/default/plantuml/cron-schema.svg +1 -1
  33. package/src/client/public/default/plantuml/server-conf.svg +1 -1
  34. package/src/client/public/default/plantuml/server-schema.svg +1 -1
  35. package/src/client/public/default/plantuml/ssr-conf.svg +1 -1
  36. package/src/client/public/default/plantuml/ssr-schema.svg +1 -1
  37. package/src/client/public/default/site.webmanifest +69 -0
  38. package/src/client/ssr/components/body/CacheControl.js +1 -1
  39. package/src/client/ssr/components/head/Production.js +1 -0
  40. package/src/client/ssr/components/head/Pwa.js +146 -0
  41. package/src/client/ssr/components/head/Seo.js +14 -0
  42. package/src/client/ssr/pages/maintenance.js +14 -0
  43. package/src/client/ssr/pages/offline.js +21 -0
  44. package/src/client/sw/default.sw.js +4 -2
  45. package/src/db/DataBaseProvider.js +12 -1
  46. package/src/db/mongo/MongooseDB.js +0 -1
  47. package/src/server/backup.js +82 -70
  48. package/src/server/client-build.js +23 -90
  49. package/src/server/conf.js +51 -5
  50. package/src/server/crypto.js +91 -0
  51. package/src/server/dns.js +42 -13
  52. package/src/server/network.js +94 -7
  53. package/src/server/proxy.js +27 -27
  54. package/src/server/runtime.js +3 -1
  55. package/src/client/ssr/offline/default.index.js +0 -31
  56. package/src/cron.js +0 -30
  57. package/src/server/cron.js +0 -35
@@ -52,7 +52,7 @@ const fullBuild = async ({
52
52
  if (!fs.existsSync(defaultBaseIconPath))
53
53
  await buildTextImg(metadata.title, { debugFilename: defaultBaseIconPath });
54
54
 
55
- if (path === '/' && !fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`))
55
+ if (!fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`))
56
56
  await buildIcons({ publicClientId, metadata });
57
57
  }
58
58
  fs.copySync(
@@ -188,7 +188,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
188
188
  iconsBuild,
189
189
  metadata,
190
190
  });
191
- if (apis)
191
+ if (apis && false)
192
192
  for (const apiBuildScript of apis) {
193
193
  const scriptPath = `src/api/${apiBuildScript}/${apiBuildScript}.build.js`;
194
194
  if (fs.existsSync(`./${scriptPath}`)) {
@@ -248,8 +248,13 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
248
248
  'const getBaseHost = () => location.host;',
249
249
  `const getBaseHost = () => '${apiBaseHost}';`,
250
250
  );
251
- if (apiBaseProxyPath)
251
+ if (apiBaseProxyPath) {
252
252
  jsSrc = jsSrc.replace('${getProxyPath()}api/', `${apiBaseProxyPath}${process.env.BASE_API}/`);
253
+ jsSrc = jsSrc.replace(
254
+ "const getWsBasePath = () => (getProxyPath() !== '/' ? `${getProxyPath()}socket.io/` : undefined);",
255
+ `const getWsBasePath = () => '${apiBaseProxyPath}socket.io/';`,
256
+ );
257
+ }
253
258
  }
254
259
  fs.writeFileSync(
255
260
  jsPublicPath,
@@ -288,11 +293,11 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
288
293
  eval(await srcFormatted(fs.readFileSync(`./src/client/ssr/Render.js`, 'utf8')));
289
294
 
290
295
  if (views) {
291
- const buildJsSrcPage = async (jsSrcPath, jsPublicPath) => {
296
+ const buildJsSrcPage = async (jsSrcPath, jsPublicPath, filter) => {
292
297
  if (!(enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath))) {
293
298
  let jsSrc = viewFormatted(await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')), dists, path, baseHost);
294
299
  if (jsSrc.split('/*imports*/')[1]) jsSrc = jsSrc.split('/*imports*/')[1];
295
-
300
+ if (filter) jsSrc = await filter(jsSrc);
296
301
  fs.writeFileSync(
297
302
  jsPublicPath,
298
303
  minifyBuild || process.env.NODE_ENV === 'production' ? UglifyJS.minify(jsSrc).code : jsSrc,
@@ -301,47 +306,17 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
301
306
  }
302
307
  };
303
308
 
304
- if (path === '/') {
305
- // service woker
306
- await buildJsSrcPage(
307
- fs.existsSync(`./src/client/sw/${publicClientId}.sw.js`)
308
- ? `./src/client/sw/${publicClientId}.sw.js`
309
- : `./src/client/sw/default.sw.js`,
310
- `${rootClientPath}/sw.js`,
311
- );
312
- }
313
-
314
- // offline html
315
- {
316
- await buildJsSrcPage(
317
- fs.existsSync(`./src/client/ssr/offline/${publicClientId}.index.js`)
318
- ? `./src/client/ssr/offline/${publicClientId}.index.js`
319
- : `./src/client/ssr/offline/default.index.js`,
320
- `${rootClientPath}/offline.js`,
321
- );
322
-
323
- const htmlSrc = Render({
324
- title: metadata?.title ? metadata.title : cap(client),
325
- ssrPath: '/',
326
- ssrHeadComponents: '',
327
- ssrBodyComponents: '',
328
- baseSsrLib: jsSsrCommonComponents + fs.readFileSync(`${rootClientPath}/offline.js`, 'utf8'),
329
- });
309
+ // service woker
310
+ await buildJsSrcPage(
311
+ fs.existsSync(`./src/client/sw/${publicClientId}.sw.js`)
312
+ ? `./src/client/sw/${publicClientId}.sw.js`
313
+ : `./src/client/sw/default.sw.js`,
314
+ `${rootClientPath}/sw.js`,
315
+ path !== '/'
316
+ ? (jsSrc) => jsSrc.replaceAll(`const PROXY_PATH = '/';`, `const PROXY_PATH = '${path}/';`)
317
+ : undefined,
318
+ );
330
319
 
331
- fs.writeFileSync(
332
- `${rootClientPath}offline.html`,
333
- minifyBuild || process.env.NODE_ENV === 'production'
334
- ? await minify(htmlSrc, {
335
- minifyCSS: true,
336
- minifyJS: true,
337
- collapseBooleanAttributes: true,
338
- collapseInlineTagWhitespace: true,
339
- collapseWhitespace: true,
340
- })
341
- : htmlSrc,
342
- 'utf8',
343
- );
344
- }
345
320
  // ssr pages
346
321
  for (const page of await fs.readdir('./src/client/ssr/pages')) {
347
322
  await buildJsSrcPage(`./src/client/ssr/pages/${page}`, `${rootClientPath}/${page}`);
@@ -355,7 +330,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
355
330
  });
356
331
 
357
332
  fs.writeFileSync(
358
- `${rootClientPath}${page.slice(0, -3)}.html`,
333
+ `${rootClientPath}/${page.slice(0, -3)}.html`,
359
334
  minifyBuild || process.env.NODE_ENV === 'production'
360
335
  ? await minify(htmlSrc, {
361
336
  minifyCSS: true,
@@ -432,7 +407,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
432
407
  fs.existsSync(`./src/client/public/${publicClientId}/browserconfig.xml`) &&
433
408
  fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`);
434
409
 
435
- if (view.path === '/' && validPwaBuild) {
410
+ if (validPwaBuild) {
436
411
  // build webmanifest
437
412
  const webmanifestJson = JSON.parse(
438
413
  fs.readFileSync(`./src/client/public/${publicClientId}/site.webmanifest`, 'utf8'),
@@ -540,48 +515,6 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
540
515
  host,
541
516
  path,
542
517
  ttiLoadTimeLimit,
543
- storage: {
544
- // 'space-background': fs.readFileSync('./src/client/public/cyberia/space-background', 'utf8'),
545
- lore0: `data:image/jpeg;base64,${fs
546
- .readFileSync('./src/client/public/cyberia/assets/lore/lore0.jpeg')
547
- .toString('base64')}`,
548
- lore1: `data:image/jpeg;base64,${fs
549
- .readFileSync('./src/client/public/cyberia/assets/lore/lore1.jpeg')
550
- .toString('base64')}`,
551
- lore2: `data:image/jpeg;base64,${fs
552
- .readFileSync('./src/client/public/cyberia/assets/lore/lore2.jpeg')
553
- .toString('base64')}`,
554
- lore3: `data:image/jpeg;base64,${fs
555
- .readFileSync('./src/client/public/cyberia/assets/lore/lore3.jpeg')
556
- .toString('base64')}`,
557
- lore4: `data:image/jpeg;base64,${fs
558
- .readFileSync('./src/client/public/cyberia/assets/lore/lore4.jpeg')
559
- .toString('base64')}`,
560
- lore5: `data:image/jpeg;base64,${fs
561
- .readFileSync('./src/client/public/cyberia/assets/lore/lore5.jpeg')
562
- .toString('base64')}`,
563
- lore6: `data:image/jpeg;base64,${fs
564
- .readFileSync('./src/client/public/cyberia/assets/lore/lore6.jpeg')
565
- .toString('base64')}`,
566
- lore7: `data:image/jpeg;base64,${fs
567
- .readFileSync('./src/client/public/cyberia/assets/lore/lore7.jpeg')
568
- .toString('base64')}`,
569
- lore8: `data:image/jpeg;base64,${fs
570
- .readFileSync('./src/client/public/cyberia/assets/lore/lore8.jpeg')
571
- .toString('base64')}`,
572
- ['arrow-left']: `data:image/png;base64,${fs
573
- .readFileSync('./src/client/public/cyberia/assets/ui-icons/arrow-left.png')
574
- .toString('base64')}`,
575
- ['arrow-right']: `data:image/png;base64,${fs
576
- .readFileSync('./src/client/public/cyberia/assets/ui-icons/arrow-right.png')
577
- .toString('base64')}`,
578
- ['fullscreen']: `data:image/png;base64,${fs
579
- .readFileSync('./src/client/public/cyberia/assets/ui-icons/fullscreen.png')
580
- .toString('base64')}`,
581
- ['cyberia-logo']: `data:image/png;base64,${fs
582
- .readFileSync('./src/client/public/cyberia/assets/util/cyberia-retro-banner.png')
583
- .toString('base64')}`,
584
- },
585
518
  });
586
519
  break;
587
520
  }
@@ -686,7 +619,7 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
686
619
  fs.copySync(`./coverage`, coverageBuildPath);
687
620
 
688
621
  // uml
689
- shellExec(`node bin/deploy uml ${host} ${path}`);
622
+ // shellExec(`node bin/deploy uml ${host} ${path}`);
690
623
 
691
624
  // https://swagger-autogen.github.io/docs/
692
625
 
@@ -7,7 +7,7 @@ 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, shellCd, getRootDirectory } from './process.js';
11
11
  import { DefaultConf } from '../../conf.js';
12
12
  import ncp from 'copy-paste';
13
13
  import read from 'read';
@@ -44,7 +44,9 @@ const Config = {
44
44
  this.default.server = {};
45
45
  for (const deployId of process.argv[3].split(',')) {
46
46
  let confPath = `./engine-private/conf/${deployId}/conf.server.json`;
47
- const privateConfDevPath = `./engine-private/conf/${deployId}/conf.server.dev.${process.argv[4]}.json`;
47
+ const privateConfDevPath = fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
48
+ ? `./engine-private/replica/${deployId}/conf.server.json`
49
+ : `./engine-private/conf/${deployId}/conf.server.dev.${process.argv[4]}.json`;
48
50
  const confDevPath = fs.existsSync(privateConfDevPath)
49
51
  ? privateConfDevPath
50
52
  : `./engine-private/conf/${deployId}/conf.server.dev.json`;
@@ -89,6 +91,11 @@ const loadConf = (deployId) => {
89
91
  if (!fs.existsSync(`./conf`)) fs.mkdirSync(`./conf`);
90
92
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
91
93
  const isValidDeployId = fs.existsSync(`${folder}`);
94
+ if (!isValidDeployId) {
95
+ logger.info(`Save new deploy conf: '${deployId}'`);
96
+ shellExec(`node bin/deploy save ${deployId}`);
97
+ return loadConf(deployId);
98
+ }
92
99
  for (const typeConf of Object.keys(Config.default)) {
93
100
  let srcConf = isValidDeployId
94
101
  ? fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8')
@@ -246,8 +253,8 @@ const buildClientSrc = async (
246
253
  );
247
254
 
248
255
  fs.writeFileSync(
249
- `./src/client/${toClientVariableName}.js`,
250
- formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.js`, 'utf8')),
256
+ `./src/client/${toClientVariableName}.index.js`,
257
+ formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.index.js`, 'utf8')),
251
258
  'utf8',
252
259
  );
253
260
 
@@ -660,6 +667,16 @@ const getDeployGroupId = () => {
660
667
  return 'dd';
661
668
  };
662
669
 
670
+ const getDeployId = () => {
671
+ const deployIndexArg = process.argv.findIndex((a) => a.match(`deploy-id:`));
672
+ if (deployIndexArg > -1) return process.argv[deployIndexArg].split(':')[1].trim();
673
+ for (const deployId of process.argv) {
674
+ if (fs.existsSync(`./engine-private/conf/${deployId}`)) return deployId;
675
+ else if (fs.existsSync(`./engine-private/replica/${deployId}`)) return deployId;
676
+ }
677
+ return 'default';
678
+ };
679
+
663
680
  const getCronBackUpFolder = (host = '', path = '') => {
664
681
  return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
665
682
  };
@@ -671,7 +688,7 @@ const execDeploy = async (options = { deployId: 'default' }) => {
671
688
  shellExec(Cmd.run(deployId));
672
689
  return await new Promise(async (resolve) => {
673
690
  const maxTime = 1000 * 60 * 5;
674
- const minTime = 10000 * 2;
691
+ const minTime = 7 * 1000;
675
692
  const intervalTime = 1000;
676
693
  let currentTime = 0;
677
694
  const attempt = () => {
@@ -833,6 +850,10 @@ const Cmd = {
833
850
  conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
834
851
  replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
835
852
  syncPorts: (deployGroupId) => `node bin/deploy sync-env-port ${deployGroupId}`,
853
+ cron: (deployId, job, expression) => {
854
+ shellExec(Cmd.delete(`${deployId}-${job}`));
855
+ return `env-cmd -f .env.production pm2 start bin/cron.js --no-autorestart --instances 1 --cron "${expression}" --name ${deployId}-${job} -- ${job} ${deployId}`;
856
+ },
836
857
  };
837
858
 
838
859
  const fixDependencies = async () => {
@@ -852,6 +873,27 @@ const fixDependencies = async () => {
852
873
  );
853
874
  };
854
875
 
876
+ const maintenancePath = `${getRootDirectory()}/public/${process.env.DEFAULT_DEPLOY_HOST}${
877
+ process.env.DEFAULT_DEPLOY_PATH
878
+ }/maintenance.html`;
879
+
880
+ const maintenanceMiddleware = (req, res, port, proxyRouter) => {
881
+ if (process.argv.includes('maintenance') && fs.existsSync(maintenancePath)) {
882
+ if (req.method.toUpperCase() === 'GET') return res.status(503).sendFile(maintenancePath);
883
+ return res.status(503).json({
884
+ status: 'error',
885
+ message: 'Server is under maintenance',
886
+ });
887
+ }
888
+ };
889
+
890
+ const setUpProxyMaintenanceServer = ({ deployGroupId }) => {
891
+ shellExec(`pm2 kill`);
892
+ const proxyDeployId = fs.readFileSync(`./engine-private/deploy/${deployGroupId}.proxy`, 'utf8').trim();
893
+ shellExec(`node bin/deploy conf ${proxyDeployId} production`);
894
+ shellExec(`node bin/deploy run ${proxyDeployId} maintenance`);
895
+ };
896
+
855
897
  export {
856
898
  Cmd,
857
899
  Config,
@@ -880,4 +922,8 @@ export {
880
922
  getRestoreCronCmd,
881
923
  mergeBackUp,
882
924
  fixDependencies,
925
+ getDeployId,
926
+ maintenancePath,
927
+ maintenanceMiddleware,
928
+ setUpProxyMaintenanceServer,
883
929
  };
@@ -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,21 +1,27 @@
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 () {
24
+ InitIpDaemon: async function ({ deployId }) {
19
25
  // WAN | NAT-VPS | LAN
20
26
  // enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
21
27
  // LAN server or device's local servers port -> 3000-3100 (2999-3101)
@@ -23,16 +29,16 @@ const Dns = {
23
29
  // DHCP (Dynamic Host Configuration Protocol) LAN reserver IP -> MAC ID
24
30
  // Forward the router's TCP/UDP ports to the LAN device's IP address
25
31
 
26
- const privateCronConfPath = `./engine-private/conf/${process.argv[2]}/conf.cron.json`;
32
+ const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
27
33
 
28
34
  const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
29
-
30
35
  let confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
31
36
  if (confCronData.ipDaemon.disabled) return;
32
37
  Dns.ip = confCronData.ipDaemon.ip;
33
38
  logger.info(`Current ip`, Dns.ip);
34
- if (Dns.ipDaemon) clearInterval(Dns.ipDaemon);
35
39
  const callback = async () => {
40
+ logger.info('init dns ip callback');
41
+ await logger.setUpInfo();
36
42
  let testIp;
37
43
  try {
38
44
  testIp = await ip.public.ipv4();
@@ -41,15 +47,12 @@ const Dns = {
41
47
  }
42
48
  if (testIp && typeof testIp === 'string' && isIPv4(testIp) && Dns.ip !== testIp) {
43
49
  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
50
  for (const recordType of Object.keys(confCronData.records)) {
48
51
  switch (recordType) {
49
52
  case 'A':
50
53
  for (const dnsProvider of confCronData.records[recordType]) {
51
54
  if (typeof Dns.services.updateIp[dnsProvider.dns] === 'function')
52
- await Dns.services.updateIp[dnsProvider.dns](dnsProvider);
55
+ await Dns.services.updateIp[dnsProvider.dns]({ ...dnsProvider, ip: testIp });
53
56
  }
54
57
  break;
55
58
 
@@ -57,16 +60,28 @@ const Dns = {
57
60
  break;
58
61
  }
59
62
  }
63
+ try {
64
+ const ipUrlTest = `https://${process.env.DEFAULT_DEPLOY_HOST}`;
65
+ const response = await axios.get(ipUrlTest);
66
+ const verifyIp = response.request.socket.remoteAddress;
67
+ logger.info(ipUrlTest + ' IP', verifyIp);
68
+ if (verifyIp === testIp) {
69
+ await this.saveIp(confCronPath, confCronData, testIp);
70
+ } else logger.error('ip not updated');
71
+ } catch (error) {
72
+ logger.error(error), 'ip not updated';
73
+ }
60
74
  }
61
75
  };
76
+ await callback();
62
77
  this.callback = callback;
63
78
  return callback;
64
79
  },
65
80
  services: {
66
81
  updateIp: {
67
82
  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}`;
83
+ const { user, api_key, host, dns, ip } = options;
84
+ const url = `https://dondns.dondominio.com/json/?user=${user}&password=${api_key}&host=${host}&ip=${ip}`;
70
85
  logger.info(`${dns} update ip url`, url);
71
86
  if (process.env.NODE_ENV !== 'production') return false;
72
87
  return new Promise((resolve) => {
@@ -84,6 +99,20 @@ const Dns = {
84
99
  },
85
100
  },
86
101
  },
102
+ saveIp: async (confCronPath, confCronData, ip) => {
103
+ Dns.ip = ip;
104
+ confCronData.ipDaemon.ip = ip;
105
+ fs.writeFileSync(confCronPath, JSON.stringify(confCronData, null, 4), 'utf8');
106
+ shellExec(
107
+ `cd ./engine-private` +
108
+ ` && git pull ${Dns.repoUrl}` +
109
+ ` && git add . && git commit -m "update ip ${new Date().toLocaleDateString()}"` +
110
+ ` && git push ${Dns.repoUrl}`,
111
+ {
112
+ disableLog: true,
113
+ },
114
+ );
115
+ },
87
116
  };
88
117
 
89
118
  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
+ };