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
package/src/cli/deploy.js CHANGED
@@ -242,12 +242,10 @@ spec:
242
242
  cert: false,
243
243
  versions: '',
244
244
  traffic: '',
245
- dashboardUpdate: false,
246
245
  replicas: '',
247
246
  restoreHosts: false,
248
247
  disableUpdateDeployment: false,
249
248
  infoTraffic: false,
250
- rebuildClientsBundle: false,
251
249
  },
252
250
  ) {
253
251
  if (options.infoUtil === true)
@@ -311,22 +309,21 @@ Password: <Your Key>
311
309
  deployId,
312
310
  env,
313
311
  traffic: UnderpostDeploy.API.getCurrentTraffic(deployId),
312
+ router: await UnderpostDeploy.API.routerFactory(deployId, env),
313
+ pods: await UnderpostDeploy.API.get(deployId),
314
314
  });
315
315
  }
316
316
  return;
317
317
  }
318
- if (options.rebuildClientsBundle === true) await UnderpostDeploy.API.rebuildClientsBundle(deployList);
319
318
  if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
320
319
  if (!options.replicas) options.replicas = 1;
321
320
  if (options.sync) UnderpostDeploy.API.sync(deployList, options);
322
321
  if (options.buildManifest === true) await UnderpostDeploy.API.buildManifest(deployList, env, options);
323
- if (options.infoRouter === true) logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
324
- if (options.dashboardUpdate === true) await UnderpostDeploy.API.updateDashboardData(deployList, env, options);
325
- if (options.infoRouter === true) return;
326
- shellExec(`kubectl delete configmap underpost-config`);
327
- shellExec(
328
- `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
329
- );
322
+ if (options.infoRouter === true) {
323
+ logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
324
+ return;
325
+ }
326
+ UnderpostDeploy.API.configMap(env);
330
327
  let renderHosts = '';
331
328
  let concatHots = '';
332
329
  const etcHost = (
@@ -435,23 +432,6 @@ Password: <Your Key>
435
432
 
436
433
  return result;
437
434
  },
438
- rebuildClientsBundle(deployList) {
439
- for (const _deployId of deployList.split(',')) {
440
- const deployId = _deployId.trim();
441
- const repoName = `engine-${deployId.split('-')[1]}`;
442
-
443
- shellExec(`underpost script set ${deployId}-client-build '
444
- cd /home/dd/engine &&
445
- git checkout . &&
446
- underpost pull . underpostnet/${repoName} &&
447
- underpost pull ./engine-private underpostnet/${repoName}-private &&
448
- underpost env ${deployId} production &&
449
- node bin/deploy build-full-client ${deployId}
450
- '`);
451
-
452
- shellExec(`node bin script run ${deployId}-client-build --itc --pod-name ${deployId}`);
453
- }
454
- },
455
435
  resourcesFactory() {
456
436
  return {
457
437
  requests: {
@@ -465,60 +445,6 @@ node bin/deploy build-full-client ${deployId}
465
445
  totalPods: UnderpostRootEnv.API.get('total-pods'),
466
446
  };
467
447
  },
468
- async updateDashboardData(deployList, env, options) {
469
- try {
470
- const deployId = process.env.DEFAULT_DEPLOY_ID;
471
- const host = process.env.DEFAULT_DEPLOY_HOST;
472
- const path = process.env.DEFAULT_DEPLOY_PATH;
473
- const { db } = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'))[host][
474
- path
475
- ];
476
-
477
- await DataBaseProvider.load({ apis: ['instance'], host, path, db });
478
-
479
- /** @type {import('../api/instance/instance.model.js').InstanceModel} */
480
- const Instance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Instance;
481
-
482
- await Instance.deleteMany();
483
-
484
- for (const _deployId of deployList.split(',')) {
485
- const deployId = _deployId.trim();
486
- if (!deployId) continue;
487
- const confServer = loadReplicas(
488
- JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
489
- 'proxy',
490
- );
491
- const router = await UnderpostDeploy.API.routerFactory(deployId, env);
492
- const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
493
-
494
- for (const host of Object.keys(confServer)) {
495
- for (const { path, port } of pathPortAssignmentData[host]) {
496
- if (!confServer[host][path]) continue;
497
-
498
- const { client, runtime, apis } = confServer[host][path];
499
-
500
- const body = {
501
- deployId,
502
- host,
503
- path,
504
- port,
505
- client,
506
- runtime,
507
- apis,
508
- };
509
-
510
- logger.info('save', body);
511
-
512
- await new Instance(body).save();
513
- }
514
- }
515
- }
516
-
517
- await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
518
- } catch (error) {
519
- logger.error(error, error.stack);
520
- }
521
- },
522
448
  existsContainerFile({ podName, path }) {
523
449
  return JSON.parse(
524
450
  shellExec(`kubectl exec ${podName} -- test -f ${path} && echo "true" || echo "false"`, {
@@ -528,6 +454,39 @@ node bin/deploy build-full-client ${deployId}
528
454
  }).trim(),
529
455
  );
530
456
  },
457
+ checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = []) {
458
+ const cmd = `underpost config get container-status`;
459
+ const pods = UnderpostDeploy.API.get(`${deployId}-${env}-${traffic}`);
460
+ const readyPods = [];
461
+ const notReadyPods = [];
462
+ for (const pod of pods) {
463
+ const { NAME } = pod;
464
+ if (ignoresNames && ignoresNames.find((t) => NAME.trim().toLowerCase().match(t.trim().toLowerCase()))) continue;
465
+ if (
466
+ shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true }).match(
467
+ `${deployId}-${env}-running-deployment`,
468
+ )
469
+ ) {
470
+ readyPods.push(pod);
471
+ } else {
472
+ notReadyPods.push(pod);
473
+ }
474
+ }
475
+ return { ready: notReadyPods.length === 0, notReadyPods, readyPods };
476
+ },
477
+ configMap(env) {
478
+ shellExec(`kubectl delete configmap underpost-config`);
479
+ shellExec(
480
+ `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
481
+ );
482
+ },
483
+ switchTraffic(deployId, env, targetTraffic, replicas = 1) {
484
+ UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
485
+ shellExec(
486
+ `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} ${deployId} ${env}`,
487
+ );
488
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
489
+ },
531
490
  };
532
491
  }
533
492
 
package/src/cli/index.js CHANGED
@@ -23,6 +23,7 @@ program.name('underpost').description(`underpost ci/cd cli ${Underpost.version}`
23
23
  program
24
24
  .command('new')
25
25
  .argument('<app-name>', 'The name of the application to create.')
26
+ .option('--dev', 'Sets the development cli context')
26
27
  .description('Initializes a new Underpost project with a predefined structure.')
27
28
  .action(Underpost.repo.new);
28
29
 
@@ -112,6 +113,7 @@ program
112
113
  .option('--mariadb', 'Initializes the cluster with a MariaDB statefulset.')
113
114
  .option('--mysql', 'Initializes the cluster with a MySQL statefulset.')
114
115
  .option('--mongodb', 'Initializes the cluster with a MongoDB statefulset.')
116
+ .option('--mongo-db-host <host>', 'Set custom mongo db host')
115
117
  .option('--postgresql', 'Initializes the cluster with a PostgreSQL statefulset.')
116
118
  .option('--mongodb4', 'Initializes the cluster with a MongoDB 4.4 service.')
117
119
  .option('--valkey', 'Initializes the cluster with a Valkey service.')
@@ -122,6 +124,11 @@ program
122
124
  .option('--full', 'Initializes the cluster with all available statefulsets and services.')
123
125
  .option('--ns-use <ns-name>', 'Switches the current Kubernetes context to the specified namespace.')
124
126
  .option('--kubeadm', 'Initializes the cluster using kubeadm for control plane management.')
127
+ .option('--grafana', 'Initializes the cluster with a Grafana deployment.')
128
+ .option(
129
+ '--prom [hosts]',
130
+ 'Initializes the cluster with a Prometheus Operator deployment and monitor scrap for specified hosts.',
131
+ )
125
132
  .option('--dev', 'Initializes a development-specific cluster configuration.')
126
133
  .option('--list-pods', 'Displays detailed information about all pods.')
127
134
  .option('--info-capacity', 'Displays the current total machine capacity information.')
@@ -154,7 +161,6 @@ program
154
161
  '--build-manifest',
155
162
  'Builds Kubernetes YAML manifests, including deployments, services, proxies, and secrets.',
156
163
  )
157
- .option('--dashboard-update', 'Updates dashboard instance data with the current router configuration.')
158
164
  .option('--replicas <replicas>', 'Sets a custom number of replicas for deployments.')
159
165
  .option('--versions <deployment-versions>', 'A comma-separated list of custom deployment versions.')
160
166
  .option('--traffic <traffic-versions>', 'A comma-separated list of custom deployment traffic weights.')
@@ -162,10 +168,6 @@ program
162
168
  .option('--info-traffic', 'Retrieves traffic configuration from current resource deployments.')
163
169
  .option('--kubeadm', 'Enables the kubeadm context for deployment operations.')
164
170
  .option('--restore-hosts', 'Restores default `/etc/hosts` entries.')
165
- .option(
166
- '--rebuild-clients-bundle',
167
- 'Inside the container, rebuilds client bundles (only static public or storage client files).',
168
- )
169
171
  .description('Manages application deployments, defaulting to deploying development pods.')
170
172
  .action(Underpost.deploy.callback);
171
173
 
@@ -239,6 +241,20 @@ program
239
241
  .description('Manages database operations, including import, export, and collection management.')
240
242
  .action(Underpost.db.callback);
241
243
 
244
+ program
245
+ .command('metadata')
246
+ .argument('[deploy-id]', 'The deployment ID to manage metadata.')
247
+ .argument('[host]', 'The host to manage metadata.')
248
+ .argument('[path]', 'The path to manage metadata.')
249
+ .option('--import', 'Imports from local storage.')
250
+ .option('--export', 'Exports to local storage.')
251
+ .option('--crons', 'Apply to cron data collection')
252
+ .option('--instances', 'Apply to instance data collection')
253
+ .option('--generate', 'Generate cluster metadata')
254
+ .option('--itc', 'Apply under container execution context')
255
+ .description('Manages cluster metadata operations, including import and export.')
256
+ .action(Underpost.db.clusterMetadataBackupCallback);
257
+
242
258
  // 'script' command: Execute scripts
243
259
  program
244
260
  .command('script')
@@ -267,7 +283,6 @@ program
267
283
  .option('--itc', 'Executes cron jobs within the container execution context.')
268
284
  .option('--init', 'Initializes cron jobs for the default deployment ID.')
269
285
  .option('--git', 'Uploads cron job configurations to GitHub.')
270
- .option('--dashboard-update', 'Updates dashboard cron data with the current job configurations.')
271
286
  .description('Manages cron jobs, including initialization, execution, and configuration updates.')
272
287
  .action(Underpost.cron.callback);
273
288
 
@@ -315,6 +330,13 @@ program
315
330
  .description('Manages health server monitoring for specified deployments.')
316
331
  .action(Underpost.monitor.callback);
317
332
 
333
+ // 'ssh' command: SSH management
334
+ program
335
+ .command('ssh')
336
+ .option('--generate', 'Generates new ssh credential and stores it in current private keys file storage.')
337
+ .description('Import and start ssh server and client based on current default deployment ID.')
338
+ .action(Underpost.ssh.callback);
339
+
318
340
  // 'run' command: Run a script
319
341
  program
320
342
  .command('run')
@@ -326,6 +348,7 @@ program
326
348
  .option('--pod-name <pod-name>', 'Optional: Specifies the pod name for test execution.')
327
349
  .option('--volume-host-path <volume-host-path>', 'Optional: Specifies the volume host path for test execution.')
328
350
  .option('--volume-mount-path <volume-mount-path>', 'Optional: Specifies the volume mount path for test execution.')
351
+ .option('--volume-type <volume-type>', 'Optional: Specifies the volume type for test execution.')
329
352
  .option('--image-name <image-name>', 'Optional: Specifies the image name for test execution.')
330
353
  .option('--container-name <container-name>', 'Optional: Specifies the container name for test execution.')
331
354
  .option('--namespace <namespace>', 'Optional: Specifies the namespace for test execution.')
@@ -120,10 +120,7 @@ class UnderpostMonitor {
120
120
  fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'),
121
121
  );
122
122
 
123
- shellExec(`kubectl delete configmap underpost-config`);
124
- shellExec(
125
- `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
126
- );
123
+ UnderpostDeploy.API.configMap(env);
127
124
 
128
125
  for (const host of Object.keys(confServer)) {
129
126
  shellExec(`sudo kubectl delete HTTPProxy ${host}`);
@@ -173,19 +170,15 @@ class UnderpostMonitor {
173
170
  monitorTrafficName = undefined;
174
171
  monitorPodName = undefined;
175
172
  }
176
- const cmd = `underpost config get container-status`;
177
173
  const checkDeploymentReadyStatus = () => {
178
- const pods = UnderpostDeploy.API.get(`${deployId}-${env}-${traffic}`);
179
- if (pods && pods[0]) {
180
- const { NAME } = pods[0];
181
- if (
182
- shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true }).match(
183
- `${deployId}-${env}-running-deployment`,
184
- )
185
- ) {
186
- monitorPodName = NAME;
187
- monitorTrafficName = `${traffic}`;
188
- }
174
+ const { ready, notReadyPods, readyPods } = UnderpostDeploy.API.checkDeploymentReadyStatus(
175
+ deployId,
176
+ env,
177
+ traffic,
178
+ );
179
+ if (ready) {
180
+ monitorPodName = readyPods[0].NAME;
181
+ monitorTrafficName = `${traffic}`;
189
182
  }
190
183
  };
191
184
  if (!monitorPodName) {
@@ -80,7 +80,7 @@ class UnderpostRepository {
80
80
  );
81
81
  },
82
82
 
83
- new(repositoryName) {
83
+ new(repositoryName, options = { dev: false }) {
84
84
  return new Promise(async (resolve, reject) => {
85
85
  try {
86
86
  await logger.setUpInfo();
@@ -89,15 +89,22 @@ class UnderpostRepository {
89
89
  await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':'),
90
90
  );
91
91
  else actionInitLog();
92
- const exeRootPath = `${getNpmRootPath()}/underpost`;
92
+ const npmRoot = getNpmRootPath();
93
+ const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
93
94
  const destFolder = `./${repositoryName}`;
94
95
  logger.info('Note: This process may take several minutes to complete');
95
96
  logger.info('build app', { destFolder });
96
97
  if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
97
98
  fs.mkdirSync(destFolder, { recursive: true });
98
- fs.copySync(exeRootPath, destFolder);
99
- fs.writeFileSync(`${destFolder}/.gitignore`, fs.readFileSync(`${exeRootPath}/.dockerignore`, 'utf8'), 'utf8');
100
- shellExec(`cd ${destFolder} && git init && git add . && git commit -m "Base template implementation"`);
99
+ if (!options.dev) {
100
+ fs.copySync(underpostRoot, destFolder);
101
+ fs.writeFileSync(
102
+ `${destFolder}/.gitignore`,
103
+ fs.readFileSync(`${underpostRoot}/.dockerignore`, 'utf8'),
104
+ 'utf8',
105
+ );
106
+ shellExec(`cd ${destFolder} && git init && git add . && git commit -m "Base template implementation"`);
107
+ }
101
108
  shellExec(`cd ${destFolder} && npm run build`);
102
109
  shellExec(`cd ${destFolder} && npm run dev`);
103
110
  return resolve();
package/src/cli/run.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { daemonProcess, getTerminalPid, openTerminal, pbcopy, shellCd, shellExec } from '../server/process.js';
2
2
  import read from 'read';
3
3
  import { getNpmRootPath } from '../server/conf.js';
4
- import { loggerFactory } from '../server/logger.js';
4
+ import { actionInitLog, loggerFactory } from '../server/logger.js';
5
5
  import UnderpostTest from './test.js';
6
6
  import fs from 'fs-extra';
7
7
  import { range, setPad, timer } from '../client/components/core/CommonJs.js';
8
8
  import UnderpostDeploy from './deploy.js';
9
+ import UnderpostRootEnv from './env.js';
9
10
 
10
11
  const logger = loggerFactory(import.meta);
11
12
 
@@ -53,6 +54,9 @@ class UnderpostRun {
53
54
  }`,
54
55
  );
55
56
  },
57
+ 'underpost-config': (path, options = UnderpostRun.DEFAULT_OPTION) => {
58
+ UnderpostDeploy.API.configMap(path ?? 'production');
59
+ },
56
60
  'gpu-env': (path, options = UnderpostRun.DEFAULT_OPTION) => {
57
61
  shellExec(
58
62
  `node bin cluster --dev --reset && node bin cluster --dev --dedicated-gpu --kubeadm && kubectl get pods --all-namespaces -o wide -w`,
@@ -64,10 +68,79 @@ class UnderpostRun {
64
68
  shellExec(`kubectl delete pod tf-gpu-test-pod`);
65
69
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/deployment/tensorflow/tf-gpu-test.yaml`);
66
70
  },
71
+ 'dev-cluster': (path, options = UnderpostRun.DEFAULT_OPTION) => {
72
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
73
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
74
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
75
+ shellExec(
76
+ `${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${'127.0.0.1'} --pull-image`,
77
+ );
78
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
79
+ shellExec(`${baseCommand} deploy --expose mongo`, { async: true });
80
+ shellExec(`${baseCommand} deploy --expose valkey`, { async: true });
81
+ },
82
+ 'ssh-cluster-info': (path, options = UnderpostRun.DEFAULT_OPTION) => {
83
+ const { underpostRoot } = options;
84
+ shellExec(`chmod +x ${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
85
+ shellExec(`${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
86
+ },
87
+ 'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
88
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
89
+ shellExec(`${baseCommand} run ide /home/dd/cyberia-server`);
90
+ shellExec(`${baseCommand} run ide /home/dd/cyberia-client`);
91
+ },
92
+ 'engine-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
93
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
94
+ shellExec(`${baseCommand} run ide /home/dd/engine`);
95
+ shellExec(`${baseCommand} run ide /home/dd/engine/engine-private`);
96
+ },
97
+ 'template-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
98
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
99
+ shellCd('/home/dd/engine');
100
+ shellExec(`git reset`);
101
+ shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
102
+ shellExec(`${baseCommand} push . underpostnet/engine`);
103
+ },
104
+ clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
105
+ shellCd(path ?? `/home/dd/engine`);
106
+ shellExec(`node bin/deploy clean-core-repo`);
107
+ },
108
+ pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
109
+ shellCd(`/home/dd/engine`);
110
+ shellExec(`node bin/deploy clean-core-repo`);
111
+ shellExec(`underpost pull . underpostnet/engine`);
112
+ shellExec(`underpost pull engine-private underpostnet/engine-private`, { silent: true });
113
+ },
114
+ 'release-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
115
+ actionInitLog();
116
+ shellExec(`underpost --version`);
117
+ shellCd(`/home/dd/engine`);
118
+ for (const _deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
119
+ const deployId = _deployId.trim();
120
+ shellExec(`underpost run deploy ${deployId}`, { async: true });
121
+ }
122
+ },
123
+ 'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
124
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
125
+ shellCd('/home/dd/engine');
126
+ shellExec(`git reset`);
127
+ shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
128
+ shellExec(`${baseCommand} push . underpostnet/engine`);
129
+ },
67
130
  ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
68
131
  const { underpostRoot } = options;
69
132
  shellExec(`node ${underpostRoot}/bin/vs ${path}`);
70
133
  },
134
+ 'dev-client': (_path, options = UnderpostRun.DEFAULT_OPTION) => {
135
+ let [deployId, hostpath, subConf, lite] = _path.split(',');
136
+ let [host, path] = hostpath.split('/');
137
+ if (!path) path = '/';
138
+ shellExec(`npm run dev-client ${deployId} ${host} ${path} ${subConf} static${lite === 'l' ? ' l' : ''}`);
139
+ },
140
+ 'dev-api': (path, options = UnderpostRun.DEFAULT_OPTION) => {
141
+ let [deployId, subConf] = path.split(',');
142
+ shellExec(`npm run dev-api ${deployId} ${subConf}`);
143
+ },
71
144
  monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
72
145
  const pid = getTerminalPid();
73
146
  logger.info('monitor pid', pid);
@@ -141,6 +214,92 @@ class UnderpostRun {
141
214
  };
142
215
  _monitor();
143
216
  },
217
+ 'db-client': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
218
+ const { underpostRoot } = options;
219
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/.`);
220
+ },
221
+ promote: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
222
+ let [inputDeployId, inputEnv, inputReplicas] = path.split(',');
223
+ if (!inputEnv) inputEnv = 'production';
224
+ if (!inputReplicas) inputReplicas = 1;
225
+ if (inputDeployId === 'dd') {
226
+ for (const deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
227
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
228
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
229
+ UnderpostDeploy.API.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas);
230
+ }
231
+ } else {
232
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(inputDeployId);
233
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
234
+ UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas);
235
+ }
236
+ },
237
+
238
+ metrics: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
239
+ const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
240
+ let hosts = [];
241
+ for (const deployId of deployList) {
242
+ const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
243
+ hosts = hosts.concat(Object.keys(confServer));
244
+ }
245
+ shellExec(`node bin cluster --prom ${hosts.join(',')}`);
246
+ shellExec(`node bin cluster --grafana`);
247
+ },
248
+
249
+ cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
250
+ const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
251
+ const env = 'production';
252
+ shellCd(`/home/dd/engine`);
253
+ shellExec(`underpost cluster --reset`);
254
+ await timer(5000);
255
+ shellExec(`underpost cluster --kubeadm`);
256
+ await timer(5000);
257
+ shellExec(`underpost dockerfile-pull-base-images --path /home/dd/engine/src/runtime/lampp --kubeadm-load`);
258
+ await timer(5000);
259
+ shellExec(`underpost cluster --kubeadm --pull-image --mongodb`);
260
+ await timer(5000);
261
+ shellExec(`underpost cluster --kubeadm --pull-image --mariadb`);
262
+ await timer(5000);
263
+ for (const deployId of deployList) {
264
+ shellExec(`underpost db ${deployId} --import --git`);
265
+ }
266
+ await timer(5000);
267
+ shellExec(`underpost cluster --kubeadm --pull-image --valkey`);
268
+ await timer(5000);
269
+ shellExec(`underpost cluster --kubeadm --contour`);
270
+ await timer(5000);
271
+ shellExec(`underpost cluster --kubeadm --cert-manager`);
272
+ for (const deployId of deployList) {
273
+ shellExec(`underpost deploy ${deployId} ${env} --kubeadm --cert`);
274
+ }
275
+ },
276
+ deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
277
+ const deployId = path;
278
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
279
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
280
+ const env = 'production';
281
+ const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
282
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
283
+
284
+ let checkStatusIteration = 0;
285
+ const checkStatusIterationMsDelay = 1000;
286
+ const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
287
+ logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
288
+
289
+ while (!UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods).ready) {
290
+ await timer(checkStatusIterationMsDelay);
291
+ checkStatusIteration++;
292
+ logger.info(
293
+ `${iteratorTag} | Deployment in progress... | Delay number check iterations: ${checkStatusIteration}`,
294
+ );
295
+ }
296
+
297
+ logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
298
+
299
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
300
+
301
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
302
+ },
144
303
  'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
145
304
  const { underpostRoot } = options;
146
305
  const podName = 'tf-vae-test';
@@ -187,6 +346,12 @@ class UnderpostRun {
187
346
  const volumeHostPath = options.volumeHostPath || path;
188
347
  const enableVolumeMount = volumeHostPath && volumeMountPath;
189
348
 
349
+ if (options.volumeType === 'dev') options.volumeType = 'FileOrCreate';
350
+ const volumeType =
351
+ options.volumeType || (enableVolumeMount && fs.statSync(volumeHostPath).isDirectory()) ? 'Directory' : 'File';
352
+
353
+ const envs = UnderpostRootEnv.API.list();
354
+
190
355
  const cmd = `kubectl apply -f - <<EOF
191
356
  apiVersion: v1
192
357
  kind: Pod
@@ -210,16 +375,19 @@ ${
210
375
  ${args.map((arg) => ` ${arg}`).join('\n')}`
211
376
  : ''
212
377
  }
213
- ${
378
+ ${`${
214
379
  gpuEnable
215
380
  ? ` resources:
216
381
  limits:
217
382
  nvidia.com/gpu: '1'
218
- env:
219
- - name: NVIDIA_VISIBLE_DEVICES
220
- value: all`
383
+ `
221
384
  : ''
222
- }
385
+ } env:
386
+ ${Object.keys(envs)
387
+ .map((key) => ({ key, value: typeof envs[key] === 'number' ? envs[key] : `"${envs[key]}"` }))
388
+ .concat(gpuEnable ? [{ key: 'NVIDIA_VISIBLE_DEVICES', value: 'all' }] : [])
389
+ .map((env) => ` - name: ${env.key}\n value: ${env.value}`)
390
+ .join('\n')}`}
223
391
  ${
224
392
  enableVolumeMount
225
393
  ? `
@@ -230,7 +398,7 @@ ${
230
398
  - name: ${volumeName}
231
399
  hostPath:
232
400
  path: ${volumeHostPath}
233
- type: ${fs.statSync(volumeHostPath).isDirectory() ? 'Directory' : 'File'}`
401
+ type: ${volumeType}`
234
402
  : ''
235
403
  }
236
404
  EOF`;
package/src/cli/ssh.js ADDED
@@ -0,0 +1,32 @@
1
+ import { getNpmRootPath } from '../server/conf.js';
2
+ import { shellExec } from '../server/process.js';
3
+
4
+ class UnderpostSSH {
5
+ static API = {
6
+ /**
7
+ * @method callback
8
+ * @param {object} options
9
+ * @param {boolean} options.generate - Generates new ssh credential and stores it in current private keys file storage.
10
+ * @description Import and start ssh server and client based on current default deployment ID.
11
+ */
12
+ callback: async (
13
+ options = {
14
+ generate: false,
15
+ },
16
+ ) => {
17
+ // only import + start
18
+ // node bin/deploy ssh root@<host> <password> import
19
+
20
+ // generate + import + start
21
+ // node bin/deploy ssh root@<host> <password>
22
+
23
+ shellExec(
24
+ `node bin/deploy ssh root@${process.env.DEFAULT_DEPLOY_HOST} ${process.env.DEFAULT_DEPLOY_PASSWORD ?? `''`}${
25
+ options.generate === true ? '' : ' import'
26
+ }`,
27
+ );
28
+ },
29
+ };
30
+ }
31
+
32
+ export default UnderpostSSH;
@@ -15,27 +15,29 @@ import { DefaultParams } from './components/default/CommonDefault.js';
15
15
  import { SocketIo } from './components/core/SocketIo.js';
16
16
  import { SocketIoDefault } from './components/default/SocketIoDefault.js';
17
17
  import { ElementsDefault } from './components/default/ElementsDefault.js';
18
- import { Scroll } from './components/core/Scroll.js';
18
+ import { CssDefaultDark, CssDefaultLight } from './components/default/CssDefault.js';
19
19
 
20
20
  const htmlMainBody = async () => {
21
- return html`<span style="color: black; padding: 5px">Hello World!!</span>`;
21
+ return html`<span>Hello World!!</span>`;
22
22
  };
23
23
 
24
24
  window.onload = () =>
25
25
  Worker.instance({
26
26
  router: RouterDefault,
27
27
  render: async () => {
28
- await Css.loadThemes();
28
+ await Css.loadThemes([CssDefaultLight, CssDefaultDark]);
29
29
  await TranslateCore.Init();
30
30
  await TranslateDefault.Init();
31
31
  await Responsive.Init();
32
32
  await MenuDefault.Render({ htmlMainBody });
33
- await SocketIo.Init({ channels: ElementsDefault.Data });
33
+ await SocketIo.Init({
34
+ channels: ElementsDefault.Data,
35
+ path: `/`,
36
+ });
34
37
  await SocketIoDefault.Init();
35
38
  await LogInDefault();
36
39
  await LogOutDefault();
37
40
  await SignUpDefault();
38
- await Scroll.pullTopRefresh();
39
41
  await Keyboard.Init({ callBackTime: DefaultParams.EVENT_CALLBACK_TIME });
40
42
  },
41
43
  });