underpost 2.8.64 → 2.8.67

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 (51) hide show
  1. package/.vscode/extensions.json +3 -2
  2. package/.vscode/settings.json +2 -0
  3. package/CHANGELOG.md +24 -4
  4. package/README.md +39 -2
  5. package/bin/deploy.js +1205 -131
  6. package/bin/file.js +8 -0
  7. package/bin/index.js +1 -233
  8. package/cli.md +451 -0
  9. package/docker-compose.yml +1 -1
  10. package/jsdoc.json +1 -1
  11. package/manifests/calico-custom-resources.yaml +25 -0
  12. package/manifests/deployment/adminer/deployment.yaml +32 -0
  13. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  14. package/manifests/deployment/adminer/service.yaml +13 -0
  15. package/manifests/mongodb-4.4/service-deployment.yaml +1 -1
  16. package/manifests/postgresql/configmap.yaml +9 -0
  17. package/manifests/postgresql/kustomization.yaml +10 -0
  18. package/manifests/postgresql/pv.yaml +15 -0
  19. package/manifests/postgresql/pvc.yaml +13 -0
  20. package/manifests/postgresql/service.yaml +10 -0
  21. package/manifests/postgresql/statefulset.yaml +37 -0
  22. package/manifests/valkey/statefulset.yaml +6 -4
  23. package/package.json +3 -9
  24. package/src/api/user/user.service.js +13 -10
  25. package/src/cli/cluster.js +113 -11
  26. package/src/cli/db.js +18 -8
  27. package/src/cli/deploy.js +157 -58
  28. package/src/cli/fs.js +14 -3
  29. package/src/cli/image.js +0 -68
  30. package/src/cli/index.js +312 -0
  31. package/src/cli/monitor.js +170 -26
  32. package/src/cli/repository.js +5 -2
  33. package/src/client/components/core/Account.js +3 -3
  34. package/src/client/components/core/CalendarCore.js +0 -1
  35. package/src/client/components/core/Css.js +0 -1
  36. package/src/client/components/core/CssCore.js +2 -0
  37. package/src/client/components/core/EventsUI.js +1 -1
  38. package/src/client/components/core/JoyStick.js +2 -2
  39. package/src/client/components/core/Modal.js +1 -0
  40. package/src/client/components/core/RichText.js +1 -11
  41. package/src/index.js +9 -8
  42. package/src/mailer/MailerProvider.js +3 -0
  43. package/src/server/client-build.js +13 -0
  44. package/src/server/conf.js +48 -0
  45. package/src/server/dns.js +47 -17
  46. package/src/server/json-schema.js +77 -0
  47. package/src/server/peer.js +2 -2
  48. package/src/server/proxy.js +4 -4
  49. package/src/server/runtime.js +24 -9
  50. package/src/server/start.js +122 -0
  51. package/src/server/valkey.js +25 -11
@@ -7,28 +7,18 @@ const RichText = {
7
7
  Render: async function (options = { id: '', parentIdModal: '' }) {
8
8
  const id = options?.id ? options.id : getId(this.Tokens, 'rich-text-');
9
9
  this.Tokens[id] = {};
10
- let top, height;
11
10
  setTimeout(() => {
12
11
  const easyMDE = new EasyMDE({
13
12
  element: s(`.${id}`),
13
+ hideIcons: ['fullscreen', 'side-by-side'],
14
14
  onToggleFullScreen: (onFs) => {
15
15
  if (onFs) {
16
16
  if (options.parentIdModal) {
17
- s(`.btn-bar-modal-container-${options.parentIdModal}`).classList.add('hide');
18
- top = newInstance(s(`.${options.parentIdModal}`).style.top);
19
- height = newInstance(s(`.${options.parentIdModal}`).style.height);
20
- s(`.${options.parentIdModal}`).style.top = '0px';
21
- s(`.${options.parentIdModal}`).style.height = `${window.innerHeight}px`;
22
17
  }
23
18
  // Modal.cleanUI();
24
- if (s(`.slide-menu-top-bar`)) s(`.slide-menu-top-bar`).classList.add('hide');
25
19
  } else {
26
20
  if (options.parentIdModal) {
27
- s(`.btn-bar-modal-container-${options.parentIdModal}`).classList.remove('hide');
28
- s(`.${options.parentIdModal}`).style.top = top;
29
- s(`.${options.parentIdModal}`).style.height = height;
30
21
  }
31
- if (s(`.slide-menu-top-bar`)) s(`.slide-menu-top-bar`).classList.add('remove');
32
22
  // Modal.restoreUI();
33
23
  }
34
24
  },
package/src/index.js CHANGED
@@ -16,6 +16,7 @@ import UnderpostRepository from './cli/repository.js';
16
16
  import UnderpostScript from './cli/script.js';
17
17
  import UnderpostSecret from './cli/secrets.js';
18
18
  import UnderpostTest from './cli/test.js';
19
+ import UnderpostStartUp from './server/start.js';
19
20
 
20
21
  /**
21
22
  * Underpost main module methods
@@ -29,7 +30,7 @@ class Underpost {
29
30
  * @type {String}
30
31
  * @memberof Underpost
31
32
  */
32
- static version = 'v2.8.64';
33
+ static version = 'v2.8.67';
33
34
  /**
34
35
  * Repository cli API
35
36
  * @static
@@ -51,6 +52,13 @@ class Underpost {
51
52
  * @memberof Underpost
52
53
  */
53
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;
54
62
  /**
55
63
  * Cluster cli API
56
64
  * @static
@@ -93,13 +101,6 @@ class Underpost {
93
101
  * @memberof Underpost
94
102
  */
95
103
  static deploy = UnderpostDeploy.API;
96
- /**
97
- * Deployment cli NETWORK
98
- * @static
99
- * @type {UnderpostDeploy.NETWORK}
100
- * @memberof Underpost
101
- */
102
- static deployNetwork = UnderpostDeploy.NETWORK;
103
104
  /**
104
105
  * Cron cli API
105
106
  * @static
@@ -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
@@ -683,6 +683,19 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
683
683
  root file where the route starts, such as index.js, app.js, routes.js, etc ... */
684
684
 
685
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
+ }
686
699
  }
687
700
 
688
701
  if (client) {
@@ -923,6 +923,53 @@ const mergeFile = async (parts = [], outputFilePath) => {
923
923
  });
924
924
  };
925
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
+
926
973
  const getRestoreCronCmd = async (options = { host: '', path: '', conf: {}, deployId: '' }) => {
927
974
  const { host, path, conf, deployId } = options;
928
975
  const { runtime, db, git, directory } = conf[host][path];
@@ -1166,4 +1213,5 @@ export {
1166
1213
  pathPortAssignmentFactory,
1167
1214
  deployRangePortFactory,
1168
1215
  awaitDeployMonitor,
1216
+ rebuildConfFactory,
1169
1217
  };
package/src/server/dns.js CHANGED
@@ -5,6 +5,9 @@ import validator from 'validator';
5
5
  import { publicIp, publicIpv4, publicIpv6 } from 'public-ip';
6
6
  import { loggerFactory } from './logger.js';
7
7
  import UnderpostRootEnv from '../cli/env.js';
8
+ import dns from 'node:dns';
9
+ import os from 'node:os';
10
+ import { shellExec } from './process.js';
8
11
 
9
12
  dotenv.config();
10
13
 
@@ -18,35 +21,59 @@ const ip = {
18
21
  },
19
22
  };
20
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
+
21
38
  class Dns {
22
39
  static callback = async function (deployList) {
23
40
  // Network topology configuration:
24
41
  // LAN -> [NAT-VPS](modem/router device) -> WAN
25
42
  // enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
26
- // 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
+
27
49
  // verify inet ip proxy server address
28
50
  // DHCP (Dynamic Host Configuration Protocol) LAN reserver IP -> MAC ID
29
51
  // LAN server or device's local servers port -> 3000-3100 (2999-3101)
30
52
  // DNS Records: [ANAME](Address Dynamic) -> [A](ipv4) host | [AAAA](ipv6) host -> [public-ip]
31
53
  // Forward the router's TCP/UDP ports to the LAN device's IP address
32
- for (const _deployId of deployList.split(',')) {
33
- const deployId = _deployId.trim();
34
- const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
35
- const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
36
- const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
37
-
38
- let testIp;
39
-
40
- try {
41
- testIp = await ip.public.ipv4();
42
- } catch (error) {
43
- logger.error(error, { testIp, stack: error.stack });
44
- }
54
+ const isOnline = await isInternetConnection();
55
+
56
+ if (!isOnline) return;
45
57
 
46
- const currentIp = UnderpostRootEnv.API.get('ip');
58
+ let testIp;
47
59
 
48
- if (testIp && typeof testIp === 'string' && validator.isIP(testIp) && currentIp !== testIp) {
49
- logger.info(`new ip`, testIp);
60
+ try {
61
+ testIp = await ip.public.ipv4();
62
+ } catch (error) {
63
+ logger.error(error, { testIp, stack: error.stack });
64
+ }
65
+
66
+ const currentIp = UnderpostRootEnv.API.get('ip');
67
+
68
+ if (validator.isIP(testIp) && currentIp !== testIp) {
69
+ logger.info(`new ip`, testIp);
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'));
50
77
  for (const recordType of Object.keys(confCronData.records)) {
51
78
  switch (recordType) {
52
79
  case 'A':
@@ -68,6 +95,7 @@ class Dns {
68
95
  if (verifyIp === testIp) {
69
96
  logger.info('ip updated successfully', testIp);
70
97
  UnderpostRootEnv.API.set('ip', testIp);
98
+ UnderpostRootEnv.API.delete('monitor-input');
71
99
  } else logger.error('ip not updated', testIp);
72
100
  } catch (error) {
73
101
  logger.error(error, error.stack);
@@ -102,3 +130,5 @@ class Dns {
102
130
  }
103
131
 
104
132
  export default Dns;
133
+
134
+ export { Dns, ip, isInternetConnection, getLocalIPv4Address };
@@ -0,0 +1,77 @@
1
+ function isPlainObject(obj) {
2
+ return obj ? typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.prototype : false;
3
+ }
4
+
5
+ const supportType = ['string', 'number', 'array', 'object', 'boolean', 'integer'];
6
+
7
+ function getType(type) {
8
+ if (!type) type = 'string';
9
+ if (supportType.indexOf(type) !== -1) {
10
+ return type;
11
+ }
12
+ return typeof type;
13
+ }
14
+
15
+ function isSchema(object) {
16
+ if (supportType.indexOf(object.type) !== -1) {
17
+ return true;
18
+ }
19
+ return false;
20
+ }
21
+
22
+ function handleSchema(json, schema) {
23
+ Object.assign(schema, json);
24
+ if (schema.type === 'object') {
25
+ delete schema.properties;
26
+ parse(json.properties, schema);
27
+ }
28
+ if (schema.type === 'array') {
29
+ delete schema.items;
30
+ schema.items = {};
31
+ parse(json.items, schema.items);
32
+ }
33
+ }
34
+
35
+ function handleArray(arr, schema) {
36
+ schema.type = 'array';
37
+ let props = (schema.items = {});
38
+ parse(arr[0], props);
39
+ }
40
+
41
+ function handleObject(json, schema) {
42
+ if (isSchema(json)) {
43
+ return handleSchema(json, schema);
44
+ }
45
+ schema.type = 'object';
46
+ schema.required = [];
47
+ let props = (schema.properties = {});
48
+ for (let key in json) {
49
+ let item = json[key];
50
+ let curSchema = (props[key] = {});
51
+ if (key[0] === '*') {
52
+ delete props[key];
53
+ key = key.substr(1);
54
+ schema.required.push(key);
55
+ curSchema = props[key] = {};
56
+ }
57
+ parse(item, curSchema);
58
+ }
59
+ }
60
+
61
+ function parse(json, schema) {
62
+ if (Array.isArray(json)) {
63
+ handleArray(json, schema);
64
+ } else if (isPlainObject(json)) {
65
+ handleObject(json, schema);
66
+ } else {
67
+ schema.type = getType(json);
68
+ }
69
+ }
70
+
71
+ function ejs(data) {
72
+ let JsonSchema = {};
73
+ parse(data, JsonSchema);
74
+ return JsonSchema;
75
+ }
76
+
77
+ export { ejs };
@@ -2,7 +2,7 @@ import { PeerServer } from 'peer';
2
2
  import dotenv from 'dotenv';
3
3
  import { loggerFactory } from './logger.js';
4
4
  import fs from 'fs-extra';
5
- import { listenServerFactory } from './network.js';
5
+ import UnderpostStartUp from './start.js';
6
6
 
7
7
  dotenv.config();
8
8
 
@@ -25,7 +25,7 @@ const createPeerServer = async ({ port, devPort, origins, host, path }) => {
25
25
  // cert: fs.readFileSync(''),
26
26
  // ca: fs.readFileSync(''),
27
27
  };
28
- const peerServer = listenServerFactory(() => PeerServer(options));
28
+ const peerServer = UnderpostStartUp.API.listenServerFactory(() => PeerServer(options));
29
29
 
30
30
  return { options, peerServer, meta: import.meta };
31
31
  };
@@ -5,9 +5,9 @@ import dotenv from 'dotenv';
5
5
 
6
6
  import { createProxyMiddleware } from 'http-proxy-middleware';
7
7
  import { loggerFactory, loggerMiddleware } from './logger.js';
8
- import { listenPortController } from './network.js';
9
8
  import { createSslServer, sslRedirectMiddleware } from './ssl.js';
10
9
  import { buildPortProxyRouter, buildProxyRouter, maintenanceMiddleware } from './conf.js';
10
+ import UnderpostStartUp from './start.js';
11
11
 
12
12
  dotenv.config();
13
13
 
@@ -71,11 +71,11 @@ const buildProxy = async () => {
71
71
  switch (port) {
72
72
  case 443:
73
73
  const { ServerSSL } = await createSslServer(app, hosts);
74
- await listenPortController(ServerSSL, port, runningData);
74
+ await UnderpostStartUp.API.listenPortController(ServerSSL, port, runningData);
75
75
  break;
76
76
 
77
77
  default:
78
- await listenPortController(app, port, runningData);
78
+ await UnderpostStartUp.API.listenPortController(app, port, runningData);
79
79
 
80
80
  break;
81
81
  }
@@ -83,7 +83,7 @@ const buildProxy = async () => {
83
83
  break;
84
84
 
85
85
  default:
86
- await listenPortController(app, port, runningData);
86
+ await UnderpostStartUp.API.listenPortController(app, port, runningData);
87
87
 
88
88
  break;
89
89
  }
@@ -9,7 +9,7 @@ import compression from 'compression';
9
9
 
10
10
  import { createServer } from 'http';
11
11
  import { getRootDirectory } from './process.js';
12
- import { listenPortController, logRuntimeRouter, listenServerFactory } from './network.js';
12
+ import UnderpostStartUp from './start.js';
13
13
  import { loggerFactory, loggerMiddleware } from './logger.js';
14
14
  import { getCapVariableName, newInstance } from '../client/components/core/CommonJs.js';
15
15
  import { Xampp } from '../runtime/xampp/Xampp.js';
@@ -21,6 +21,7 @@ import { Lampp } from '../runtime/lampp/Lampp.js';
21
21
  import { getDeployId } from './conf.js';
22
22
  import { JSONweb, ssrFactory } from './client-formatted.js';
23
23
  import Underpost from '../index.js';
24
+ import { createValkeyConnection } from './valkey.js';
24
25
 
25
26
  dotenv.config();
26
27
 
@@ -67,6 +68,7 @@ const buildRuntime = async () => {
67
68
  peer,
68
69
  singleReplica,
69
70
  replicas,
71
+ valkey,
70
72
  } = confServer[host][path];
71
73
 
72
74
  if (singleReplica && replicas && replicas.length > 0 && !singleReplicaHosts.includes(host)) {
@@ -140,6 +142,8 @@ const buildRuntime = async () => {
140
142
  // if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
141
143
  // $_SERVER['HTTPS'] = 'on';
142
144
  // }
145
+ // For plugins:
146
+ // define( 'FS_METHOD', 'direct' );
143
147
 
144
148
  // ErrorDocument 404 /custom_404.html
145
149
  // ErrorDocument 500 /custom_50x.html
@@ -182,7 +186,11 @@ const buildRuntime = async () => {
182
186
  // RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
183
187
  // RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301]
184
188
 
185
- await listenPortController(listenServerFactory(), port, runningData);
189
+ await UnderpostStartUp.API.listenPortController(
190
+ UnderpostStartUp.API.listenServerFactory(),
191
+ port,
192
+ runningData,
193
+ );
186
194
  break;
187
195
  case 'xampp':
188
196
  if (!Xampp.enabled()) continue;
@@ -229,7 +237,11 @@ const buildRuntime = async () => {
229
237
  // if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
230
238
  // $_SERVER['HTTPS'] = 'on';
231
239
  // }
232
- await listenPortController(listenServerFactory(), port, runningData);
240
+ await UnderpostStartUp.API.listenPortController(
241
+ UnderpostStartUp.API.listenServerFactory(),
242
+ port,
243
+ runningData,
244
+ );
233
245
  break;
234
246
  case 'nodejs':
235
247
  const app = express();
@@ -282,7 +294,7 @@ const buildRuntime = async () => {
282
294
  currentPort += 2;
283
295
  const staticPort = newInstance(currentPort);
284
296
 
285
- await listenPortController(app, staticPort, runningData);
297
+ await UnderpostStartUp.API.listenPortController(app, staticPort, runningData);
286
298
  currentPort++;
287
299
  continue;
288
300
  }
@@ -333,7 +345,7 @@ const buildRuntime = async () => {
333
345
  // }),
334
346
  // );
335
347
 
336
- await listenPortController(app, port, runningData);
348
+ await UnderpostStartUp.API.listenPortController(app, port, runningData);
337
349
  break;
338
350
  }
339
351
 
@@ -353,6 +365,9 @@ const buildRuntime = async () => {
353
365
 
354
366
  if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
355
367
 
368
+ // valkey server
369
+ await createValkeyConnection({ host, path }, valkey);
370
+
356
371
  if (mailer) {
357
372
  const mailerSsrConf = confSSR[getCapVariableName(client)];
358
373
  await MailerProvider.load({
@@ -441,7 +456,7 @@ const buildRuntime = async () => {
441
456
  port,
442
457
  origins,
443
458
  });
444
- await listenPortController(listenServerFactory(), port, {
459
+ await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), port, {
445
460
  runtime: 'nodejs',
446
461
  client: null,
447
462
  host,
@@ -461,7 +476,7 @@ const buildRuntime = async () => {
461
476
  path,
462
477
  });
463
478
 
464
- await listenPortController(peerServer, peerPort, {
479
+ await UnderpostStartUp.API.listenPortController(peerServer, peerPort, {
465
480
  runtime: 'nodejs',
466
481
  client: null,
467
482
  host,
@@ -470,7 +485,7 @@ const buildRuntime = async () => {
470
485
  });
471
486
  }
472
487
 
473
- await listenPortController(server, port, runningData);
488
+ await UnderpostStartUp.API.listenPortController(server, port, runningData);
474
489
 
475
490
  break;
476
491
  default:
@@ -483,7 +498,7 @@ const buildRuntime = async () => {
483
498
  if (Xampp.enabled() && Xampp.router) Xampp.initService();
484
499
  if (Lampp.enabled() && Lampp.router) Lampp.initService();
485
500
 
486
- logRuntimeRouter();
501
+ UnderpostStartUp.API.logRuntimeRouter();
487
502
  };
488
503
 
489
504
  export { buildRuntime };
@@ -0,0 +1,122 @@
1
+ import UnderpostDeploy from '../cli/deploy.js';
2
+ import fs from 'fs-extra';
3
+ import { awaitDeployMonitor } from './conf.js';
4
+ import { actionInitLog, loggerFactory } from './logger.js';
5
+ import { shellCd, shellExec } from './process.js';
6
+ import UnderpostRootEnv from '../cli/env.js';
7
+
8
+ const logger = loggerFactory(import.meta);
9
+
10
+ class UnderpostStartUp {
11
+ static API = {
12
+ logRuntimeRouter: () => {
13
+ const displayLog = {};
14
+
15
+ for (const host of Object.keys(UnderpostDeploy.NETWORK))
16
+ for (const path of Object.keys(UnderpostDeploy.NETWORK[host]))
17
+ displayLog[UnderpostDeploy.NETWORK[host][path].publicHost] = UnderpostDeploy.NETWORK[host][path].local;
18
+
19
+ logger.info('Runtime network', displayLog);
20
+ },
21
+ listenServerFactory: (logic = async () => {}) => {
22
+ return {
23
+ listen: async (...args) => {
24
+ const msDelta = 1000;
25
+ const msMax = 30 * 24 * 60 * 60 * 1000; // ~ 1 month
26
+ let msCount = 0;
27
+ setInterval(() => {
28
+ msCount += msDelta;
29
+ if (msCount >= msMax) {
30
+ const message = 'Listen server factory timeout';
31
+ logger.error(message);
32
+ throw new Error(message);
33
+ }
34
+ }, msDelta);
35
+ return logic ? await logic(...args) : undefined, args[1]();
36
+ },
37
+ };
38
+ },
39
+ listenPortController: async (server, port, metadata) =>
40
+ new Promise((resolve) => {
41
+ try {
42
+ if (port === ':') {
43
+ server.listen(port, actionInitLog);
44
+ return resolve(true);
45
+ }
46
+
47
+ const { host, path, client, runtime, meta } = metadata;
48
+ const error = [];
49
+ if (port === undefined) error.push(`port`);
50
+ if (host === undefined) error.push(`host`);
51
+ if (path === undefined) error.push(`path`);
52
+ if (client === undefined) error.push(`client`);
53
+ if (runtime === undefined) error.push(`runtime`);
54
+ if (meta === undefined) error.push(`meta`);
55
+ if (error.length > 0) throw new Error('Listen port controller requires values: ' + error.join(', '));
56
+
57
+ server.listen(port, () => {
58
+ if (!UnderpostDeploy.NETWORK[host]) UnderpostDeploy.NETWORK[host] = {};
59
+ UnderpostDeploy.NETWORK[host][path] = {
60
+ meta,
61
+ client,
62
+ runtime,
63
+ port,
64
+ publicHost:
65
+ port === 80
66
+ ? `http://${host}${path}`
67
+ : port === 443
68
+ ? `https://${host}${path}`
69
+ : `http://${host}:${port}${path}`,
70
+ local: `http://localhost:${port}${path}`,
71
+ apis: metadata.apis,
72
+ };
73
+
74
+ return resolve(true);
75
+ });
76
+ } catch (error) {
77
+ logger.error(error, { metadata, port, stack: error.stack });
78
+ resolve(false);
79
+ }
80
+ }),
81
+
82
+ async callback(deployId = 'default', env = 'development', options = { build: false, run: false }) {
83
+ if (options.build === true) await UnderpostStartUp.API.build(deployId, env);
84
+ if (options.run === true) await UnderpostStartUp.API.run(deployId, env);
85
+ },
86
+ async build(deployId = 'default', env = 'development') {
87
+ const buildBasePath = `/home/dd`;
88
+ const repoName = `engine-${deployId.split('-')[1]}`;
89
+ shellExec(`cd ${buildBasePath} && underpost clone underpostnet/${repoName}`);
90
+ shellExec(`cd ${buildBasePath} && sudo mv ./${repoName} ./engine`);
91
+ shellExec(`cd ${buildBasePath}/engine && underpost clone underpostnet/${repoName}-private`);
92
+ shellExec(`cd ${buildBasePath}/engine && sudo mv ./${repoName}-private ./engine-private`);
93
+ shellCd(`${buildBasePath}/engine`);
94
+ shellExec(`npm install`);
95
+ shellExec(`node bin/deploy conf ${deployId} ${env}`);
96
+ if (fs.existsSync('./engine-private/itc-scripts')) {
97
+ const itcScripts = await fs.readdir('./engine-private/itc-scripts');
98
+ for (const itcScript of itcScripts)
99
+ if (itcScript.match(deployId)) shellExec(`node ./engine-private/itc-scripts/${itcScript}`);
100
+ }
101
+ shellExec(`node bin/deploy build-full-client ${deployId}`);
102
+ },
103
+ async run(deployId = 'default', env = 'development') {
104
+ const runCmd = env === 'production' ? 'run prod-img' : 'run dev-img';
105
+ if (fs.existsSync(`./engine-private/replica`)) {
106
+ const replicas = await fs.readdir(`./engine-private/replica`);
107
+ for (const replica of replicas) {
108
+ if (!replica.match(deployId)) continue;
109
+ shellExec(`node bin/deploy conf ${replica} ${env}`);
110
+ shellExec(`npm ${runCmd} deploy deploy-id:${replica}`, { async: true });
111
+ await awaitDeployMonitor(true);
112
+ }
113
+ }
114
+ shellExec(`node bin/deploy conf ${deployId} ${env}`);
115
+ shellExec(`npm ${runCmd} deploy deploy-id:${deployId}`, { async: true });
116
+ await awaitDeployMonitor(true);
117
+ UnderpostRootEnv.API.set('container-status', `${deployId}-${env}-running-deployment`);
118
+ },
119
+ };
120
+ }
121
+
122
+ export default UnderpostStartUp;