underpost 2.8.1 → 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 (145) hide show
  1. package/.dockerignore +1 -0
  2. package/.github/workflows/ghpkg.yml +19 -49
  3. package/.github/workflows/npmpkg.yml +67 -0
  4. package/.github/workflows/publish.yml +5 -5
  5. package/.github/workflows/pwa-microservices-template.page.yml +12 -4
  6. package/.github/workflows/pwa-microservices-template.test.yml +2 -2
  7. package/.vscode/extensions.json +18 -71
  8. package/.vscode/settings.json +20 -3
  9. package/AUTHORS.md +16 -5
  10. package/CHANGELOG.md +123 -3
  11. package/Dockerfile +27 -70
  12. package/README.md +39 -29
  13. package/bin/build.js +186 -0
  14. package/bin/db.js +2 -24
  15. package/bin/deploy.js +1467 -236
  16. package/bin/file.js +67 -16
  17. package/bin/hwt.js +0 -10
  18. package/bin/index.js +1 -77
  19. package/bin/ssl.js +19 -11
  20. package/bin/util.js +9 -104
  21. package/bin/vs.js +26 -2
  22. package/cli.md +451 -0
  23. package/conf.js +29 -138
  24. package/docker-compose.yml +1 -1
  25. package/jsdoc.json +1 -1
  26. package/manifests/calico-custom-resources.yaml +25 -0
  27. package/manifests/deployment/adminer/deployment.yaml +32 -0
  28. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  29. package/manifests/deployment/adminer/service.yaml +13 -0
  30. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  31. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  32. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  33. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  34. package/manifests/deployment/kafka/deployment.yaml +69 -0
  35. package/manifests/deployment/mongo-express/deployment.yaml +60 -0
  36. package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
  37. package/manifests/kind-config-dev.yaml +12 -0
  38. package/manifests/kind-config.yaml +12 -0
  39. package/manifests/kubeadm-calico-config.yaml +119 -0
  40. package/manifests/letsencrypt-prod.yaml +15 -0
  41. package/manifests/mariadb/config.yaml +10 -0
  42. package/manifests/mariadb/kustomization.yaml +9 -0
  43. package/manifests/mariadb/pv.yaml +12 -0
  44. package/manifests/mariadb/pvc.yaml +10 -0
  45. package/manifests/mariadb/secret.yaml +8 -0
  46. package/manifests/mariadb/service.yaml +10 -0
  47. package/manifests/mariadb/statefulset.yaml +55 -0
  48. package/manifests/mongodb/backup-access.yaml +16 -0
  49. package/manifests/mongodb/backup-cronjob.yaml +42 -0
  50. package/manifests/mongodb/backup-pv-pvc.yaml +22 -0
  51. package/manifests/mongodb/configmap.yaml +26 -0
  52. package/manifests/mongodb/headless-service.yaml +10 -0
  53. package/manifests/mongodb/kustomization.yaml +11 -0
  54. package/manifests/mongodb/pv-pvc.yaml +23 -0
  55. package/manifests/mongodb/statefulset.yaml +125 -0
  56. package/manifests/mongodb-4.4/kustomization.yaml +7 -0
  57. package/manifests/mongodb-4.4/pv-pvc.yaml +23 -0
  58. package/manifests/mongodb-4.4/service-deployment.yaml +63 -0
  59. package/manifests/postgresql/configmap.yaml +9 -0
  60. package/manifests/postgresql/kustomization.yaml +10 -0
  61. package/manifests/postgresql/pv.yaml +15 -0
  62. package/manifests/postgresql/pvc.yaml +13 -0
  63. package/manifests/postgresql/service.yaml +10 -0
  64. package/manifests/postgresql/statefulset.yaml +37 -0
  65. package/manifests/valkey/kustomization.yaml +7 -0
  66. package/manifests/valkey/service.yaml +17 -0
  67. package/manifests/valkey/statefulset.yaml +41 -0
  68. package/package.json +127 -136
  69. package/src/api/core/core.service.js +1 -1
  70. package/src/api/default/default.service.js +1 -1
  71. package/src/api/user/user.model.js +16 -3
  72. package/src/api/user/user.service.js +15 -12
  73. package/src/cli/cluster.js +389 -0
  74. package/src/cli/cron.js +121 -0
  75. package/src/cli/db.js +222 -0
  76. package/src/cli/deploy.js +487 -0
  77. package/src/cli/env.js +58 -0
  78. package/src/cli/fs.js +161 -0
  79. package/src/cli/image.js +66 -0
  80. package/src/cli/index.js +312 -0
  81. package/src/cli/monitor.js +236 -0
  82. package/src/cli/repository.js +128 -0
  83. package/src/cli/script.js +53 -0
  84. package/src/cli/secrets.js +37 -0
  85. package/src/cli/test.js +118 -0
  86. package/src/client/components/core/Account.js +28 -24
  87. package/src/client/components/core/Auth.js +22 -4
  88. package/src/client/components/core/Blockchain.js +1 -1
  89. package/src/client/components/core/CalendarCore.js +128 -121
  90. package/src/client/components/core/CommonJs.js +283 -19
  91. package/src/client/components/core/CssCore.js +16 -4
  92. package/src/client/components/core/Docs.js +1 -2
  93. package/src/client/components/core/DropDown.js +5 -1
  94. package/src/client/components/core/EventsUI.js +3 -3
  95. package/src/client/components/core/FileExplorer.js +86 -78
  96. package/src/client/components/core/Input.js +22 -6
  97. package/src/client/components/core/JoyStick.js +2 -2
  98. package/src/client/components/core/LoadingAnimation.js +3 -12
  99. package/src/client/components/core/LogIn.js +3 -3
  100. package/src/client/components/core/LogOut.js +1 -1
  101. package/src/client/components/core/Modal.js +54 -20
  102. package/src/client/components/core/Panel.js +109 -90
  103. package/src/client/components/core/PanelForm.js +23 -30
  104. package/src/client/components/core/Recover.js +3 -3
  105. package/src/client/components/core/RichText.js +1 -11
  106. package/src/client/components/core/Router.js +3 -1
  107. package/src/client/components/core/Scroll.js +1 -0
  108. package/src/client/components/core/SignUp.js +2 -2
  109. package/src/client/components/core/Translate.js +47 -9
  110. package/src/client/components/core/Validator.js +9 -1
  111. package/src/client/components/core/VanillaJs.js +0 -9
  112. package/src/client/components/core/Worker.js +34 -31
  113. package/src/client/components/default/RoutesDefault.js +3 -2
  114. package/src/client/services/core/core.service.js +15 -10
  115. package/src/client/services/default/default.management.js +46 -37
  116. package/src/client/ssr/Render.js +6 -1
  117. package/src/client/ssr/body/CacheControl.js +2 -3
  118. package/src/client/sw/default.sw.js +3 -3
  119. package/src/db/mongo/MongooseDB.js +29 -1
  120. package/src/index.js +101 -19
  121. package/src/mailer/MailerProvider.js +3 -0
  122. package/src/runtime/lampp/Dockerfile +65 -0
  123. package/src/runtime/lampp/Lampp.js +1 -13
  124. package/src/runtime/xampp/Xampp.js +0 -13
  125. package/src/server/auth.js +3 -3
  126. package/src/server/backup.js +49 -93
  127. package/src/server/client-build.js +49 -46
  128. package/src/server/client-formatted.js +6 -3
  129. package/src/server/conf.js +297 -55
  130. package/src/server/dns.js +75 -62
  131. package/src/server/downloader.js +0 -8
  132. package/src/server/json-schema.js +77 -0
  133. package/src/server/logger.js +15 -10
  134. package/src/server/network.js +20 -161
  135. package/src/server/peer.js +2 -2
  136. package/src/server/process.js +25 -2
  137. package/src/server/proxy.js +7 -29
  138. package/src/server/runtime.js +53 -40
  139. package/src/server/ssl.js +1 -1
  140. package/src/server/start.js +122 -0
  141. package/src/server/valkey.js +27 -11
  142. package/test/api.test.js +0 -8
  143. package/src/dns.js +0 -22
  144. package/src/server/prompt-optimizer.js +0 -28
  145. package/startup.js +0 -11
@@ -0,0 +1,487 @@
1
+ import {
2
+ buildKindPorts,
3
+ buildPortProxyRouter,
4
+ buildProxyRouter,
5
+ Config,
6
+ deployRangePortFactory,
7
+ getDataDeploy,
8
+ loadReplicas,
9
+ pathPortAssignmentFactory,
10
+ } from '../server/conf.js';
11
+ import { loggerFactory } from '../server/logger.js';
12
+ import { shellExec } from '../server/process.js';
13
+ import fs from 'fs-extra';
14
+ import dotenv from 'dotenv';
15
+ import { DataBaseProvider } from '../db/DataBaseProvider.js';
16
+ import UnderpostRootEnv from './env.js';
17
+ import UnderpostCluster from './cluster.js';
18
+
19
+ const logger = loggerFactory(import.meta);
20
+
21
+ class UnderpostDeploy {
22
+ static NETWORK = {};
23
+ static API = {
24
+ sync(deployList, { versions, replicas }) {
25
+ const deployGroupId = 'dd.router';
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);
41
+ return getDataDeploy({
42
+ buildSingleReplica: true,
43
+ deployGroupId,
44
+ });
45
+ },
46
+ async routerFactory(deployList, env) {
47
+ const initEnvPath = `./engine-private/conf/${deployList.split(',')[0]}/.env.${env}`;
48
+ const initEnvObj = dotenv.parse(fs.readFileSync(initEnvPath, 'utf8'));
49
+ process.env.PORT = initEnvObj.PORT;
50
+ process.env.NODE_ENV = env;
51
+ await Config.build(undefined, 'proxy', deployList);
52
+ return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
53
+ },
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
66
+ kind: Deployment
67
+ metadata:
68
+ name: ${deployId}-${env}-${suffix}
69
+ labels:
70
+ app: ${deployId}-${env}-${suffix}
71
+ spec:
72
+ replicas: ${replicas}
73
+ selector:
74
+ matchLabels:
75
+ app: ${deployId}-${env}-${suffix}
76
+ template:
77
+ metadata:
78
+ labels:
79
+ app: ${deployId}-${env}-${suffix}
80
+ spec:
81
+ containers:
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
106
+ ---
107
+ apiVersion: v1
108
+ kind: Service
109
+ metadata:
110
+ name: ${deployId}-${env}-${suffix}-service
111
+ spec:
112
+ selector:
113
+ app: ${deployId}-${env}-${suffix}
114
+ ports:
115
+ {{ports}} type: LoadBalancer`;
116
+ },
117
+ async buildManifest(deployList, env, options) {
118
+ const resources = UnderpostDeploy.API.resourcesFactory();
119
+ const replicas = options.replicas;
120
+
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',
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');
150
+
151
+ let proxyYaml = '';
152
+ let secretYaml = '';
153
+
154
+ for (const host of Object.keys(confServer)) {
155
+ if (env === 'production')
156
+ secretYaml += `
157
+ ---
158
+ apiVersion: cert-manager.io/v1
159
+ kind: Certificate
160
+ metadata:
161
+ name: ${host}
162
+ spec:
163
+ commonName: ${host}
164
+ dnsNames:
165
+ - ${host}
166
+ issuerRef:
167
+ name: letsencrypt-prod
168
+ kind: ClusterIssuer
169
+ secretName: ${host}`;
170
+
171
+ const pathPortAssignment = pathPortAssignmentData[host];
172
+ // logger.info('', { host, pathPortAssignment });
173
+ proxyYaml += `
174
+ ---
175
+ apiVersion: projectcontour.io/v1
176
+ kind: HTTPProxy
177
+ metadata:
178
+ name: ${host}
179
+ spec:
180
+ virtualhost:
181
+ fqdn: ${host}${
182
+ env === 'development'
183
+ ? ''
184
+ : `
185
+ tls:
186
+ secretName: ${host}`
187
+ }
188
+ routes:`;
189
+ for (const conditionObj of pathPortAssignment) {
190
+ const { path, port } = conditionObj;
191
+ proxyYaml += `
192
+ - conditions:
193
+ - prefix: ${path}
194
+ enableWebsockets: true
195
+ services:
196
+ ${UnderpostDeploy.API.deploymentYamlServiceFactory({
197
+ deployId,
198
+ env,
199
+ port,
200
+ deploymentVersions:
201
+ options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'],
202
+ })}`;
203
+ }
204
+ }
205
+ const yamlPath = `./engine-private/conf/${deployId}/build/${env}/proxy.yaml`;
206
+ fs.writeFileSync(yamlPath, proxyYaml, 'utf8');
207
+ if (env === 'production') {
208
+ const yamlPath = `./engine-private/conf/${deployId}/build/${env}/secret.yaml`;
209
+ fs.writeFileSync(yamlPath, secretYaml, 'utf8');
210
+ } else {
211
+ const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml'];
212
+ for (const file of deploymentsFiles) {
213
+ if (fs.existsSync(`./engine-private/conf/${deployId}/build/${env}/${file}`)) {
214
+ fs.copyFileSync(
215
+ `./engine-private/conf/${deployId}/build/${env}/${file}`,
216
+ `./manifests/deployment/${deployId}-${env}/${file}`,
217
+ );
218
+ }
219
+ }
220
+ }
221
+ }
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
+ },
231
+ async callback(
232
+ deployList = 'default',
233
+ env = 'development',
234
+ options = {
235
+ remove: false,
236
+ infoRouter: false,
237
+ sync: false,
238
+ buildManifest: false,
239
+ infoUtil: false,
240
+ expose: false,
241
+ cert: false,
242
+ versions: '',
243
+ traffic: '',
244
+ dashboardUpdate: false,
245
+ replicas: '',
246
+ disableUpdateDeployment: false,
247
+ infoTraffic: false,
248
+ rebuildClientsBundle: false,
249
+ },
250
+ ) {
251
+ if (options.infoUtil === true)
252
+ return logger.info(`
253
+ kubectl rollout restart deployment/deployment-name
254
+ kubectl rollout undo deployment/deployment-name
255
+ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
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
+ `);
273
+ if (deployList === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`))
274
+ deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
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
+ );
298
+ const etcHost = (
299
+ concat,
300
+ ) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
301
+ ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
302
+ let concatHots = '';
303
+
304
+ for (const _deployId of deployList.split(',')) {
305
+ const deployId = _deployId.trim();
306
+ if (!deployId) continue;
307
+ if (options.expose === true) {
308
+ const svc = UnderpostDeploy.API.get(deployId, 'svc')[0];
309
+ const port = parseInt(svc[`PORT(S)`].split('/TCP')[0]);
310
+ logger.info(deployId, {
311
+ svc,
312
+ port,
313
+ });
314
+ shellExec(`sudo kubectl port-forward -n default svc/${svc.NAME} ${port}:${port}`, { async: true });
315
+ continue;
316
+ }
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
+ }
323
+
324
+ const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
325
+ for (const host of Object.keys(confServer)) {
326
+ shellExec(`sudo kubectl delete HTTPProxy ${host}`);
327
+ if (env === 'production' && options.cert === true) shellExec(`sudo kubectl delete Certificate ${host}`);
328
+ if (!options.remove === true && env === 'development') concatHots += ` ${host}`;
329
+ }
330
+
331
+ const manifestsPath =
332
+ env === 'production'
333
+ ? `engine-private/conf/${deployId}/build/production`
334
+ : `manifests/deployment/${deployId}-${env}`;
335
+
336
+ if (!options.remove === true) {
337
+ if (!options.disableUpdateDeployment) shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml`);
338
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml`);
339
+ if (env === 'production' && options.cert === true)
340
+ shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
341
+ }
342
+ }
343
+ let renderHosts;
344
+ switch (process.platform) {
345
+ case 'linux':
346
+ {
347
+ switch (env) {
348
+ case 'development':
349
+ renderHosts = etcHost(concatHots);
350
+ fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
351
+
352
+ break;
353
+
354
+ default:
355
+ break;
356
+ }
357
+ }
358
+ break;
359
+
360
+ default:
361
+ break;
362
+ }
363
+ if (renderHosts)
364
+ logger.info(
365
+ `
366
+ ` + renderHosts,
367
+ );
368
+ },
369
+ get(deployId, kindType = 'pods') {
370
+ const raw = shellExec(`sudo kubectl get ${kindType} --all-namespaces -o wide`, {
371
+ stdout: true,
372
+ disableLog: true,
373
+ silent: true,
374
+ });
375
+
376
+ const heads = raw
377
+ .split(`\n`)[0]
378
+ .split(' ')
379
+ .filter((_r) => _r.trim());
380
+
381
+ const pods = raw
382
+ .split(`\n`)
383
+ .filter((r) => (deployId ? r.match(deployId) : r.trim() && !r.match('NAME')))
384
+ .map((r) => r.split(' ').filter((_r) => _r.trim()));
385
+
386
+ const result = [];
387
+
388
+ for (const row of pods) {
389
+ const pod = {};
390
+ let index = -1;
391
+ for (const head of heads) {
392
+ index++;
393
+ pod[head] = row[index];
394
+ }
395
+ result.push(pod);
396
+ }
397
+
398
+ return result;
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
+ },
484
+ };
485
+ }
486
+
487
+ export default UnderpostDeploy;
package/src/cli/env.js ADDED
@@ -0,0 +1,58 @@
1
+ import { getNpmRootPath, writeEnv } from '../server/conf.js';
2
+ import fs from 'fs-extra';
3
+ import { loggerFactory } from '../server/logger.js';
4
+ import dotenv from 'dotenv';
5
+
6
+ dotenv.config();
7
+
8
+ const logger = loggerFactory(import.meta);
9
+
10
+ class UnderpostRootEnv {
11
+ static API = {
12
+ set(key, value) {
13
+ const exeRootPath = `${getNpmRootPath()}/underpost`;
14
+ const envPath = `${exeRootPath}/.env`;
15
+ let env = {};
16
+ if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
17
+ env[key] = value;
18
+ writeEnv(envPath, env);
19
+ },
20
+ delete(key) {
21
+ const exeRootPath = `${getNpmRootPath()}/underpost`;
22
+ const envPath = `${exeRootPath}/.env`;
23
+ let env = {};
24
+ if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
25
+ delete env[key];
26
+ writeEnv(envPath, env);
27
+ },
28
+ get(key) {
29
+ const exeRootPath = `${getNpmRootPath()}/underpost`;
30
+ const envPath = `${exeRootPath}/.env`;
31
+ if (!fs.existsSync(envPath)) {
32
+ logger.error(`Unable to find underpost root environment`);
33
+ return undefined;
34
+ }
35
+ const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
36
+ logger.info(`${key}(${typeof env[key]})`, env[key]);
37
+ return env[key];
38
+ },
39
+ list() {
40
+ const exeRootPath = `${getNpmRootPath()}/underpost`;
41
+ const envPath = `${exeRootPath}/.env`;
42
+ if (!fs.existsSync(envPath)) {
43
+ logger.error(`Unable to find underpost root environment`);
44
+ return {};
45
+ }
46
+ const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
47
+ logger.info('underpost root', env);
48
+ return env;
49
+ },
50
+ clean() {
51
+ const exeRootPath = `${getNpmRootPath()}/underpost`;
52
+ const envPath = `${exeRootPath}/.env`;
53
+ fs.removeSync(envPath);
54
+ },
55
+ };
56
+ }
57
+
58
+ export default UnderpostRootEnv;