underpost 2.7.7 → 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 (79) hide show
  1. package/.github/workflows/ghpkg.yml +115 -0
  2. package/.github/workflows/publish.yml +20 -3
  3. package/.github/workflows/pwa-microservices-template.page.yml +54 -0
  4. package/.github/workflows/pwa-microservices-template.test.yml +30 -0
  5. package/.vscode/settings.json +6 -0
  6. package/CHANGELOG.md +64 -16
  7. package/bin/cron.js +47 -0
  8. package/bin/db.js +9 -1
  9. package/bin/deploy.js +207 -11
  10. package/bin/file.js +17 -1
  11. package/bin/index.js +1 -1
  12. package/bin/util.js +22 -0
  13. package/conf.js +18 -4
  14. package/docker-compose.yml +1 -1
  15. package/package.json +3 -3
  16. package/src/api/core/core.router.js +9 -9
  17. package/src/api/core/core.service.js +6 -4
  18. package/src/api/default/default.service.js +4 -4
  19. package/src/api/file/file.service.js +3 -3
  20. package/src/api/user/user.service.js +7 -7
  21. package/src/client/components/core/Css.js +0 -222
  22. package/src/client/components/core/CssCore.js +30 -3
  23. package/src/client/components/core/Docs.js +110 -10
  24. package/src/client/components/core/Modal.js +224 -22
  25. package/src/client/components/core/Panel.js +1 -1
  26. package/src/client/components/core/PanelForm.js +2 -1
  27. package/src/client/components/core/Responsive.js +15 -0
  28. package/src/client/components/core/RichText.js +4 -2
  29. package/src/client/components/core/Translate.js +6 -2
  30. package/src/client/components/core/WebComponent.js +44 -0
  31. package/src/client/components/core/Worker.js +12 -4
  32. package/src/client/public/default/plantuml/client-conf.svg +1 -1
  33. package/src/client/public/default/plantuml/client-schema.svg +1 -1
  34. package/src/client/public/default/plantuml/cron-conf.svg +1 -1
  35. package/src/client/public/default/plantuml/cron-schema.svg +1 -1
  36. package/src/client/public/default/plantuml/server-conf.svg +1 -1
  37. package/src/client/public/default/plantuml/server-schema.svg +1 -1
  38. package/src/client/public/default/plantuml/ssr-conf.svg +1 -1
  39. package/src/client/public/default/plantuml/ssr-schema.svg +1 -1
  40. package/src/client/public/default/site.webmanifest +69 -0
  41. package/src/client/services/default/default.management.js +118 -120
  42. package/src/client/ssr/Render.js +224 -3
  43. package/src/client/ssr/common/Alert.js +75 -0
  44. package/src/client/ssr/common/SsrCore.js +91 -0
  45. package/src/client/ssr/common/Translate.js +26 -0
  46. package/src/client/ssr/common/Worker.js +28 -0
  47. package/src/client/ssr/{body-components → components/body}/CacheControl.js +1 -1
  48. package/src/client/ssr/{body-components → components/body}/DefaultSplashScreen.js +15 -4
  49. package/src/client/ssr/components/head/Pwa.js +146 -0
  50. package/src/client/ssr/pages/404.js +12 -0
  51. package/src/client/ssr/pages/500.js +12 -0
  52. package/src/client/ssr/pages/maintenance.js +14 -0
  53. package/src/client/ssr/pages/offline.js +21 -0
  54. package/src/client/sw/default.sw.js +13 -9
  55. package/src/db/DataBaseProvider.js +12 -1
  56. package/src/db/mongo/MongooseDB.js +0 -1
  57. package/src/mailer/EmailRender.js +1 -1
  58. package/src/server/backup.js +82 -70
  59. package/src/server/client-build-live.js +6 -0
  60. package/src/server/client-build.js +76 -73
  61. package/src/server/client-formatted.js +11 -1
  62. package/src/server/client-icons.js +1 -1
  63. package/src/server/conf.js +60 -12
  64. package/src/server/crypto.js +91 -0
  65. package/src/server/dns.js +42 -13
  66. package/src/server/network.js +94 -7
  67. package/src/server/proxy.js +27 -27
  68. package/src/server/runtime.js +27 -8
  69. package/.github/workflows/test.yml +0 -80
  70. package/src/client/ssr/head-components/Microdata.js +0 -11
  71. package/src/cron.js +0 -30
  72. package/src/server/cron.js +0 -35
  73. /package/src/client/ssr/{email-components → components/email}/DefaultRecoverEmail.js +0 -0
  74. /package/src/client/ssr/{email-components → components/email}/DefaultVerifyEmail.js +0 -0
  75. /package/src/client/ssr/{head-components → components/head}/Css.js +0 -0
  76. /package/src/client/ssr/{head-components → components/head}/DefaultScripts.js +0 -0
  77. /package/src/client/ssr/{head-components → components/head}/Production.js +0 -0
  78. /package/src/client/ssr/{head-components → components/head}/PwaDefault.js +0 -0
  79. /package/src/client/ssr/{head-components → components/head}/Seo.js +0 -0
@@ -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')
@@ -240,14 +247,14 @@ const buildClientSrc = async (
240
247
  }
241
248
 
242
249
  fs.writeFileSync(
243
- `./src/client/ssr/head-components/${toClientVariableName}Scripts.js`,
244
- formattedSrc(fs.readFileSync(`./src/client/ssr/head-components/${fromClientVariableName}Scripts.js`, 'utf8')),
250
+ `./src/client/ssr/components/head/${toClientVariableName}Scripts.js`,
251
+ formattedSrc(fs.readFileSync(`./src/client/ssr/components/head/${fromClientVariableName}Scripts.js`, 'utf8')),
245
252
  'utf8',
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
 
@@ -560,7 +567,7 @@ const validateTemplatePath = (absolutePath = '') => {
560
567
  const confServer = DefaultConf.server[host][path];
561
568
  const confClient = DefaultConf.client[client];
562
569
  const confSsr = DefaultConf.ssr[ssr];
563
- const clients = Object.keys(confClient).concat(['core', 'test']);
570
+ const clients = Object.keys(confClient).concat(['core', 'test', 'default']);
564
571
 
565
572
  if (absolutePath.match('src/api') && !confServer.apis.find((p) => absolutePath.match(`src/api/${p}/`))) {
566
573
  return false;
@@ -584,14 +591,14 @@ const validateTemplatePath = (absolutePath = '') => {
584
591
  return false;
585
592
  }
586
593
  if (
587
- absolutePath.match('src/client/ssr/body-components') &&
588
- !confSsr.body.find((p) => absolutePath.match(`src/client/ssr/body-components/${p}.js`))
594
+ absolutePath.match('src/client/ssr/components/body') &&
595
+ !confSsr.body.find((p) => absolutePath.match(`src/client/ssr/components/body/${p}.js`))
589
596
  ) {
590
597
  return false;
591
598
  }
592
599
  if (
593
- absolutePath.match('src/client/ssr/head-components') &&
594
- !confSsr.head.find((p) => absolutePath.match(`src/client/ssr/head-components/${p}.js`))
600
+ absolutePath.match('src/client/ssr/components/head') &&
601
+ !confSsr.head.find((p) => absolutePath.match(`src/client/ssr/components/head/${p}.js`))
595
602
  ) {
596
603
  return false;
597
604
  }
@@ -599,6 +606,7 @@ const validateTemplatePath = (absolutePath = '') => {
599
606
  if (
600
607
  absolutePath.match('/client') &&
601
608
  absolutePath.match('.index.js') &&
609
+ !absolutePath.match('/offline') &&
602
610
  !clients.find((p) => absolutePath.match(`src/client/${capFirst(p)}.index.js`))
603
611
  ) {
604
612
  return false;
@@ -659,6 +667,16 @@ const getDeployGroupId = () => {
659
667
  return 'dd';
660
668
  };
661
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
+
662
680
  const getCronBackUpFolder = (host = '', path = '') => {
663
681
  return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
664
682
  };
@@ -670,7 +688,7 @@ const execDeploy = async (options = { deployId: 'default' }) => {
670
688
  shellExec(Cmd.run(deployId));
671
689
  return await new Promise(async (resolve) => {
672
690
  const maxTime = 1000 * 60 * 5;
673
- const minTime = 10000 * 2;
691
+ const minTime = 7 * 1000;
674
692
  const intervalTime = 1000;
675
693
  let currentTime = 0;
676
694
  const attempt = () => {
@@ -695,6 +713,7 @@ const execDeploy = async (options = { deployId: 'default' }) => {
695
713
  const deployRun = async (dataDeploy, reset) => {
696
714
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
697
715
  if (reset) fs.writeFileSync(`./tmp/runtime-router.json`, '{}', 'utf8');
716
+ await fixDependencies();
698
717
  for (const deploy of dataDeploy) await execDeploy(deploy);
699
718
  const { failed } = await deployTest(dataDeploy);
700
719
  if (failed.length > 0) {
@@ -831,6 +850,10 @@ const Cmd = {
831
850
  conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
832
851
  replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
833
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
+ },
834
857
  };
835
858
 
836
859
  const fixDependencies = async () => {
@@ -850,6 +873,27 @@ const fixDependencies = async () => {
850
873
  );
851
874
  };
852
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
+
853
897
  export {
854
898
  Cmd,
855
899
  Config,
@@ -878,4 +922,8 @@ export {
878
922
  getRestoreCronCmd,
879
923
  mergeBackUp,
880
924
  fixDependencies,
925
+ getDeployId,
926
+ maintenancePath,
927
+ maintenanceMiddleware,
928
+ setUpProxyMaintenanceServer,
881
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
+ };
@@ -9,7 +9,8 @@ import { loggerFactory, loggerMiddleware } from './logger.js';
9
9
  import { listenPortController, network } from './network.js';
10
10
  import { orderArrayFromAttrInt } from '../client/components/core/CommonJs.js';
11
11
  import { createSslServer, sslRedirectMiddleware } from './ssl.js';
12
- import { buildProxyRouter } from './conf.js';
12
+ import { buildProxyRouter, maintenanceMiddleware } from './conf.js';
13
+ import { getRootDirectory } from './process.js';
13
14
 
14
15
  dotenv.config();
15
16
 
@@ -25,6 +26,9 @@ const buildProxy = async () => {
25
26
  for (let port of Object.keys(proxyRouter)) {
26
27
  port = parseInt(port);
27
28
  const hosts = proxyRouter[port];
29
+ const proxyPath = '/';
30
+ const proxyHost = 'localhost';
31
+ const runningData = { host: proxyHost, path: proxyPath, client: null, runtime: 'nodejs', meta: import.meta };
28
32
  const app = express();
29
33
 
30
34
  // set logger
@@ -47,6 +51,7 @@ const buildProxy = async () => {
47
51
  onProxyReq: (proxyReq, req, res, options) => {
48
52
  // https://wtools.io/check-http-status-code
49
53
  // http://nexodev.org
54
+ maintenanceMiddleware(req, res, port, proxyRouter);
50
55
  sslRedirectMiddleware(req, res, port, proxyRouter);
51
56
  },
52
57
  pathRewrite: {
@@ -54,30 +59,27 @@ const buildProxy = async () => {
54
59
  // '^/target-path': '/',
55
60
  },
56
61
  };
57
-
58
- // build router
59
- Object.keys(hosts).map((hostKey) => {
60
- let { host, path, target, proxy, peer } = hosts[hostKey];
61
- if (process.env.NODE_ENV === 'development') host = `localhost`;
62
-
63
- if (!proxy.includes(port)) return;
64
- const absoluteHost = [80, 443].includes(port)
65
- ? `${host}${path === '/' ? '' : path}`
66
- : `${host}:${port}${path === '/' ? '' : path}`;
67
-
68
- if (!(absoluteHost in options.router)) options.router[absoluteHost] = target;
69
- });
70
- if (Object.keys(options.router).length === 0) continue;
71
-
72
- // order router
73
- const router = {};
74
- for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(options.router), 'length'))
75
- router[absoluteHostKey] = options.router[absoluteHostKey];
76
- options.router = router;
77
-
78
- // instance proxy server
79
- const proxyPath = '/';
80
- const proxyHost = 'localhost';
62
+ if (!process.argv.includes('maintenance')) {
63
+ // build router
64
+ Object.keys(hosts).map((hostKey) => {
65
+ let { host, path, target, proxy, peer } = hosts[hostKey];
66
+ if (process.env.NODE_ENV === 'development') host = `localhost`;
67
+
68
+ if (!proxy.includes(port)) return;
69
+ const absoluteHost = [80, 443].includes(port)
70
+ ? `${host}${path === '/' ? '' : path}`
71
+ : `${host}:${port}${path === '/' ? '' : path}`;
72
+
73
+ if (!(absoluteHost in options.router)) options.router[absoluteHost] = target;
74
+ });
75
+ if (Object.keys(options.router).length === 0) continue;
76
+
77
+ // order router
78
+ const router = {};
79
+ for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(options.router), 'length'))
80
+ router[absoluteHostKey] = options.router[absoluteHostKey];
81
+ options.router = router;
82
+ }
81
83
 
82
84
  const filter = false
83
85
  ? (pathname, req) => {
@@ -88,8 +90,6 @@ const buildProxy = async () => {
88
90
  app.use(proxyPath, createProxyMiddleware(filter, options));
89
91
  await network.port.portClean(port);
90
92
 
91
- const runningData = { host: proxyHost, path: proxyPath, client: null, runtime: 'nodejs', meta: import.meta };
92
-
93
93
  switch (process.env.NODE_ENV) {
94
94
  case 'production':
95
95
  switch (port) {