underpost 2.8.84 → 2.8.86

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 (128) hide show
  1. package/.env.development +1 -0
  2. package/.env.production +1 -0
  3. package/.env.test +1 -0
  4. package/.github/workflows/{ghpkg.yml → ghpkg.ci.yml} +1 -1
  5. package/.github/workflows/{npmpkg.yml → npmpkg.ci.yml} +1 -1
  6. package/.github/workflows/{publish.yml → publish.ci.yml} +1 -1
  7. package/.github/workflows/{pwa-microservices-template.page.yml → pwa-microservices-template-page.cd.yml} +2 -2
  8. package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
  9. package/.github/workflows/release.cd.yml +37 -0
  10. package/.vscode/settings.json +0 -1
  11. package/README.md +16 -10
  12. package/bin/build.js +15 -5
  13. package/bin/cyberia0.js +78 -0
  14. package/bin/db.js +1 -3
  15. package/bin/deploy.js +29 -431
  16. package/bin/file.js +26 -9
  17. package/cli.md +102 -61
  18. package/conf.js +1 -1
  19. package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
  20. package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
  21. package/manifests/grafana/deployment.yaml +57 -0
  22. package/manifests/grafana/kustomization.yaml +7 -0
  23. package/manifests/grafana/pvc.yaml +12 -0
  24. package/manifests/grafana/service.yaml +14 -0
  25. package/manifests/maas/gpu-diag.sh +1 -1
  26. package/manifests/maas/ssh-cluster-info.sh +14 -0
  27. package/manifests/prometheus/deployment.yaml +82 -0
  28. package/package.json +4 -7
  29. package/src/api/user/user.router.js +24 -1
  30. package/src/api/user/user.service.js +9 -38
  31. package/src/cli/cluster.js +83 -29
  32. package/src/cli/cron.js +12 -45
  33. package/src/cli/db.js +149 -0
  34. package/src/cli/deploy.js +40 -81
  35. package/src/cli/index.js +29 -6
  36. package/src/cli/monitor.js +9 -16
  37. package/src/cli/repository.js +12 -5
  38. package/src/cli/run.js +175 -7
  39. package/src/cli/ssh.js +32 -0
  40. package/src/client/Default.index.js +7 -5
  41. package/src/client/components/core/Account.js +7 -3
  42. package/src/client/components/core/Chat.js +1 -1
  43. package/src/client/components/core/CommonJs.js +24 -22
  44. package/src/client/components/core/Content.js +12 -12
  45. package/src/client/components/core/Css.js +262 -18
  46. package/src/client/components/core/CssCore.js +8 -8
  47. package/src/client/components/core/Docs.js +14 -61
  48. package/src/client/components/core/DropDown.js +137 -82
  49. package/src/client/components/core/EventsUI.js +92 -5
  50. package/src/client/components/core/Input.js +6 -1
  51. package/src/client/components/core/LoadingAnimation.js +8 -15
  52. package/src/client/components/core/LogIn.js +3 -0
  53. package/src/client/components/core/LogOut.js +1 -1
  54. package/src/client/components/core/Modal.js +601 -137
  55. package/src/client/components/core/NotificationManager.js +2 -2
  56. package/src/client/components/core/ObjectLayerEngine.js +638 -0
  57. package/src/client/components/core/Panel.js +158 -34
  58. package/src/client/components/core/PanelForm.js +12 -3
  59. package/src/client/components/core/Recover.js +6 -3
  60. package/src/client/components/core/Router.js +77 -17
  61. package/src/client/components/core/Scroll.js +65 -120
  62. package/src/client/components/core/SignUp.js +1 -0
  63. package/src/client/components/core/SocketIo.js +3 -3
  64. package/src/client/components/core/Translate.js +6 -2
  65. package/src/client/components/core/VanillaJs.js +48 -5
  66. package/src/client/components/core/Worker.js +3 -1
  67. package/src/client/components/default/CssDefault.js +17 -3
  68. package/src/client/components/default/MenuDefault.js +266 -47
  69. package/src/client/components/default/RoutesDefault.js +8 -14
  70. package/src/client/public/default/android-chrome-144x144.png +0 -0
  71. package/src/client/public/default/android-chrome-192x192.png +0 -0
  72. package/src/client/public/default/android-chrome-256x256.png +0 -0
  73. package/src/client/public/default/android-chrome-36x36.png +0 -0
  74. package/src/client/public/default/android-chrome-48x48.png +0 -0
  75. package/src/client/public/default/android-chrome-72x72.png +0 -0
  76. package/src/client/public/default/android-chrome-96x96.png +0 -0
  77. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  78. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  79. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  80. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  81. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  82. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  83. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  84. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  85. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  86. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  87. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  88. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  89. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  90. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  91. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  92. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  93. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  94. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  95. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  96. package/src/client/public/default/apple-touch-icon.png +0 -0
  97. package/src/client/public/default/assets/background/dark.jpg +0 -0
  98. package/src/client/public/default/assets/background/dark.svg +557 -0
  99. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  100. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  101. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  102. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  103. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  104. package/src/client/public/default/favicon-16x16.png +0 -0
  105. package/src/client/public/default/favicon-32x32.png +0 -0
  106. package/src/client/public/default/favicon.ico +0 -0
  107. package/src/client/public/default/mstile-144x144.png +0 -0
  108. package/src/client/public/default/mstile-150x150.png +0 -0
  109. package/src/client/public/default/mstile-310x150.png +0 -0
  110. package/src/client/public/default/mstile-310x310.png +0 -0
  111. package/src/client/public/default/mstile-70x70.png +0 -0
  112. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  113. package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
  114. package/src/index.js +9 -1
  115. package/src/mailer/MailerProvider.js +37 -0
  116. package/src/monitor.js +24 -0
  117. package/src/runtime/lampp/Dockerfile +30 -39
  118. package/src/runtime/lampp/Lampp.js +11 -2
  119. package/src/server/client-build-docs.js +205 -0
  120. package/src/server/client-build-live.js +1 -1
  121. package/src/server/client-build.js +16 -166
  122. package/src/server/client-dev-server.js +1 -1
  123. package/src/server/conf.js +14 -277
  124. package/src/server/proxy.js +1 -2
  125. package/src/server/start.js +3 -3
  126. package/src/server/valkey.js +102 -41
  127. package/docker-compose.yml +0 -67
  128. package/prometheus.yml +0 -36
@@ -31,20 +31,10 @@ const UserService = {
31
31
  const token = hashJWT({ email: req.body.email }, '15m');
32
32
  const payloadToken = hashJWT({ email: req.body.email }, '15m');
33
33
  const id = `${options.host}${options.path}`;
34
- const translate = {
35
- H1: {
36
- en: 'Recover your account',
37
- es: 'Recupera tu cuenta',
38
- },
39
- P1: {
40
- en: 'To recover your account, please click the button below:',
41
- es: 'Para recuperar tu cuenta, haz click en el botón de abajo:',
42
- },
43
- BTN_LABEL: {
44
- en: 'Recover Password',
45
- es: 'Recuperar Contraseña',
46
- },
47
- };
34
+ const translate = MailerProvider.instance[id].translateTemplates.recoverEmail;
35
+ const recoverUrl = `${process.env.NODE_ENV === 'development' ? 'http://' : 'https://'}${req.body.hostname}${
36
+ req.body.proxyPath
37
+ }recover?payload=${payloadToken}`;
48
38
  const sendResult = await MailerProvider.send({
49
39
  id,
50
40
  sendOptions: {
@@ -55,13 +45,8 @@ const UserService = {
55
45
  .replace('{{H1}}', translate.H1[req.lang])
56
46
  .replace('{{P1}}', translate.P1[req.lang])
57
47
  .replace('{{TOKEN}}', token)
58
- .replace(`{{COMPANY}}`, options.host) // html body
59
- .replace(
60
- '{{RECOVER_WEB_URL}}',
61
- `${process.env === 'development' ? 'http://' : 'https://'}${options.host}${options.path}${
62
- options.path === '/' ? 'recover' : `/recover`
63
- }?payload=${payloadToken}`,
64
- )
48
+ .replace(`{{COMPANY}}`, req.body.hostname) // html body
49
+ .replace('{{RECOVER_WEB_URL}}', recoverUrl)
65
50
  .replace('{{RECOVER_BTN_LABEL}}', translate.BTN_LABEL[req.lang]),
66
51
 
67
52
  attachments: [
@@ -98,18 +83,7 @@ const UserService = {
98
83
  },
99
84
  );
100
85
  }
101
- const translate = {
102
- H1: {
103
- en: 'Confirm Your Email',
104
- zh: '请确认您的电子邮箱',
105
- es: 'Confirma tu correo electrónico',
106
- },
107
- P1: {
108
- en: 'Email confirmed! Thanks.',
109
- zh: '电子邮箱已确认!感谢。',
110
- es: 'Correo electrónico confirmado! Gracias.',
111
- },
112
- };
86
+ const translate = MailerProvider.instance[id].translateTemplates.confirmEmail;
113
87
  const sendResult = await MailerProvider.send({
114
88
  id,
115
89
  sendOptions: {
@@ -120,7 +94,7 @@ const UserService = {
120
94
  .replace('{{H1}}', translate.H1[req.lang])
121
95
  .replace('{{P1}}', translate.P1[req.lang])
122
96
  .replace('{{TOKEN}}', token)
123
- .replace(`{{COMPANY}}`, options.host), // html body
97
+ .replace(`{{COMPANY}}`, req.body.hostname), // html body
124
98
  attachments: [
125
99
  // {
126
100
  // filename: 'logo.png',
@@ -444,7 +418,7 @@ const UserService = {
444
418
  _id: user._id,
445
419
  }).select(UserDto.select.get());
446
420
  } else throw new Error('invalid token');
447
- }
421
+ } else delete req.body.password;
448
422
 
449
423
  switch (req.params.id) {
450
424
  default: {
@@ -453,9 +427,6 @@ const UserService = {
453
427
  });
454
428
  switch (user.role) {
455
429
  case 'admin': {
456
- if (req.body.password !== undefined && req.body.password !== user.password)
457
- req.body.password = await hashPassword(req.body.password);
458
- else delete req.body.password;
459
430
  return await User.findByIdAndUpdate(req.params.id, req.body, {
460
431
  runValidators: true,
461
432
  });
@@ -5,6 +5,7 @@ import UnderpostBaremetal from './baremetal.js';
5
5
  import UnderpostDeploy from './deploy.js';
6
6
  import UnderpostTest from './test.js';
7
7
  import os from 'os';
8
+ import fs from 'fs-extra';
8
9
 
9
10
  const logger = loggerFactory(import.meta);
10
11
 
@@ -19,6 +20,7 @@ class UnderpostCluster {
19
20
  * @param {object} [options] - Configuration options for cluster initialization.
20
21
  * @param {boolean} [options.mongodb=false] - Deploy MongoDB.
21
22
  * @param {boolean} [options.mongodb4=false] - Deploy MongoDB 4.4.
23
+ * @param {String} [options.mongoDbHost=''] - Set custom mongo db host
22
24
  * @param {boolean} [options.mariadb=false] - Deploy MariaDB.
23
25
  * @param {boolean} [options.mysql=false] - Deploy MySQL.
24
26
  * @param {boolean} [options.postgresql=false] - Deploy PostgreSQL.
@@ -32,12 +34,13 @@ class UnderpostCluster {
32
34
  * @param {string} [options.nsUse=''] - Set the current kubectl namespace.
33
35
  * @param {boolean} [options.infoCapacity=false] - Display resource capacity information for the cluster.
34
36
  * @param {boolean} [options.infoCapacityPod=false] - Display resource capacity information for pods.
35
- * @param {boolean} [options.istio=false] - Deploy Istio service mesh.
36
37
  * @param {boolean} [options.pullImage=false] - Pull necessary Docker images before deployment.
37
38
  * @param {boolean} [options.dedicatedGpu=false] - Configure for dedicated GPU usage (e.g., NVIDIA GPU Operator).
38
39
  * @param {boolean} [options.kubeadm=false] - Initialize the cluster using Kubeadm.
39
40
  * @param {boolean} [options.k3s=false] - Initialize the cluster using K3s.
40
41
  * @param {boolean} [options.initHost=false] - Perform initial host setup (install Docker, Podman, Kind, Kubeadm, Helm).
42
+ * @param {boolean} [options.grafana=false] - Initialize the cluster with a Grafana deployment.
43
+ * @param {string} [options.prom=''] - Initialize the cluster with a Prometheus Operator deployment and monitor scrap for specified hosts.
41
44
  * @param {boolean} [options.uninstallHost=false] - Uninstall all host components.
42
45
  * @param {boolean} [options.config=false] - Apply general host configuration (SELinux, containerd, sysctl, firewalld).
43
46
  * @param {boolean} [options.worker=false] - Configure as a worker node (for Kubeadm or K3s join).
@@ -48,6 +51,7 @@ class UnderpostCluster {
48
51
  options = {
49
52
  mongodb: false,
50
53
  mongodb4: false,
54
+ mongoDbHost: '',
51
55
  mariadb: false,
52
56
  mysql: false,
53
57
  postgresql: false,
@@ -61,12 +65,13 @@ class UnderpostCluster {
61
65
  nsUse: '',
62
66
  infoCapacity: false,
63
67
  infoCapacityPod: false,
64
- istio: false,
65
68
  pullImage: false,
66
69
  dedicatedGpu: false,
67
70
  kubeadm: false,
68
71
  k3s: false,
69
72
  initHost: false,
73
+ grafana: false,
74
+ prom: '',
70
75
  uninstallHost: false,
71
76
  config: false,
72
77
  worker: false,
@@ -258,19 +263,56 @@ class UnderpostCluster {
258
263
  );
259
264
  }
260
265
 
266
+ if (options.grafana === true) {
267
+ shellExec(`kubectl delete deployment grafana --ignore-not-found`);
268
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/grafana`);
269
+ }
270
+
271
+ if (options.prom && typeof options.prom === 'string') {
272
+ shellExec(`kubectl delete deployment prometheus --ignore-not-found`);
273
+ shellExec(`kubectl delete configmap prometheus-config --ignore-not-found`);
274
+ shellExec(`kubectl delete service prometheus --ignore-not-found`);
275
+ // Prometheus server host: http://<prometheus-cluster-ip>:9090
276
+ const yaml = `${fs.readFileSync(`${underpostRoot}/manifests/prometheus/deployment.yaml`, 'utf8').replace(
277
+ '- targets: []',
278
+ `- targets: [${options.prom
279
+ .split(',')
280
+ .map((host) => `'${host}'`)
281
+ .join(',')}]`,
282
+ )}`;
283
+ console.log(yaml);
284
+ shellExec(`kubectl apply -f - <<EOF
285
+ ${yaml}
286
+ EOF
287
+ `);
288
+
289
+ // https://grafana.com/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/configuration/config-other-methods/prometheus/prometheus-operator/
290
+ // shellExec(
291
+ // `kubectl create -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/master/bundle.yaml`,
292
+ // );
293
+ // shellExec(`kubectl apply -f ${underpostRoot}/manifests/prometheus/prometheus-cr.yaml`);
294
+ // shellExec(`kubectl rollout status sts prometheus-prometheus -n default`);
295
+ // shellExec(`kubectl apply -f ${underpostRoot}/manifests/prometheus/prometheus-server.yaml`);
296
+ // shellExec(`helm repo add prometheus-community https://prometheus-community.github.io/helm-charts`);
297
+ // shellExec(`helm repo update`);
298
+ // shellExec(`helm install prometheus prometheus-community/prometheus`);
299
+ // shellExec(`kubectl rollout status deployment prometheus-server -n default`);
300
+ }
301
+
261
302
  if (options.full === true || options.valkey === true) {
262
303
  if (options.pullImage === true) {
263
- shellExec(`docker pull valkey/valkey:latest`);
264
- shellExec(`sudo podman pull valkey/valkey:latest`);
265
- if (!options.kubeadm && !options.k3s)
304
+ // shellExec(`sudo podman pull valkey/valkey:latest`);
305
+ if (!options.kubeadm && !options.k3s) {
266
306
  // Only load if not kubeadm/k3s (Kind needs it)
307
+ shellExec(`docker pull valkey/valkey:latest`);
267
308
  shellExec(`sudo kind load docker-image valkey/valkey:latest`);
268
- else if (options.kubeadm || options.k3s)
309
+ } else if (options.kubeadm || options.k3s)
269
310
  // For kubeadm/k3s, ensure it's available for containerd
270
311
  shellExec(`sudo crictl pull valkey/valkey:latest`);
271
312
  }
272
313
  shellExec(`kubectl delete statefulset valkey-service --ignore-not-found`);
273
314
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/valkey`);
315
+ await UnderpostTest.API.statusMonitor('valkey-service', 'Running', 'pods', 1000, 60);
274
316
  }
275
317
  if (options.full === true || options.mariadb === true) {
276
318
  shellExec(
@@ -279,18 +321,16 @@ class UnderpostCluster {
279
321
  shellExec(`kubectl delete statefulset mariadb-statefulset --ignore-not-found`);
280
322
 
281
323
  if (options.pullImage === true) {
282
- shellExec(`docker pull mariadb:latest`);
283
- shellExec(`sudo podman pull mariadb:latest`);
284
- if (!options.kubeadm && !options.k3s)
324
+ // shellExec(`sudo podman pull mariadb:latest`);
325
+ if (!options.kubeadm && !options.k3s) {
285
326
  // Only load if not kubeadm/k3s (Kind needs it)
327
+ shellExec(`docker pull mariadb:latest`);
286
328
  shellExec(`sudo kind load docker-image mariadb:latest`);
287
- else if (options.kubeadm || options.k3s)
329
+ } else if (options.kubeadm || options.k3s)
288
330
  // For kubeadm/k3s, ensure it's available for containerd
289
331
  shellExec(`sudo crictl pull mariadb:latest`);
290
332
  }
291
- if (options.kubeadm === true)
292
- // This storage class is specific to kubeadm setup
293
- shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml`);
333
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml`);
294
334
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb`);
295
335
  }
296
336
  if (options.full === true || options.mysql === true) {
@@ -304,11 +344,11 @@ class UnderpostCluster {
304
344
  }
305
345
  if (options.full === true || options.postgresql === true) {
306
346
  if (options.pullImage === true) {
307
- shellExec(`docker pull postgres:latest`);
308
- if (!options.kubeadm && !options.k3s)
347
+ if (!options.kubeadm && !options.k3s) {
309
348
  // Only load if not kubeadm/k3s (Kind needs it)
349
+ shellExec(`docker pull postgres:latest`);
310
350
  shellExec(`sudo kind load docker-image postgres:latest`);
311
- else if (options.kubeadm || options.k3s)
351
+ } else if (options.kubeadm || options.k3s)
312
352
  // For kubeadm/k3s, ensure it's available for containerd
313
353
  shellExec(`sudo crictl pull postgres:latest`);
314
354
  }
@@ -319,11 +359,11 @@ class UnderpostCluster {
319
359
  }
320
360
  if (options.mongodb4 === true) {
321
361
  if (options.pullImage === true) {
322
- shellExec(`docker pull mongo:4.4`);
323
- if (!options.kubeadm && !options.k3s)
362
+ if (!options.kubeadm && !options.k3s) {
324
363
  // Only load if not kubeadm/k3s (Kind needs it)
364
+ shellExec(`docker pull mongo:4.4`);
325
365
  shellExec(`sudo kind load docker-image mongo:4.4`);
326
- else if (options.kubeadm || options.k3s)
366
+ } else if (options.kubeadm || options.k3s)
327
367
  // For kubeadm/k3s, ensure it's available for containerd
328
368
  shellExec(`sudo crictl pull mongo:4.4`);
329
369
  }
@@ -334,9 +374,10 @@ class UnderpostCluster {
334
374
  const successInstance = await UnderpostTest.API.statusMonitor(deploymentName);
335
375
 
336
376
  if (successInstance) {
377
+ if (!options.mongoDbHost) options.mongoDbHost = 'mongodb-service';
337
378
  const mongoConfig = {
338
379
  _id: 'rs0',
339
- members: [{ _id: 0, host: 'mongodb-service:27017' }],
380
+ members: [{ _id: 0, host: `${options.mongoDbHost}:27017` }],
340
381
  };
341
382
 
342
383
  const [pod] = UnderpostDeploy.API.get(deploymentName);
@@ -348,11 +389,11 @@ class UnderpostCluster {
348
389
  }
349
390
  } else if (options.full === true || options.mongodb === true) {
350
391
  if (options.pullImage === true) {
351
- shellExec(`docker pull mongo:latest`);
352
- if (!options.kubeadm && !options.k3s)
392
+ if (!options.kubeadm && !options.k3s) {
353
393
  // Only load if not kubeadm/k3s (Kind needs it)
394
+ shellExec(`docker pull mongo:latest`);
354
395
  shellExec(`sudo kind load docker-image mongo:latest`);
355
- else if (options.kubeadm || options.k3s)
396
+ } else if (options.kubeadm || options.k3s)
356
397
  // For kubeadm/k3s, ensure it's available for containerd
357
398
  shellExec(`sudo crictl pull mongo:latest`);
358
399
  }
@@ -363,19 +404,26 @@ class UnderpostCluster {
363
404
  `sudo kubectl create secret generic mongodb-secret --from-file=username=/home/dd/engine/engine-private/mongodb-username --from-file=password=/home/dd/engine/engine-private/mongodb-password --dry-run=client -o yaml | kubectl apply -f -`,
364
405
  );
365
406
  shellExec(`kubectl delete statefulset mongodb --ignore-not-found`);
366
- if (options.kubeadm === true)
367
- // This storage class is specific to kubeadm setup
368
- shellExec(`kubectl apply -f ${underpostRoot}/manifests/mongodb/storage-class.yaml`);
407
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/mongodb/storage-class.yaml`);
369
408
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb`);
370
409
 
371
- const successInstance = await UnderpostTest.API.statusMonitor('mongodb-1');
410
+ const successInstance = await UnderpostTest.API.statusMonitor('mongodb-0', 'Running', 'pods', 1000, 60 * 10);
372
411
 
373
412
  if (successInstance) {
413
+ if (!options.mongoDbHost) options.mongoDbHost = 'mongodb-service';
374
414
  const mongoConfig = {
375
415
  _id: 'rs0',
376
416
  members: [
377
- { _id: 0, host: 'mongodb-0.mongodb-service:27017', priority: 1 },
378
- { _id: 1, host: 'mongodb-1.mongodb-service:27017', priority: 1 },
417
+ {
418
+ _id: 0,
419
+ host: `${options.mongoDbHost === 'mongodb-service' ? 'mongodb-0.' : ''}${options.mongoDbHost}:27017`,
420
+ priority: 1,
421
+ },
422
+ // {
423
+ // _id: 1,
424
+ // host: `${options.mongoDbHost === 'mongodb-service' ? 'mongodb-1.' : ''}${options.mongoDbHost}:27017`,
425
+ // priority: 1,
426
+ // },
379
427
  ],
380
428
  };
381
429
 
@@ -467,6 +515,12 @@ net.bridge.bridge-nf-call-arptables = 1
467
515
  net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
468
516
  { silent: true },
469
517
  );
518
+
519
+ // Increase inotify limits
520
+ shellExec(`sudo sysctl -w fs.inotify.max_user_watches=2099999999`);
521
+ shellExec(`sudo sysctl -w fs.inotify.max_user_instances=2099999999`);
522
+ shellExec(`sudo sysctl -w fs.inotify.max_queued_events=2099999999`);
523
+
470
524
  // shellExec(`sudo sysctl --system`); // Apply sysctl changes immediately
471
525
  // Apply NAT iptables rules.
472
526
  shellExec(`${underpostRoot}/manifests/maas/nat-iptables.sh`, { silent: true });
package/src/cli/cron.js CHANGED
@@ -21,7 +21,6 @@ const logger = loggerFactory(import.meta);
21
21
  * @memberof UnderpostCron
22
22
  */
23
23
  class UnderpostCron {
24
- static NETWORK = [];
25
24
  static JOB = {
26
25
  /**
27
26
  * DNS cli API
@@ -50,10 +49,9 @@ class UnderpostCron {
50
49
  callback: async function (
51
50
  deployList = 'default',
52
51
  jobList = Object.keys(UnderpostCron.JOB),
53
- options = { itc: false, init: false, git: false, dashboardUpdate: false },
52
+ options = { itc: false, init: false, git: false },
54
53
  ) {
55
54
  if (options.init === true) {
56
- UnderpostCron.NETWORK = [];
57
55
  const jobDeployId = fs.readFileSync('./engine-private/deploy/dd.cron', 'utf8').trim();
58
56
  deployList = fs.readFileSync('./engine-private/deploy/dd.router', 'utf8').trim();
59
57
  const confCronConfig = JSON.parse(fs.readFileSync(`./engine-private/conf/${jobDeployId}/conf.cron.json`));
@@ -61,26 +59,11 @@ class UnderpostCron {
61
59
  for (const job of Object.keys(confCronConfig.jobs)) {
62
60
  const name = `${jobDeployId}-${job}`;
63
61
  let deployId;
64
- if (!options.dashboardUpdate) shellExec(Cmd.delete(name));
65
- switch (job) {
66
- case 'dns':
67
- deployId = jobDeployId;
68
- break;
69
-
70
- default:
71
- deployId = deployList;
72
- break;
73
- }
74
- if (!options.dashboardUpdate)
75
- shellExec(Cmd.cron(deployId, job, name, confCronConfig.jobs[job].expression, options));
76
- UnderpostCron.NETWORK.push({
77
- deployId,
78
- jobId: job,
79
- expression: confCronConfig.jobs[job].expression,
80
- });
62
+ shellExec(Cmd.delete(name));
63
+ deployId = UnderpostCron.API.getRelatedDeployId(job);
64
+ shellExec(Cmd.cron(deployId, job, name, confCronConfig.jobs[job].expression, options));
81
65
  }
82
66
  }
83
- if (options.dashboardUpdate === true) await UnderpostCron.API.updateDashboardData();
84
67
  if (fs.existsSync(`./tmp/await-deploy`)) fs.remove(`./tmp/await-deploy`);
85
68
  return;
86
69
  }
@@ -89,30 +72,14 @@ class UnderpostCron {
89
72
  if (UnderpostCron.JOB[jobId]) await UnderpostCron.JOB[jobId].callback(deployList, options);
90
73
  }
91
74
  },
92
- async updateDashboardData() {
93
- try {
94
- const deployId = process.env.DEFAULT_DEPLOY_ID;
95
- const host = process.env.DEFAULT_DEPLOY_HOST;
96
- const path = process.env.DEFAULT_DEPLOY_PATH;
97
- const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
98
- const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
99
- const { db } = confServer[host][path];
100
-
101
- await DataBaseProvider.load({ apis: ['cron'], host, path, db });
102
-
103
- /** @type {import('../api/cron/cron.model.js').CronModel} */
104
- const Cron = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Cron;
105
-
106
- await Cron.deleteMany();
107
-
108
- for (const cronInstance of UnderpostCron.NETWORK) {
109
- logger.info('save', cronInstance);
110
- await new Cron(cronInstance).save();
111
- }
112
-
113
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
114
- } catch (error) {
115
- logger.error(error, error.stack);
75
+ getRelatedDeployId(jobId) {
76
+ switch (jobId) {
77
+ case 'dns':
78
+ return fs.readFileSync('./engine-private/deploy/dd.cron', 'utf8').trim();
79
+ case 'backup':
80
+ return fs.readFileSync('./engine-private/deploy/dd.router', 'utf8').trim();
81
+ default:
82
+ return fs.readFileSync('./engine-private/deploy/dd.cron', 'utf8').trim();
116
83
  }
117
84
  },
118
85
  };
package/src/cli/db.js CHANGED
@@ -3,6 +3,9 @@ import { loggerFactory } from '../server/logger.js';
3
3
  import { shellExec } from '../server/process.js';
4
4
  import fs from 'fs-extra';
5
5
  import UnderpostDeploy from './deploy.js';
6
+ import UnderpostCron from './cron.js';
7
+ import { DataBaseProvider } from '../db/DataBaseProvider.js';
8
+ import { loadReplicas, pathPortAssignmentFactory } from '../server/conf.js';
6
9
 
7
10
  const logger = loggerFactory(import.meta);
8
11
 
@@ -216,6 +219,152 @@ class UnderpostDB {
216
219
  }
217
220
  }
218
221
  },
222
+ async clusterMetadataFactory(
223
+ deployId = process.env.DEFAULT_DEPLOY_ID,
224
+ host = process.env.DEFAULT_DEPLOY_HOST,
225
+ path = process.env.DEFAULT_DEPLOY_PATH,
226
+ ) {
227
+ deployId = deployId ?? process.env.DEFAULT_DEPLOY_ID;
228
+ host = host ?? process.env.DEFAULT_DEPLOY_HOST;
229
+ path = path ?? process.env.DEFAULT_DEPLOY_PATH;
230
+ const env = 'production';
231
+ const deployList = fs.readFileSync('./engine-private/deploy/dd.router', 'utf8').split(',');
232
+
233
+ const { db } = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'))[host][
234
+ path
235
+ ];
236
+ try {
237
+ await DataBaseProvider.load({ apis: ['instance', 'cron'], host, path, db });
238
+
239
+ /** @type {import('../api/instance/instance.model.js').InstanceModel} */
240
+ const Instance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Instance;
241
+
242
+ await Instance.deleteMany();
243
+
244
+ for (const _deployId of deployList) {
245
+ const deployId = _deployId.trim();
246
+ if (!deployId) continue;
247
+ const confServer = loadReplicas(
248
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
249
+ 'proxy',
250
+ );
251
+ const router = await UnderpostDeploy.API.routerFactory(deployId, env);
252
+ const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
253
+
254
+ for (const host of Object.keys(confServer)) {
255
+ for (const { path, port } of pathPortAssignmentData[host]) {
256
+ if (!confServer[host][path]) continue;
257
+
258
+ const { client, runtime, apis, peer } = confServer[host][path];
259
+ {
260
+ const body = {
261
+ deployId,
262
+ host,
263
+ path,
264
+ port,
265
+ client,
266
+ runtime,
267
+ apis,
268
+ };
269
+
270
+ logger.info('Instance save', body);
271
+ await new Instance(body).save();
272
+ }
273
+
274
+ if (peer) {
275
+ const body = {
276
+ deployId,
277
+ host,
278
+ path: path === '/' ? '/peer' : `${path}/peer`,
279
+ port: port + 1,
280
+ runtime: 'nodejs',
281
+ };
282
+
283
+ logger.info('Instance save', body);
284
+ await new Instance(body).save();
285
+ }
286
+ }
287
+ }
288
+ }
289
+ } catch (error) {
290
+ logger.error(error, error.stack);
291
+ }
292
+
293
+ try {
294
+ const cronDeployId = fs.readFileSync('./engine-private/deploy/dd.cron', 'utf8').trim();
295
+ const confCronPath = `./engine-private/conf/${cronDeployId}/conf.cron.json`;
296
+ const confCron = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
297
+
298
+ await DataBaseProvider.load({ apis: ['cron'], host, path, db });
299
+
300
+ /** @type {import('../api/cron/cron.model.js').CronModel} */
301
+ const Cron = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Cron;
302
+
303
+ await Cron.deleteMany();
304
+
305
+ for (const jobId of Object.keys(confCron.jobs)) {
306
+ const body = {
307
+ jobId,
308
+ deployId: UnderpostCron.API.getRelatedDeployId(jobId),
309
+ expression: confCron.jobs[jobId].expression,
310
+ enabled: confCron.jobs[jobId].enabled,
311
+ };
312
+ logger.info('Cron save', body);
313
+ await new Cron(body).save();
314
+ }
315
+ } catch (error) {
316
+ logger.error(error, error.stack);
317
+ }
318
+ await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
319
+ },
320
+ clusterMetadataBackupCallback(
321
+ deployId = process.env.DEFAULT_DEPLOY_ID,
322
+ host = process.env.DEFAULT_DEPLOY_HOST,
323
+ path = process.env.DEFAULT_DEPLOY_PATH,
324
+ options = {
325
+ generate: false,
326
+ itc: false,
327
+ import: false,
328
+ export: false,
329
+ instances: false,
330
+ crons: false,
331
+ },
332
+ ) {
333
+ deployId = deployId ?? process.env.DEFAULT_DEPLOY_ID;
334
+ host = host ?? process.env.DEFAULT_DEPLOY_HOST;
335
+ path = path ?? process.env.DEFAULT_DEPLOY_PATH;
336
+
337
+ if (options.generate === true) {
338
+ UnderpostDB.API.clusterMetadataFactory(deployId, host, path);
339
+ }
340
+
341
+ if (options.instances === true) {
342
+ const outputPath = './engine-private/instances';
343
+ if (fs.existsSync(outputPath)) fs.mkdirSync(outputPath, { recursive: true });
344
+ const collection = 'instances';
345
+ if (options.export === true)
346
+ shellExec(
347
+ `node bin db --export --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
348
+ );
349
+ if (options.import === true)
350
+ shellExec(
351
+ `node bin db --import --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
352
+ );
353
+ }
354
+ if (options.crons === true) {
355
+ const outputPath = './engine-private/crons';
356
+ if (fs.existsSync(outputPath)) fs.mkdirSync(outputPath, { recursive: true });
357
+ const collection = 'crons';
358
+ if (options.export === true)
359
+ shellExec(
360
+ `node bin db --export --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
361
+ );
362
+ if (options.import === true)
363
+ shellExec(
364
+ `node bin db --import --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
365
+ );
366
+ }
367
+ },
219
368
  };
220
369
  }
221
370