underpost 2.8.6 → 2.8.7

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/.vscode/extensions.json +3 -2
  2. package/.vscode/settings.json +2 -0
  3. package/CHANGELOG.md +24 -4
  4. package/Dockerfile +9 -10
  5. package/README.md +39 -2
  6. package/bin/build.js +2 -2
  7. package/bin/deploy.js +1337 -131
  8. package/bin/file.js +8 -0
  9. package/bin/index.js +1 -218
  10. package/cli.md +451 -0
  11. package/docker-compose.yml +1 -1
  12. package/jsdoc.json +1 -1
  13. package/manifests/calico-custom-resources.yaml +25 -0
  14. package/manifests/deployment/adminer/deployment.yaml +32 -0
  15. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  16. package/manifests/deployment/adminer/service.yaml +13 -0
  17. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  18. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  19. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  20. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  21. package/manifests/deployment/kafka/deployment.yaml +69 -0
  22. package/manifests/kubeadm-calico-config.yaml +119 -0
  23. package/manifests/mongodb-4.4/service-deployment.yaml +1 -1
  24. package/manifests/postgresql/configmap.yaml +9 -0
  25. package/manifests/postgresql/kustomization.yaml +10 -0
  26. package/manifests/postgresql/pv.yaml +15 -0
  27. package/manifests/postgresql/pvc.yaml +13 -0
  28. package/manifests/postgresql/service.yaml +10 -0
  29. package/manifests/postgresql/statefulset.yaml +37 -0
  30. package/manifests/valkey/statefulset.yaml +6 -4
  31. package/package.json +3 -9
  32. package/src/api/default/default.service.js +1 -1
  33. package/src/api/user/user.service.js +14 -11
  34. package/src/cli/cluster.js +207 -20
  35. package/src/cli/cron.js +39 -8
  36. package/src/cli/db.js +20 -10
  37. package/src/cli/deploy.js +254 -85
  38. package/src/cli/env.js +9 -3
  39. package/src/cli/fs.js +21 -9
  40. package/src/cli/image.js +42 -124
  41. package/src/cli/index.js +312 -0
  42. package/src/cli/monitor.js +236 -0
  43. package/src/cli/repository.js +5 -2
  44. package/src/client/components/core/Account.js +28 -24
  45. package/src/client/components/core/Blockchain.js +1 -1
  46. package/src/client/components/core/CalendarCore.js +14 -84
  47. package/src/client/components/core/CommonJs.js +2 -1
  48. package/src/client/components/core/Css.js +0 -1
  49. package/src/client/components/core/CssCore.js +10 -2
  50. package/src/client/components/core/Docs.js +1 -1
  51. package/src/client/components/core/EventsUI.js +3 -3
  52. package/src/client/components/core/FileExplorer.js +86 -78
  53. package/src/client/components/core/JoyStick.js +2 -2
  54. package/src/client/components/core/LoadingAnimation.js +1 -17
  55. package/src/client/components/core/LogIn.js +3 -3
  56. package/src/client/components/core/LogOut.js +1 -1
  57. package/src/client/components/core/Modal.js +14 -8
  58. package/src/client/components/core/Panel.js +19 -61
  59. package/src/client/components/core/PanelForm.js +13 -22
  60. package/src/client/components/core/Recover.js +3 -3
  61. package/src/client/components/core/RichText.js +1 -11
  62. package/src/client/components/core/Router.js +3 -1
  63. package/src/client/components/core/SignUp.js +2 -2
  64. package/src/client/components/default/RoutesDefault.js +3 -2
  65. package/src/client/services/default/default.management.js +45 -38
  66. package/src/client/ssr/Render.js +2 -0
  67. package/src/index.js +18 -2
  68. package/src/mailer/MailerProvider.js +3 -0
  69. package/src/runtime/lampp/Dockerfile +65 -0
  70. package/src/server/client-build.js +13 -0
  71. package/src/server/conf.js +93 -1
  72. package/src/server/dns.js +56 -18
  73. package/src/server/json-schema.js +77 -0
  74. package/src/server/network.js +7 -122
  75. package/src/server/peer.js +2 -2
  76. package/src/server/proxy.js +4 -4
  77. package/src/server/runtime.js +24 -11
  78. package/src/server/start.js +122 -0
  79. package/src/server/valkey.js +25 -11
package/src/cli/deploy.js CHANGED
@@ -3,22 +3,41 @@ import {
3
3
  buildPortProxyRouter,
4
4
  buildProxyRouter,
5
5
  Config,
6
+ deployRangePortFactory,
6
7
  getDataDeploy,
7
8
  loadReplicas,
9
+ pathPortAssignmentFactory,
8
10
  } from '../server/conf.js';
9
11
  import { loggerFactory } from '../server/logger.js';
10
12
  import { shellExec } from '../server/process.js';
11
13
  import fs from 'fs-extra';
12
14
  import dotenv from 'dotenv';
13
- import Underpost from '../index.js';
15
+ import { DataBaseProvider } from '../db/DataBaseProvider.js';
16
+ import UnderpostRootEnv from './env.js';
17
+ import UnderpostCluster from './cluster.js';
14
18
 
15
19
  const logger = loggerFactory(import.meta);
16
20
 
17
21
  class UnderpostDeploy {
22
+ static NETWORK = {};
18
23
  static API = {
19
- sync(deployList) {
20
- const deployGroupId = 'dd.tmp';
24
+ sync(deployList, { versions, replicas }) {
25
+ const deployGroupId = 'dd.router';
21
26
  fs.writeFileSync(`./engine-private/deploy/${deployGroupId}`, deployList, 'utf8');
27
+ const totalPods = deployList.split(',').length * versions.split(',').length * parseInt(replicas);
28
+ const limitFactor = 0.8;
29
+ const reserveFactor = 0.05;
30
+ const resources = UnderpostCluster.API.getResourcesCapacity();
31
+ const memory = parseInt(resources.memory.value / totalPods);
32
+ const cpu = parseInt(resources.cpu.value / totalPods);
33
+ UnderpostRootEnv.API.set(
34
+ 'resources.requests.memory',
35
+ `${parseInt(memory * reserveFactor)}${resources.memory.unit}`,
36
+ );
37
+ UnderpostRootEnv.API.set('resources.requests.cpu', `${parseInt(cpu * reserveFactor)}${resources.cpu.unit}`);
38
+ UnderpostRootEnv.API.set('resources.limits.memory', `${parseInt(memory * limitFactor)}${resources.memory.unit}`);
39
+ UnderpostRootEnv.API.set('resources.limits.cpu', `${parseInt(cpu * limitFactor)}${resources.cpu.unit}`);
40
+ UnderpostRootEnv.API.set('total-pods', totalPods);
22
41
  return getDataDeploy({
23
42
  buildSingleReplica: true,
24
43
  deployGroupId,
@@ -32,75 +51,102 @@ class UnderpostDeploy {
32
51
  await Config.build(undefined, 'proxy', deployList);
33
52
  return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
34
53
  },
35
- async buildManifest(deployList, env, version) {
36
- for (const _deployId of deployList.split(',')) {
37
- const deployId = _deployId.trim();
38
- if (!deployId) continue;
39
-
40
- const router = await UnderpostDeploy.API.routerFactory(deployId, env);
41
- const ports = Object.values(router).map((p) => parseInt(p.split(':')[2]));
42
- const fromPort = Math.min(...ports);
43
- const toPort = Math.max(...ports);
44
- const confServer = loadReplicas(
45
- JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
46
- 'proxy',
47
- );
48
-
49
- fs.mkdirSync(`./engine-private/conf/${deployId}/build/${env}`, { recursive: true });
50
- if (env === 'development') fs.mkdirSync(`./manifests/deployment/${deployId}-${env}`, { recursive: true });
51
-
52
- logger.info('port range', { deployId, fromPort, toPort });
53
-
54
- const deploymentYamlParts = `apiVersion: apps/v1
54
+ deploymentYamlServiceFactory({ deployId, env, port, deploymentVersions }) {
55
+ return deploymentVersions
56
+ .map(
57
+ (version, i) => ` - name: ${deployId}-${env}-${version}-service
58
+ port: ${port}
59
+ weight: ${i === 0 ? 100 : 0}
60
+ `,
61
+ )
62
+ .join('');
63
+ },
64
+ deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas }) {
65
+ return `apiVersion: apps/v1
55
66
  kind: Deployment
56
67
  metadata:
57
- name: ${deployId}-${env}
68
+ name: ${deployId}-${env}-${suffix}
58
69
  labels:
59
- app: ${deployId}-${env}
70
+ app: ${deployId}-${env}-${suffix}
60
71
  spec:
61
- replicas: 2
72
+ replicas: ${replicas}
62
73
  selector:
63
74
  matchLabels:
64
- app: ${deployId}-${env}
75
+ app: ${deployId}-${env}-${suffix}
65
76
  template:
66
77
  metadata:
67
78
  labels:
68
- app: ${deployId}-${env}
79
+ app: ${deployId}-${env}-${suffix}
69
80
  spec:
70
81
  containers:
71
- - name: ${deployId}-${env}
72
- image: localhost/underpost-engine:${version && typeof version === 'string' ? version : Underpost.version}
73
- lifecycle:
74
- postStart:
75
- exec:
76
- command:
77
- - /bin/sh
78
- - -c
79
- - >
80
- sleep 60 &&
81
- underpost config set deploy-id ${deployId} &&
82
- underpost config set deploy-env ${env}
83
- # image: localhost/${deployId}-${env}:${version && typeof version === 'string' ? version : Underpost.version}
82
+ - name: ${deployId}-${env}-${suffix}
83
+ image: localhost/debian:underpost
84
+ resources:
85
+ requests:
86
+ memory: "${resources.requests.memory}"
87
+ cpu: "${resources.requests.cpu}"
88
+ limits:
89
+ memory: "${resources.limits.memory}"
90
+ cpu: "${resources.limits.cpu}"
91
+ command:
92
+ - /bin/sh
93
+ - -c
94
+ - >
95
+ npm install -g npm@11.2.0 &&
96
+ npm install -g underpost &&
97
+ underpost secret underpost --create-from-file /etc/config/.env.${env} &&
98
+ underpost start --build --run ${deployId} ${env}
99
+ volumeMounts:
100
+ - name: config-volume
101
+ mountPath: /etc/config
102
+ volumes:
103
+ - name: config-volume
104
+ configMap:
105
+ name: underpost-config
84
106
  ---
85
107
  apiVersion: v1
86
108
  kind: Service
87
109
  metadata:
88
- name: ${deployId}-${env}-service
110
+ name: ${deployId}-${env}-${suffix}-service
89
111
  spec:
90
112
  selector:
91
- app: ${deployId}-${env}
113
+ app: ${deployId}-${env}-${suffix}
92
114
  ports:
93
- type: LoadBalancer`.split('ports:');
94
- deploymentYamlParts[1] =
95
- buildKindPorts(fromPort, toPort) +
96
- ` type: LoadBalancer
97
- `;
115
+ {{ports}} type: LoadBalancer`;
116
+ },
117
+ async buildManifest(deployList, env, options) {
118
+ const resources = UnderpostDeploy.API.resourcesFactory();
119
+ const replicas = options.replicas;
98
120
 
99
- fs.writeFileSync(
100
- `./engine-private/conf/${deployId}/build/${env}/deployment.yaml`,
101
- deploymentYamlParts.join(`ports:
102
- `),
121
+ for (const _deployId of deployList.split(',')) {
122
+ const deployId = _deployId.trim();
123
+ if (!deployId) continue;
124
+ const confServer = loadReplicas(
125
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
126
+ 'proxy',
103
127
  );
128
+ const router = await UnderpostDeploy.API.routerFactory(deployId, env);
129
+ const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
130
+ const { fromPort, toPort } = deployRangePortFactory(router);
131
+ const deploymentVersions = options.versions.split(',');
132
+ fs.mkdirSync(`./engine-private/conf/${deployId}/build/${env}`, { recursive: true });
133
+ if (env === 'development') fs.mkdirSync(`./manifests/deployment/${deployId}-${env}`, { recursive: true });
134
+
135
+ logger.info('port range', { deployId, fromPort, toPort });
136
+
137
+ let deploymentYamlParts = '';
138
+ for (const deploymentVersion of deploymentVersions) {
139
+ deploymentYamlParts += `---
140
+ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
141
+ deployId,
142
+ env,
143
+ suffix: deploymentVersion,
144
+ resources,
145
+ replicas,
146
+ }).replace('{{ports}}', buildKindPorts(fromPort, toPort))}
147
+ `;
148
+ }
149
+ fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
104
150
 
105
151
  let proxyYaml = '';
106
152
  let secretYaml = '';
@@ -122,27 +168,8 @@ spec:
122
168
  kind: ClusterIssuer
123
169
  secretName: ${host}`;
124
170
 
125
- const pathPortConditions = [];
126
- for (const path of Object.keys(confServer[host])) {
127
- const { peer } = confServer[host][path];
128
- if (!router[`${host}${path === '/' ? '' : path}`]) continue;
129
- const port = parseInt(router[`${host}${path === '/' ? '' : path}`].split(':')[2]);
130
- // logger.info('', { host, port, path });
131
- pathPortConditions.push({
132
- port,
133
- path,
134
- });
135
-
136
- if (peer) {
137
- // logger.info('', { host, port: port + 1, path: '/peer' });
138
- pathPortConditions.push({
139
- port: port + 1,
140
- path: '/peer',
141
- });
142
- }
143
- }
144
-
145
- // logger.info('', { host, pathPortConditions });
171
+ const pathPortAssignment = pathPortAssignmentData[host];
172
+ // logger.info('', { host, pathPortAssignment });
146
173
  proxyYaml += `
147
174
  ---
148
175
  apiVersion: projectcontour.io/v1
@@ -159,15 +186,20 @@ spec:
159
186
  secretName: ${host}`
160
187
  }
161
188
  routes:`;
162
- for (const conditionObj of pathPortConditions) {
189
+ for (const conditionObj of pathPortAssignment) {
163
190
  const { path, port } = conditionObj;
164
191
  proxyYaml += `
165
192
  - conditions:
166
193
  - prefix: ${path}
167
194
  enableWebsockets: true
168
195
  services:
169
- - name: ${deployId}-${env}-service
170
- port: ${port}`;
196
+ ${UnderpostDeploy.API.deploymentYamlServiceFactory({
197
+ deployId,
198
+ env,
199
+ port,
200
+ deploymentVersions:
201
+ options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'],
202
+ })}`;
171
203
  }
172
204
  }
173
205
  const yamlPath = `./engine-private/conf/${deployId}/build/${env}/proxy.yaml`;
@@ -188,6 +220,14 @@ spec:
188
220
  }
189
221
  }
190
222
  },
223
+ getCurrentTraffic(deployId) {
224
+ // kubectl get deploy,sts,svc,configmap,secret -n default -o yaml --export > default.yaml
225
+ const hostTest = Object.keys(
226
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
227
+ )[0];
228
+ const info = shellExec(`sudo kubectl get HTTPProxy/${hostTest} -o yaml`, { silent: true, stdout: true });
229
+ return info.match('blue') ? 'blue' : info.match('green') ? 'green' : null;
230
+ },
191
231
  async callback(
192
232
  deployList = 'default',
193
233
  env = 'development',
@@ -199,7 +239,13 @@ spec:
199
239
  infoUtil: false,
200
240
  expose: false,
201
241
  cert: false,
202
- version: '',
242
+ versions: '',
243
+ traffic: '',
244
+ dashboardUpdate: false,
245
+ replicas: '',
246
+ disableUpdateDeployment: false,
247
+ infoTraffic: false,
248
+ rebuildClientsBundle: false,
203
249
  },
204
250
  ) {
205
251
  if (options.infoUtil === true)
@@ -207,13 +253,48 @@ spec:
207
253
  kubectl rollout restart deployment/deployment-name
208
254
  kubectl rollout undo deployment/deployment-name
209
255
  kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
210
- `);
256
+ kubectl get pods -w
257
+ kubectl patch statefulset service-valkey --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"valkey/valkey:latest"}]'
258
+ kubectl patch statefulset service-valkey -p '{"spec":{"template":{"spec":{"containers":[{"name":"service-valkey","imagePullPolicy":"Never"}]}}}}'
259
+ kubectl logs -f <pod-name>
260
+ kubectl describe pod <pod-name>
261
+ kubectl exec -it <pod-name> -- bash
262
+ kubectl exec -it <pod-name> -- sh
263
+ docker exec -it kind-control-plane bash
264
+ curl -4 -v google.com
265
+ kubectl taint nodes <node-name> node-role.kubernetes.io/control-plane:NoSchedule-
266
+ kubectl run test-pod --image=busybox:latest --restart=Never -- /bin/sh -c "while true; do sleep 30; done;"
267
+ kubectl run test-pod --image=alpine/curl:latest --restart=Never -- sh -c "sleep infinity"
268
+ kubectl get ippools -o yaml
269
+ kubectl get node <node-name> -o jsonpath='{.spec.podCIDR}'
270
+ kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "10.244.0.0/16"}]'
271
+ kubectl patch ippool default-ipv4-ippool --type='json' -p='[{"op": "replace", "path": "/spec/cidr", "value": "192.168.0.0/24"}]'
272
+ `);
211
273
  if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
212
274
  deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
213
- if (options.sync) UnderpostDeploy.API.sync(deployList);
214
- if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options.version);
215
- if (options.infoRouter === true)
216
- return logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
275
+ if (options.infoTraffic === true) {
276
+ for (const _deployId of deployList.split(',')) {
277
+ const deployId = _deployId.trim();
278
+ logger.info('', {
279
+ deployId,
280
+ env,
281
+ traffic: UnderpostDeploy.API.getCurrentTraffic(deployId),
282
+ });
283
+ }
284
+ return;
285
+ }
286
+ if (options.rebuildClientsBundle === true) await UnderpostDeploy.API.rebuildClientsBundle(deployList);
287
+ if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
288
+ if (!options.replicas) options.replicas = 1;
289
+ if (options.sync) UnderpostDeploy.API.sync(deployList, options);
290
+ if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options);
291
+ if (options.infoRouter === true) logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
292
+ if (options.dashboardUpdate === true) await UnderpostDeploy.API.updateDashboardData(deployList, env, options);
293
+ if (options.infoRouter === true) return;
294
+ shellExec(`kubectl delete configmap underpost-config`);
295
+ shellExec(
296
+ `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
297
+ );
217
298
  const etcHost = (
218
299
  concat,
219
300
  ) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
@@ -233,8 +314,12 @@ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
233
314
  shellExec(`sudo kubectl port-forward -n default svc/${svc.NAME} ${port}:${port}`, { async: true });
234
315
  continue;
235
316
  }
236
- shellExec(`sudo kubectl delete svc ${deployId}-${env}-service`);
237
- shellExec(`sudo kubectl delete deployment ${deployId}-${env}`);
317
+
318
+ if (!options.disableUpdateDeployment)
319
+ for (const version of options.versions.split(',')) {
320
+ shellExec(`sudo kubectl delete svc ${deployId}-${env}-${version}-service`);
321
+ shellExec(`sudo kubectl delete deployment ${deployId}-${env}-${version}`);
322
+ }
238
323
 
239
324
  const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
240
325
  for (const host of Object.keys(confServer)) {
@@ -249,7 +334,7 @@ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
249
334
  : `manifests/deployment/${deployId}-${env}`;
250
335
 
251
336
  if (!options.remove === true) {
252
- shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
337
+ if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
253
338
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
254
339
  if (env === 'production' && options.cert === true)
255
340
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
@@ -312,6 +397,90 @@ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
312
397
 
313
398
  return result;
314
399
  },
400
+ rebuildClientsBundle(deployList) {
401
+ for (const _deployId of deployList.split(',')) {
402
+ const deployId = _deployId.trim();
403
+ const repoName = `engine-${deployId.split('-')[1]}`;
404
+
405
+ shellExec(`underpost script set ${deployId}-client-build '
406
+ cd /home/dd/engine &&
407
+ git checkout . &&
408
+ underpost pull . underpostnet/${repoName} &&
409
+ underpost pull ./engine-private underpostnet/${repoName}-private &&
410
+ underpost env ${deployId} production &&
411
+ node bin/deploy build-full-client ${deployId}
412
+ '`);
413
+
414
+ shellExec(`node bin script run ${deployId}-client-build --itc --pod-name ${deployId}`);
415
+ }
416
+ },
417
+ resourcesFactory() {
418
+ return {
419
+ requests: {
420
+ memory: UnderpostRootEnv.API.get('resources.requests.memory'),
421
+ cpu: UnderpostRootEnv.API.get('resources.requests.cpu'),
422
+ },
423
+ limits: {
424
+ memory: UnderpostRootEnv.API.get('resources.limits.memory'),
425
+ cpu: UnderpostRootEnv.API.get('resources.limits.cpu'),
426
+ },
427
+ totalPods: UnderpostRootEnv.API.get('total-pods'),
428
+ };
429
+ },
430
+ async updateDashboardData(deployList, env, options) {
431
+ try {
432
+ const deployId = process.env.DEFAULT_DEPLOY_ID;
433
+ const host = process.env.DEFAULT_DEPLOY_HOST;
434
+ const path = process.env.DEFAULT_DEPLOY_PATH;
435
+ const { db } = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'))[host][
436
+ path
437
+ ];
438
+
439
+ await DataBaseProvider.load({ apis: ['instance'], host, path, db });
440
+
441
+ /** @type {import('../api/instance/instance.model.js').InstanceModel} */
442
+ const Instance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Instance;
443
+
444
+ await Instance.deleteMany();
445
+
446
+ for (const _deployId of deployList.split(',')) {
447
+ const deployId = _deployId.trim();
448
+ if (!deployId) continue;
449
+ const confServer = loadReplicas(
450
+ JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
451
+ 'proxy',
452
+ );
453
+ const router = await UnderpostDeploy.API.routerFactory(deployId, env);
454
+ const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
455
+
456
+ for (const host of Object.keys(confServer)) {
457
+ for (const { path, port } of pathPortAssignmentData[host]) {
458
+ if (!confServer[host][path]) continue;
459
+
460
+ const { client, runtime, apis } = confServer[host][path];
461
+
462
+ const body = {
463
+ deployId,
464
+ host,
465
+ path,
466
+ port,
467
+ client,
468
+ runtime,
469
+ apis,
470
+ };
471
+
472
+ logger.info('save', body);
473
+
474
+ await new Instance(body).save();
475
+ }
476
+ }
477
+ }
478
+
479
+ await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
480
+ } catch (error) {
481
+ logger.error(error, error.stack);
482
+ }
483
+ },
315
484
  };
316
485
  }
317
486
 
package/src/cli/env.js CHANGED
@@ -28,15 +28,21 @@ class UnderpostRootEnv {
28
28
  get(key) {
29
29
  const exeRootPath = `${getNpmRootPath()}/underpost`;
30
30
  const envPath = `${exeRootPath}/.env`;
31
- if (!fs.existsSync(envPath)) return logger.error(`Unable to find underpost root environment`);
31
+ if (!fs.existsSync(envPath)) {
32
+ logger.error(`Unable to find underpost root environment`);
33
+ return undefined;
34
+ }
32
35
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
33
- logger.info('underpost root', { [key]: env[key] });
36
+ logger.info(`${key}(${typeof env[key]})`, env[key]);
34
37
  return env[key];
35
38
  },
36
39
  list() {
37
40
  const exeRootPath = `${getNpmRootPath()}/underpost`;
38
41
  const envPath = `${exeRootPath}/.env`;
39
- if (!fs.existsSync(envPath)) return logger.error(`Unable to find underpost root environment`);
42
+ if (!fs.existsSync(envPath)) {
43
+ logger.error(`Unable to find underpost root environment`);
44
+ return {};
45
+ }
40
46
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
41
47
  logger.info('underpost root', env);
42
48
  return env;
package/src/cli/fs.js CHANGED
@@ -24,7 +24,7 @@ class UnderpostFileStorage {
24
24
  getStorageConf(options) {
25
25
  let storage, storageConf;
26
26
  if (options.deployId && typeof options.deployId === 'string') {
27
- storageConf = `./engine-private/conf/${options.deployId}/storage.json`;
27
+ storageConf = options.storageFilePath ?? `./engine-private/conf/${options.deployId}/storage.json`;
28
28
  if (!fs.existsSync(storageConf)) fs.writeFileSync(storageConf, JSON.stringify({}), 'utf8');
29
29
  storage = JSON.parse(fs.readFileSync(storageConf, 'utf8'));
30
30
  }
@@ -35,10 +35,18 @@ class UnderpostFileStorage {
35
35
  },
36
36
  async recursiveCallback(
37
37
  path,
38
- options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false },
38
+ options = {
39
+ rm: false,
40
+ recursive: false,
41
+ deployId: '',
42
+ force: false,
43
+ pull: false,
44
+ git: false,
45
+ storageFilePath: '',
46
+ },
39
47
  ) {
40
48
  const { storage, storageConf } = UnderpostFileStorage.API.getStorageConf(options);
41
- const deleteFiles = UnderpostRepository.API.getDeleteFiles(path);
49
+ const deleteFiles = options.pull === true ? [] : UnderpostRepository.API.getDeleteFiles(path);
42
50
  for (const relativePath of deleteFiles) {
43
51
  const _path = path + '/' + relativePath;
44
52
  if (_path in storage) {
@@ -46,10 +54,6 @@ class UnderpostFileStorage {
46
54
  delete storage[_path];
47
55
  }
48
56
  }
49
- const files =
50
- options.git === true
51
- ? UnderpostRepository.API.getChangedFiles(path)
52
- : await fs.readdir(path, { recursive: true });
53
57
  if (options.pull === true) {
54
58
  for (const _path of Object.keys(storage)) {
55
59
  if (!fs.existsSync(_path) || options.force === true) {
@@ -57,7 +61,11 @@ class UnderpostFileStorage {
57
61
  await UnderpostFileStorage.API.pull(_path, options);
58
62
  } else logger.warn(`Pull path already exists`, _path);
59
63
  }
60
- } else
64
+ } else {
65
+ const files =
66
+ options.git === true
67
+ ? UnderpostRepository.API.getChangedFiles(path)
68
+ : await fs.readdir(path, { recursive: true });
61
69
  for (const relativePath of files) {
62
70
  const _path = path + '/' + relativePath;
63
71
  if (fs.statSync(_path).isDirectory()) {
@@ -68,6 +76,7 @@ class UnderpostFileStorage {
68
76
  if (storage) storage[_path] = {};
69
77
  } else logger.warn('File already exists', _path);
70
78
  }
79
+ }
71
80
  UnderpostFileStorage.API.writeStorageConf(storage, storageConf);
72
81
  if (options.git === true) {
73
82
  shellExec(`cd ${path} && git add .`);
@@ -84,7 +93,10 @@ class UnderpostFileStorage {
84
93
  if (options.rm === true) return await UnderpostFileStorage.API.delete(path, options);
85
94
  return await UnderpostFileStorage.API.upload(path, options);
86
95
  },
87
- async upload(path, options = { rm: false, recursive: false, deployId: '', force: false, pull: false }) {
96
+ async upload(
97
+ path,
98
+ options = { rm: false, recursive: false, deployId: '', force: false, pull: false, storageFilePath: '' },
99
+ ) {
88
100
  UnderpostFileStorage.API.cloudinaryConfig();
89
101
  const { storage, storageConf } = UnderpostFileStorage.API.getStorageConf(options);
90
102
  // path = UnderpostFileStorage.API.file2Zip(path);