underpost 2.8.85 → 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 (52) hide show
  1. package/.env.development +1 -1
  2. package/.env.production +1 -1
  3. package/.env.test +1 -1
  4. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
  5. package/.github/workflows/release.cd.yml +37 -0
  6. package/README.md +7 -44
  7. package/bin/cyberia0.js +78 -0
  8. package/bin/db.js +1 -3
  9. package/bin/deploy.js +13 -350
  10. package/bin/file.js +11 -1
  11. package/cli.md +39 -19
  12. package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
  13. package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
  14. package/manifests/grafana/deployment.yaml +57 -0
  15. package/manifests/grafana/kustomization.yaml +7 -0
  16. package/manifests/grafana/pvc.yaml +12 -0
  17. package/manifests/grafana/service.yaml +14 -0
  18. package/manifests/maas/ssh-cluster-info.sh +14 -0
  19. package/manifests/prometheus/deployment.yaml +82 -0
  20. package/package.json +1 -2
  21. package/src/api/user/user.service.js +8 -34
  22. package/src/cli/cluster.js +41 -2
  23. package/src/cli/cron.js +12 -45
  24. package/src/cli/db.js +149 -0
  25. package/src/cli/deploy.js +20 -81
  26. package/src/cli/index.js +20 -6
  27. package/src/cli/monitor.js +1 -4
  28. package/src/cli/repository.js +12 -5
  29. package/src/cli/run.js +77 -14
  30. package/src/client/Default.index.js +0 -2
  31. package/src/client/components/core/Account.js +6 -2
  32. package/src/client/components/core/Content.js +11 -7
  33. package/src/client/components/core/Css.js +5 -1
  34. package/src/client/components/core/Input.js +6 -1
  35. package/src/client/components/core/LogIn.js +3 -0
  36. package/src/client/components/core/LogOut.js +1 -1
  37. package/src/client/components/core/Modal.js +7 -4
  38. package/src/client/components/core/Recover.js +5 -2
  39. package/src/client/components/core/Scroll.js +65 -120
  40. package/src/client/components/core/SignUp.js +1 -0
  41. package/src/client/components/core/VanillaJs.js +48 -2
  42. package/src/client/components/default/MenuDefault.js +2 -2
  43. package/src/client/components/default/RoutesDefault.js +3 -3
  44. package/src/index.js +1 -1
  45. package/src/mailer/MailerProvider.js +37 -0
  46. package/src/server/client-build-live.js +1 -1
  47. package/src/server/client-dev-server.js +1 -1
  48. package/src/server/conf.js +2 -272
  49. package/src/server/proxy.js +1 -2
  50. package/src/server/start.js +3 -3
  51. package/docker-compose.yml +0 -67
  52. package/prometheus.yml +0 -36
@@ -16,7 +16,7 @@ spec:
16
16
  - prefix: /
17
17
  enableWebsockets: true
18
18
  services:
19
- - name: dd-template-development-blue-service
19
+ - name: dd-default-development-blue-service
20
20
  port: 4001
21
21
  weight: 100
22
22
 
@@ -24,7 +24,7 @@ spec:
24
24
  - prefix: /peer
25
25
  enableWebsockets: true
26
26
  services:
27
- - name: dd-template-development-blue-service
27
+ - name: dd-default-development-blue-service
28
28
  port: 4002
29
29
  weight: 100
30
30
 
@@ -41,6 +41,6 @@ spec:
41
41
  - prefix: /
42
42
  enableWebsockets: true
43
43
  services:
44
- - name: dd-template-development-blue-service
44
+ - name: dd-default-development-blue-service
45
45
  port: 4003
46
46
  weight: 100
@@ -0,0 +1,57 @@
1
+ ---
2
+ apiVersion: apps/v1
3
+ kind: Deployment
4
+ metadata:
5
+ labels:
6
+ app: grafana
7
+ name: grafana
8
+ spec:
9
+ selector:
10
+ matchLabels:
11
+ app: grafana
12
+ template:
13
+ metadata:
14
+ labels:
15
+ app: grafana
16
+ spec:
17
+ securityContext:
18
+ fsGroup: 472
19
+ supplementalGroups:
20
+ - 0
21
+ containers:
22
+ - name: grafana
23
+ image: grafana/grafana:latest
24
+ imagePullPolicy: IfNotPresent
25
+ ports:
26
+ - containerPort: 3000
27
+ name: http-grafana
28
+ protocol: TCP
29
+ readinessProbe:
30
+ failureThreshold: 3
31
+ httpGet:
32
+ path: /robots.txt
33
+ port: 3000
34
+ scheme: HTTP
35
+ initialDelaySeconds: 10
36
+ periodSeconds: 30
37
+ successThreshold: 1
38
+ timeoutSeconds: 2
39
+ livenessProbe:
40
+ failureThreshold: 3
41
+ initialDelaySeconds: 30
42
+ periodSeconds: 10
43
+ successThreshold: 1
44
+ tcpSocket:
45
+ port: 3000
46
+ timeoutSeconds: 1
47
+ resources:
48
+ requests:
49
+ cpu: 250m
50
+ memory: 750Mi
51
+ volumeMounts:
52
+ - mountPath: /var/lib/grafana
53
+ name: grafana-pv
54
+ volumes:
55
+ - name: grafana-pv
56
+ persistentVolumeClaim:
57
+ claimName: grafana-pvc
@@ -0,0 +1,7 @@
1
+ ---
2
+ apiVersion: kustomize.config.k8s.io/v1beta1
3
+ kind: Kustomization
4
+ resources:
5
+ - pvc.yaml
6
+ - deployment.yaml
7
+ - service.yaml
@@ -0,0 +1,12 @@
1
+ ---
2
+ apiVersion: v1
3
+ kind: PersistentVolumeClaim
4
+ metadata:
5
+ name: grafana-pvc
6
+ spec:
7
+ accessModes:
8
+ - ReadWriteOnce
9
+ storageClassName: local-path
10
+ resources:
11
+ requests:
12
+ storage: 5Gi
@@ -0,0 +1,14 @@
1
+ ---
2
+ apiVersion: v1
3
+ kind: Service
4
+ metadata:
5
+ name: grafana
6
+ spec:
7
+ ports:
8
+ - port: 3000
9
+ protocol: TCP
10
+ targetPort: http-grafana
11
+ selector:
12
+ app: grafana
13
+ sessionAffinity: None
14
+ type: LoadBalancer
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ REMOTE_USER=$(node bin config get --plain DEFAULT_SSH_USER)
5
+ REMOTE_HOST=$(node bin config get --plain DEFAULT_SSH_HOST)
6
+ SSH_KEY=$(node bin config get --plain DEFAULT_SSH_KEY_PATH)
7
+
8
+ chmod 600 "$SSH_KEY"
9
+
10
+ ssh -i "$SSH_KEY" -o BatchMode=yes "${REMOTE_USER}@${REMOTE_HOST}" sh <<EOF
11
+ cd /home/dd/engine
12
+ node bin deploy dd production --info-traffic
13
+ kubectl get pods -A
14
+ EOF
@@ -0,0 +1,82 @@
1
+ ---
2
+ apiVersion: v1
3
+ kind: ConfigMap
4
+ metadata:
5
+ name: prometheus-config
6
+ labels:
7
+ app: prometheus
8
+ data:
9
+ prometheus.yml: |
10
+ global:
11
+ scrape_interval: 30s
12
+ evaluation_interval: 30s
13
+
14
+ scrape_configs:
15
+ - job_name: 'scraper'
16
+ metrics_path: /metrics
17
+ scheme: https
18
+ static_configs:
19
+ - targets: []
20
+ # tls_config:
21
+ # insecure_skip_verify: true
22
+
23
+ ---
24
+ apiVersion: apps/v1
25
+ kind: Deployment
26
+ metadata:
27
+ name: prometheus
28
+ labels:
29
+ app: prometheus
30
+ spec:
31
+ replicas: 1
32
+ selector:
33
+ matchLabels:
34
+ app: prometheus
35
+ template:
36
+ metadata:
37
+ labels:
38
+ app: prometheus
39
+ spec:
40
+ containers:
41
+ - name: prometheus
42
+ image: prom/prometheus:latest
43
+ args:
44
+ - '--config.file=/etc/prometheus/prometheus.yml'
45
+ - '--storage.tsdb.path=/prometheus'
46
+ - '--web.console.libraries=/usr/share/prometheus/console_libraries'
47
+ - '--web.console.templates=/usr/share/prometheus/consoles'
48
+ ports:
49
+ - containerPort: 9090
50
+ name: web
51
+ volumeMounts:
52
+ - name: prometheus-config
53
+ mountPath: /etc/prometheus/
54
+ - name: prometheus-data
55
+ mountPath: /prometheus
56
+ resources:
57
+ requests:
58
+ memory: '200Mi'
59
+ cpu: '100m'
60
+ volumes:
61
+ - name: prometheus-config
62
+ configMap:
63
+ name: prometheus-config
64
+ - name: prometheus-data
65
+ emptyDir: {}
66
+
67
+ ---
68
+ apiVersion: v1
69
+ kind: Service
70
+ metadata:
71
+ name: prometheus
72
+ labels:
73
+ app: prometheus
74
+ spec:
75
+ ports:
76
+ - port: 9090
77
+ targetPort: 9090
78
+ protocol: TCP
79
+ name: web
80
+ selector:
81
+ app: prometheus
82
+ type: ClusterIP
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "2.8.85",
5
+ "version": "2.8.86",
6
6
  "description": "pwa api rest template",
7
7
  "scripts": {
8
8
  "start": "env-cmd -f .env.production node --max-old-space-size=8192 src/server",
@@ -21,7 +21,6 @@
21
21
  "install-test": "npm install -g mocha && npm install -g c8 && npm install -g nyc && npm install -g coveralls",
22
22
  "install-underpost": "cp -a $(npm root -g)/underpost/node_modules ./node_modules && npm install --only=dev --ignore-scripts",
23
23
  "install": "npm run install-global && npm run install-test",
24
- "docker:start": "docker-compose up",
25
24
  "prettier": "prettier --write .",
26
25
  "fix": "npm audit fix --force && npm audit",
27
26
  "changelog": "auto-changelog"
@@ -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',
@@ -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
 
@@ -33,12 +34,13 @@ class UnderpostCluster {
33
34
  * @param {string} [options.nsUse=''] - Set the current kubectl namespace.
34
35
  * @param {boolean} [options.infoCapacity=false] - Display resource capacity information for the cluster.
35
36
  * @param {boolean} [options.infoCapacityPod=false] - Display resource capacity information for pods.
36
- * @param {boolean} [options.istio=false] - Deploy Istio service mesh.
37
37
  * @param {boolean} [options.pullImage=false] - Pull necessary Docker images before deployment.
38
38
  * @param {boolean} [options.dedicatedGpu=false] - Configure for dedicated GPU usage (e.g., NVIDIA GPU Operator).
39
39
  * @param {boolean} [options.kubeadm=false] - Initialize the cluster using Kubeadm.
40
40
  * @param {boolean} [options.k3s=false] - Initialize the cluster using K3s.
41
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.
42
44
  * @param {boolean} [options.uninstallHost=false] - Uninstall all host components.
43
45
  * @param {boolean} [options.config=false] - Apply general host configuration (SELinux, containerd, sysctl, firewalld).
44
46
  * @param {boolean} [options.worker=false] - Configure as a worker node (for Kubeadm or K3s join).
@@ -63,12 +65,13 @@ class UnderpostCluster {
63
65
  nsUse: '',
64
66
  infoCapacity: false,
65
67
  infoCapacityPod: false,
66
- istio: false,
67
68
  pullImage: false,
68
69
  dedicatedGpu: false,
69
70
  kubeadm: false,
70
71
  k3s: false,
71
72
  initHost: false,
73
+ grafana: false,
74
+ prom: '',
72
75
  uninstallHost: false,
73
76
  config: false,
74
77
  worker: false,
@@ -260,6 +263,42 @@ class UnderpostCluster {
260
263
  );
261
264
  }
262
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
+
263
302
  if (options.full === true || options.valkey === true) {
264
303
  if (options.pullImage === true) {
265
304
  // shellExec(`sudo podman pull valkey/valkey:latest`);
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