underpost 2.8.84 → 2.8.85

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 (104) 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} +1 -1
  8. package/.github/workflows/{pwa-microservices-template.test.yml → pwa-microservices-template-test.ci.yml} +1 -1
  9. package/.vscode/settings.json +0 -1
  10. package/README.md +45 -2
  11. package/bin/build.js +15 -5
  12. package/bin/deploy.js +17 -82
  13. package/bin/file.js +15 -8
  14. package/cli.md +65 -44
  15. package/conf.js +1 -1
  16. package/docker-compose.yml +1 -1
  17. package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
  18. package/manifests/maas/gpu-diag.sh +1 -1
  19. package/package.json +4 -6
  20. package/src/api/user/user.router.js +24 -1
  21. package/src/api/user/user.service.js +1 -4
  22. package/src/cli/cluster.js +42 -27
  23. package/src/cli/deploy.js +20 -0
  24. package/src/cli/index.js +9 -0
  25. package/src/cli/monitor.js +8 -12
  26. package/src/cli/run.js +111 -6
  27. package/src/cli/ssh.js +32 -0
  28. package/src/client/Default.index.js +7 -3
  29. package/src/client/components/core/Account.js +1 -1
  30. package/src/client/components/core/Chat.js +1 -1
  31. package/src/client/components/core/CommonJs.js +24 -22
  32. package/src/client/components/core/Content.js +1 -5
  33. package/src/client/components/core/Css.js +258 -18
  34. package/src/client/components/core/CssCore.js +8 -8
  35. package/src/client/components/core/Docs.js +14 -61
  36. package/src/client/components/core/DropDown.js +137 -82
  37. package/src/client/components/core/EventsUI.js +92 -5
  38. package/src/client/components/core/LoadingAnimation.js +8 -15
  39. package/src/client/components/core/Modal.js +597 -136
  40. package/src/client/components/core/NotificationManager.js +2 -2
  41. package/src/client/components/core/ObjectLayerEngine.js +638 -0
  42. package/src/client/components/core/Panel.js +158 -34
  43. package/src/client/components/core/PanelForm.js +12 -3
  44. package/src/client/components/core/Recover.js +1 -1
  45. package/src/client/components/core/Router.js +77 -17
  46. package/src/client/components/core/SocketIo.js +3 -3
  47. package/src/client/components/core/Translate.js +6 -2
  48. package/src/client/components/core/VanillaJs.js +0 -3
  49. package/src/client/components/core/Worker.js +3 -1
  50. package/src/client/components/default/CssDefault.js +17 -3
  51. package/src/client/components/default/MenuDefault.js +264 -45
  52. package/src/client/components/default/RoutesDefault.js +6 -12
  53. package/src/client/public/default/android-chrome-144x144.png +0 -0
  54. package/src/client/public/default/android-chrome-192x192.png +0 -0
  55. package/src/client/public/default/android-chrome-256x256.png +0 -0
  56. package/src/client/public/default/android-chrome-36x36.png +0 -0
  57. package/src/client/public/default/android-chrome-48x48.png +0 -0
  58. package/src/client/public/default/android-chrome-72x72.png +0 -0
  59. package/src/client/public/default/android-chrome-96x96.png +0 -0
  60. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  61. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  62. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  63. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  64. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  65. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  66. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  67. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  68. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  69. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  70. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  71. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  72. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  73. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  74. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  75. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  76. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  77. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  78. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  79. package/src/client/public/default/apple-touch-icon.png +0 -0
  80. package/src/client/public/default/assets/background/dark.jpg +0 -0
  81. package/src/client/public/default/assets/background/dark.svg +557 -0
  82. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  83. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  84. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  85. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  86. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  87. package/src/client/public/default/favicon-16x16.png +0 -0
  88. package/src/client/public/default/favicon-32x32.png +0 -0
  89. package/src/client/public/default/favicon.ico +0 -0
  90. package/src/client/public/default/mstile-144x144.png +0 -0
  91. package/src/client/public/default/mstile-150x150.png +0 -0
  92. package/src/client/public/default/mstile-310x150.png +0 -0
  93. package/src/client/public/default/mstile-310x310.png +0 -0
  94. package/src/client/public/default/mstile-70x70.png +0 -0
  95. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  96. package/src/client/ssr/body/DefaultSplashScreen.js +2 -2
  97. package/src/index.js +9 -1
  98. package/src/monitor.js +24 -0
  99. package/src/runtime/lampp/Dockerfile +30 -39
  100. package/src/runtime/lampp/Lampp.js +11 -2
  101. package/src/server/client-build-docs.js +205 -0
  102. package/src/server/client-build.js +16 -166
  103. package/src/server/conf.js +12 -5
  104. package/src/server/valkey.js +102 -41
@@ -1,8 +1,9 @@
1
- import { authMiddleware } from '../../server/auth.js';
1
+ import { authMiddleware, hashPassword } from '../../server/auth.js';
2
2
  import fs from 'fs-extra';
3
3
  import { loggerFactory } from '../../server/logger.js';
4
4
  import { UserController } from './user.controller.js';
5
5
  import express from 'express';
6
+ import { DataBaseProvider } from '../../db/DataBaseProvider.js';
6
7
 
7
8
  const logger = loggerFactory(import.meta);
8
9
 
@@ -10,6 +11,28 @@ const UserRouter = (options) => {
10
11
  const router = express.Router();
11
12
 
12
13
  (async () => {
14
+ const models = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models;
15
+ if (models.User) {
16
+ try {
17
+ const adminUser = await models.User.findOne({ role: 'admin' });
18
+ if (!adminUser) {
19
+ const defaultPassword = process.env.DEFAULT_ADMIN_PASSWORD || 'changethis';
20
+ const hashedPassword = hashPassword(defaultPassword);
21
+
22
+ const result = await models.User.create({
23
+ username: 'admin',
24
+ email: process.env.DEFAULT_ADMIN_EMAIL || 'admin@' + options.host,
25
+ password: hashedPassword,
26
+ role: 'admin',
27
+ emailConfirmed: true,
28
+ publicKey: [],
29
+ });
30
+ logger.warn('Default admin user created. Please change the default password immediately!', result._doc);
31
+ }
32
+ } catch (error) {
33
+ logger.error('Error checking/creating admin user:', error);
34
+ }
35
+ }
13
36
  options.png = {
14
37
  buffer: {
15
38
  'invalid-token': fs.readFileSync(`./src/client/public/default/assets/mailer/api-user-invalid-token.png`),
@@ -444,7 +444,7 @@ const UserService = {
444
444
  _id: user._id,
445
445
  }).select(UserDto.select.get());
446
446
  } else throw new Error('invalid token');
447
- }
447
+ } else delete req.body.password;
448
448
 
449
449
  switch (req.params.id) {
450
450
  default: {
@@ -453,9 +453,6 @@ const UserService = {
453
453
  });
454
454
  switch (user.role) {
455
455
  case 'admin': {
456
- if (req.body.password !== undefined && req.body.password !== user.password)
457
- req.body.password = await hashPassword(req.body.password);
458
- else delete req.body.password;
459
456
  return await User.findByIdAndUpdate(req.params.id, req.body, {
460
457
  runValidators: true,
461
458
  });
@@ -19,6 +19,7 @@ class UnderpostCluster {
19
19
  * @param {object} [options] - Configuration options for cluster initialization.
20
20
  * @param {boolean} [options.mongodb=false] - Deploy MongoDB.
21
21
  * @param {boolean} [options.mongodb4=false] - Deploy MongoDB 4.4.
22
+ * @param {String} [options.mongoDbHost=''] - Set custom mongo db host
22
23
  * @param {boolean} [options.mariadb=false] - Deploy MariaDB.
23
24
  * @param {boolean} [options.mysql=false] - Deploy MySQL.
24
25
  * @param {boolean} [options.postgresql=false] - Deploy PostgreSQL.
@@ -48,6 +49,7 @@ class UnderpostCluster {
48
49
  options = {
49
50
  mongodb: false,
50
51
  mongodb4: false,
52
+ mongoDbHost: '',
51
53
  mariadb: false,
52
54
  mysql: false,
53
55
  postgresql: false,
@@ -260,17 +262,18 @@ class UnderpostCluster {
260
262
 
261
263
  if (options.full === true || options.valkey === true) {
262
264
  if (options.pullImage === true) {
263
- shellExec(`docker pull valkey/valkey:latest`);
264
- shellExec(`sudo podman pull valkey/valkey:latest`);
265
- if (!options.kubeadm && !options.k3s)
265
+ // shellExec(`sudo podman pull valkey/valkey:latest`);
266
+ if (!options.kubeadm && !options.k3s) {
266
267
  // Only load if not kubeadm/k3s (Kind needs it)
268
+ shellExec(`docker pull valkey/valkey:latest`);
267
269
  shellExec(`sudo kind load docker-image valkey/valkey:latest`);
268
- else if (options.kubeadm || options.k3s)
270
+ } else if (options.kubeadm || options.k3s)
269
271
  // For kubeadm/k3s, ensure it's available for containerd
270
272
  shellExec(`sudo crictl pull valkey/valkey:latest`);
271
273
  }
272
274
  shellExec(`kubectl delete statefulset valkey-service --ignore-not-found`);
273
275
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/valkey`);
276
+ await UnderpostTest.API.statusMonitor('valkey-service', 'Running', 'pods', 1000, 60);
274
277
  }
275
278
  if (options.full === true || options.mariadb === true) {
276
279
  shellExec(
@@ -279,18 +282,16 @@ class UnderpostCluster {
279
282
  shellExec(`kubectl delete statefulset mariadb-statefulset --ignore-not-found`);
280
283
 
281
284
  if (options.pullImage === true) {
282
- shellExec(`docker pull mariadb:latest`);
283
- shellExec(`sudo podman pull mariadb:latest`);
284
- if (!options.kubeadm && !options.k3s)
285
+ // shellExec(`sudo podman pull mariadb:latest`);
286
+ if (!options.kubeadm && !options.k3s) {
285
287
  // Only load if not kubeadm/k3s (Kind needs it)
288
+ shellExec(`docker pull mariadb:latest`);
286
289
  shellExec(`sudo kind load docker-image mariadb:latest`);
287
- else if (options.kubeadm || options.k3s)
290
+ } else if (options.kubeadm || options.k3s)
288
291
  // For kubeadm/k3s, ensure it's available for containerd
289
292
  shellExec(`sudo crictl pull mariadb:latest`);
290
293
  }
291
- if (options.kubeadm === true)
292
- // This storage class is specific to kubeadm setup
293
- shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml`);
294
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml`);
294
295
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb`);
295
296
  }
296
297
  if (options.full === true || options.mysql === true) {
@@ -304,11 +305,11 @@ class UnderpostCluster {
304
305
  }
305
306
  if (options.full === true || options.postgresql === true) {
306
307
  if (options.pullImage === true) {
307
- shellExec(`docker pull postgres:latest`);
308
- if (!options.kubeadm && !options.k3s)
308
+ if (!options.kubeadm && !options.k3s) {
309
309
  // Only load if not kubeadm/k3s (Kind needs it)
310
+ shellExec(`docker pull postgres:latest`);
310
311
  shellExec(`sudo kind load docker-image postgres:latest`);
311
- else if (options.kubeadm || options.k3s)
312
+ } else if (options.kubeadm || options.k3s)
312
313
  // For kubeadm/k3s, ensure it's available for containerd
313
314
  shellExec(`sudo crictl pull postgres:latest`);
314
315
  }
@@ -319,11 +320,11 @@ class UnderpostCluster {
319
320
  }
320
321
  if (options.mongodb4 === true) {
321
322
  if (options.pullImage === true) {
322
- shellExec(`docker pull mongo:4.4`);
323
- if (!options.kubeadm && !options.k3s)
323
+ if (!options.kubeadm && !options.k3s) {
324
324
  // Only load if not kubeadm/k3s (Kind needs it)
325
+ shellExec(`docker pull mongo:4.4`);
325
326
  shellExec(`sudo kind load docker-image mongo:4.4`);
326
- else if (options.kubeadm || options.k3s)
327
+ } else if (options.kubeadm || options.k3s)
327
328
  // For kubeadm/k3s, ensure it's available for containerd
328
329
  shellExec(`sudo crictl pull mongo:4.4`);
329
330
  }
@@ -334,9 +335,10 @@ class UnderpostCluster {
334
335
  const successInstance = await UnderpostTest.API.statusMonitor(deploymentName);
335
336
 
336
337
  if (successInstance) {
338
+ if (!options.mongoDbHost) options.mongoDbHost = 'mongodb-service';
337
339
  const mongoConfig = {
338
340
  _id: 'rs0',
339
- members: [{ _id: 0, host: 'mongodb-service:27017' }],
341
+ members: [{ _id: 0, host: `${options.mongoDbHost}:27017` }],
340
342
  };
341
343
 
342
344
  const [pod] = UnderpostDeploy.API.get(deploymentName);
@@ -348,11 +350,11 @@ class UnderpostCluster {
348
350
  }
349
351
  } else if (options.full === true || options.mongodb === true) {
350
352
  if (options.pullImage === true) {
351
- shellExec(`docker pull mongo:latest`);
352
- if (!options.kubeadm && !options.k3s)
353
+ if (!options.kubeadm && !options.k3s) {
353
354
  // Only load if not kubeadm/k3s (Kind needs it)
355
+ shellExec(`docker pull mongo:latest`);
354
356
  shellExec(`sudo kind load docker-image mongo:latest`);
355
- else if (options.kubeadm || options.k3s)
357
+ } else if (options.kubeadm || options.k3s)
356
358
  // For kubeadm/k3s, ensure it's available for containerd
357
359
  shellExec(`sudo crictl pull mongo:latest`);
358
360
  }
@@ -363,19 +365,26 @@ class UnderpostCluster {
363
365
  `sudo kubectl create secret generic mongodb-secret --from-file=username=/home/dd/engine/engine-private/mongodb-username --from-file=password=/home/dd/engine/engine-private/mongodb-password --dry-run=client -o yaml | kubectl apply -f -`,
364
366
  );
365
367
  shellExec(`kubectl delete statefulset mongodb --ignore-not-found`);
366
- if (options.kubeadm === true)
367
- // This storage class is specific to kubeadm setup
368
- shellExec(`kubectl apply -f ${underpostRoot}/manifests/mongodb/storage-class.yaml`);
368
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/mongodb/storage-class.yaml`);
369
369
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb`);
370
370
 
371
- const successInstance = await UnderpostTest.API.statusMonitor('mongodb-1');
371
+ const successInstance = await UnderpostTest.API.statusMonitor('mongodb-0', 'Running', 'pods', 1000, 60 * 10);
372
372
 
373
373
  if (successInstance) {
374
+ if (!options.mongoDbHost) options.mongoDbHost = 'mongodb-service';
374
375
  const mongoConfig = {
375
376
  _id: 'rs0',
376
377
  members: [
377
- { _id: 0, host: 'mongodb-0.mongodb-service:27017', priority: 1 },
378
- { _id: 1, host: 'mongodb-1.mongodb-service:27017', priority: 1 },
378
+ {
379
+ _id: 0,
380
+ host: `${options.mongoDbHost === 'mongodb-service' ? 'mongodb-0.' : ''}${options.mongoDbHost}:27017`,
381
+ priority: 1,
382
+ },
383
+ // {
384
+ // _id: 1,
385
+ // host: `${options.mongoDbHost === 'mongodb-service' ? 'mongodb-1.' : ''}${options.mongoDbHost}:27017`,
386
+ // priority: 1,
387
+ // },
379
388
  ],
380
389
  };
381
390
 
@@ -467,6 +476,12 @@ net.bridge.bridge-nf-call-arptables = 1
467
476
  net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`,
468
477
  { silent: true },
469
478
  );
479
+
480
+ // Increase inotify limits
481
+ shellExec(`sudo sysctl -w fs.inotify.max_user_watches=2099999999`);
482
+ shellExec(`sudo sysctl -w fs.inotify.max_user_instances=2099999999`);
483
+ shellExec(`sudo sysctl -w fs.inotify.max_queued_events=2099999999`);
484
+
470
485
  // shellExec(`sudo sysctl --system`); // Apply sysctl changes immediately
471
486
  // Apply NAT iptables rules.
472
487
  shellExec(`${underpostRoot}/manifests/maas/nat-iptables.sh`, { silent: true });
package/src/cli/deploy.js CHANGED
@@ -528,6 +528,26 @@ node bin/deploy build-full-client ${deployId}
528
528
  }).trim(),
529
529
  );
530
530
  },
531
+ checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = []) {
532
+ const cmd = `underpost config get container-status`;
533
+ const pods = UnderpostDeploy.API.get(`${deployId}-${env}-${traffic}`);
534
+ const readyPods = [];
535
+ const notReadyPods = [];
536
+ for (const pod of pods) {
537
+ const { NAME } = pod;
538
+ if (ignoresNames && ignoresNames.find((t) => NAME.trim().toLowerCase().match(t.trim().toLowerCase()))) continue;
539
+ if (
540
+ shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true }).match(
541
+ `${deployId}-${env}-running-deployment`,
542
+ )
543
+ ) {
544
+ readyPods.push(pod);
545
+ } else {
546
+ notReadyPods.push(pod);
547
+ }
548
+ }
549
+ return { ready: notReadyPods.length === 0, notReadyPods, readyPods };
550
+ },
531
551
  };
532
552
  }
533
553
 
package/src/cli/index.js CHANGED
@@ -112,6 +112,7 @@ program
112
112
  .option('--mariadb', 'Initializes the cluster with a MariaDB statefulset.')
113
113
  .option('--mysql', 'Initializes the cluster with a MySQL statefulset.')
114
114
  .option('--mongodb', 'Initializes the cluster with a MongoDB statefulset.')
115
+ .option('--mongo-db-host <host>', 'Set custom mongo db host')
115
116
  .option('--postgresql', 'Initializes the cluster with a PostgreSQL statefulset.')
116
117
  .option('--mongodb4', 'Initializes the cluster with a MongoDB 4.4 service.')
117
118
  .option('--valkey', 'Initializes the cluster with a Valkey service.')
@@ -315,6 +316,13 @@ program
315
316
  .description('Manages health server monitoring for specified deployments.')
316
317
  .action(Underpost.monitor.callback);
317
318
 
319
+ // 'ssh' command: SSH management
320
+ program
321
+ .command('ssh')
322
+ .option('--generate', 'Generates new ssh credential and stores it in current private keys file storage.')
323
+ .description('Import and start ssh server and client based on current default deployment ID.')
324
+ .action(Underpost.ssh.callback);
325
+
318
326
  // 'run' command: Run a script
319
327
  program
320
328
  .command('run')
@@ -326,6 +334,7 @@ program
326
334
  .option('--pod-name <pod-name>', 'Optional: Specifies the pod name for test execution.')
327
335
  .option('--volume-host-path <volume-host-path>', 'Optional: Specifies the volume host path for test execution.')
328
336
  .option('--volume-mount-path <volume-mount-path>', 'Optional: Specifies the volume mount path for test execution.')
337
+ .option('--volume-type <volume-type>', 'Optional: Specifies the volume type for test execution.')
329
338
  .option('--image-name <image-name>', 'Optional: Specifies the image name for test execution.')
330
339
  .option('--container-name <container-name>', 'Optional: Specifies the container name for test execution.')
331
340
  .option('--namespace <namespace>', 'Optional: Specifies the namespace for test execution.')
@@ -173,19 +173,15 @@ class UnderpostMonitor {
173
173
  monitorTrafficName = undefined;
174
174
  monitorPodName = undefined;
175
175
  }
176
- const cmd = `underpost config get container-status`;
177
176
  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
- }
177
+ const { ready, notReadyPods, readyPods } = UnderpostDeploy.API.checkDeploymentReadyStatus(
178
+ deployId,
179
+ env,
180
+ traffic,
181
+ );
182
+ if (ready) {
183
+ monitorPodName = readyPods[0].NAME;
184
+ monitorTrafficName = `${traffic}`;
189
185
  }
190
186
  };
191
187
  if (!monitorPodName) {
package/src/cli/run.js CHANGED
@@ -6,6 +6,7 @@ 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
 
@@ -64,6 +65,41 @@ class UnderpostRun {
64
65
  shellExec(`kubectl delete pod tf-gpu-test-pod`);
65
66
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/deployment/tensorflow/tf-gpu-test.yaml`);
66
67
  },
68
+ 'dev-cluster': (path, options = UnderpostRun.DEFAULT_OPTION) => {
69
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
70
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
71
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
72
+ shellExec(
73
+ `${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${'127.0.0.1'} --pull-image`,
74
+ );
75
+ shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
76
+ shellExec(`${baseCommand} deploy --expose mongo`, { async: true });
77
+ shellExec(`${baseCommand} deploy --expose valkey`, { async: true });
78
+ },
79
+ 'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
80
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
81
+ shellExec(`${baseCommand} run ide /home/dd/cyberia-server`);
82
+ shellExec(`${baseCommand} run ide /home/dd/cyberia-client`);
83
+ },
84
+ 'engine-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
85
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
86
+ shellExec(`${baseCommand} run ide /home/dd/engine`);
87
+ shellExec(`${baseCommand} run ide /home/dd/engine/engine-private`);
88
+ },
89
+ 'template-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
90
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
91
+ shellCd('/home/dd/engine');
92
+ shellExec(`git reset`);
93
+ shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
94
+ shellExec(`${baseCommand} push . underpostnet/engine`);
95
+ },
96
+ 'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
97
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
98
+ shellCd('/home/dd/engine');
99
+ shellExec(`git reset`);
100
+ shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
101
+ shellExec(`${baseCommand} push . underpostnet/engine`);
102
+ },
67
103
  ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
68
104
  const { underpostRoot } = options;
69
105
  shellExec(`node ${underpostRoot}/bin/vs ${path}`);
@@ -141,6 +177,66 @@ class UnderpostRun {
141
177
  };
142
178
  _monitor();
143
179
  },
180
+ 'db-client': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
181
+ const { underpostRoot } = options;
182
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/.`);
183
+ },
184
+ cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
185
+ const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
186
+ const env = 'production';
187
+ shellCd(`/home/dd/engine`);
188
+ shellExec(`underpost cluster --reset`);
189
+ await timer(5000);
190
+ shellExec(`underpost cluster --kubeadm`);
191
+ await timer(5000);
192
+ shellExec(`underpost dockerfile-pull-base-images --path /home/dd/engine/src/runtime/lampp --kubeadm-load`);
193
+ await timer(5000);
194
+ shellExec(`underpost cluster --kubeadm --pull-image --mongodb`);
195
+ await timer(5000);
196
+ shellExec(`underpost cluster --kubeadm --pull-image --mariadb`);
197
+ await timer(5000);
198
+ for (const deployId of deployList) {
199
+ shellExec(`underpost db ${deployId} --import --git`);
200
+ }
201
+ await timer(5000);
202
+ shellExec(`underpost cluster --kubeadm --pull-image --valkey`);
203
+ await timer(5000);
204
+ shellExec(`underpost cluster --kubeadm --contour`);
205
+ await timer(5000);
206
+ shellExec(`underpost cluster --kubeadm --cert-manager`);
207
+ for (const deployId of deployList) {
208
+ shellExec(`underpost deploy ${deployId} ${env} --kubeadm --cert`);
209
+ }
210
+ },
211
+ deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
212
+ const deployId = path;
213
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
214
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
215
+ const env = 'production';
216
+ const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
217
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
218
+
219
+ let secondsElapsed = 0;
220
+ logger.info('Deployment init', { deployId, env, targetTraffic });
221
+
222
+ while (!UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods).ready) {
223
+ await timer(1000);
224
+ secondsElapsed++;
225
+ logger.info(`Deployment in progress, seconds elapsed: ${secondsElapsed}`);
226
+ }
227
+
228
+ logger.info(`Deployment ready, seconds elapsed: ${secondsElapsed}`);
229
+
230
+ UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
231
+
232
+ shellExec(
233
+ `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${
234
+ options.replicas ? options.replicas : 1
235
+ } ${deployId} ${env}`,
236
+ );
237
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
238
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
239
+ },
144
240
  'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
145
241
  const { underpostRoot } = options;
146
242
  const podName = 'tf-vae-test';
@@ -187,6 +283,12 @@ class UnderpostRun {
187
283
  const volumeHostPath = options.volumeHostPath || path;
188
284
  const enableVolumeMount = volumeHostPath && volumeMountPath;
189
285
 
286
+ if (options.volumeType === 'dev') options.volumeType = 'FileOrCreate';
287
+ const volumeType =
288
+ options.volumeType || (enableVolumeMount && fs.statSync(volumeHostPath).isDirectory()) ? 'Directory' : 'File';
289
+
290
+ const envs = UnderpostRootEnv.API.list();
291
+
190
292
  const cmd = `kubectl apply -f - <<EOF
191
293
  apiVersion: v1
192
294
  kind: Pod
@@ -210,16 +312,19 @@ ${
210
312
  ${args.map((arg) => ` ${arg}`).join('\n')}`
211
313
  : ''
212
314
  }
213
- ${
315
+ ${`${
214
316
  gpuEnable
215
317
  ? ` resources:
216
318
  limits:
217
319
  nvidia.com/gpu: '1'
218
- env:
219
- - name: NVIDIA_VISIBLE_DEVICES
220
- value: all`
320
+ `
221
321
  : ''
222
- }
322
+ } env:
323
+ ${Object.keys(envs)
324
+ .map((key) => ({ key, value: typeof envs[key] === 'number' ? envs[key] : `"${envs[key]}"` }))
325
+ .concat(gpuEnable ? [{ key: 'NVIDIA_VISIBLE_DEVICES', value: 'all' }] : [])
326
+ .map((env) => ` - name: ${env.key}\n value: ${env.value}`)
327
+ .join('\n')}`}
223
328
  ${
224
329
  enableVolumeMount
225
330
  ? `
@@ -230,7 +335,7 @@ ${
230
335
  - name: ${volumeName}
231
336
  hostPath:
232
337
  path: ${volumeHostPath}
233
- type: ${fs.statSync(volumeHostPath).isDirectory() ? 'Directory' : 'File'}`
338
+ type: ${volumeType}`
234
339
  : ''
235
340
  }
236
341
  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;
@@ -16,21 +16,25 @@ 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
18
  import { Scroll } from './components/core/Scroll.js';
19
+ import { CssDefaultDark, CssDefaultLight } from './components/default/CssDefault.js';
19
20
 
20
21
  const htmlMainBody = async () => {
21
- return html`<span style="color: black; padding: 5px">Hello World!!</span>`;
22
+ return html`<span>Hello World!!</span>`;
22
23
  };
23
24
 
24
25
  window.onload = () =>
25
26
  Worker.instance({
26
27
  router: RouterDefault,
27
28
  render: async () => {
28
- await Css.loadThemes();
29
+ await Css.loadThemes([CssDefaultLight, CssDefaultDark]);
29
30
  await TranslateCore.Init();
30
31
  await TranslateDefault.Init();
31
32
  await Responsive.Init();
32
33
  await MenuDefault.Render({ htmlMainBody });
33
- await SocketIo.Init({ channels: ElementsDefault.Data });
34
+ await SocketIo.Init({
35
+ channels: ElementsDefault.Data,
36
+ path: `/`,
37
+ });
34
38
  await SocketIoDefault.Init();
35
39
  await LogInDefault();
36
40
  await LogOutDefault();
@@ -44,7 +44,7 @@ const Account = {
44
44
  { model: 'email', id: `account-email`, rules: [{ type: 'isEmpty' }, { type: 'isEmail' }] },
45
45
  {
46
46
  model: 'password',
47
- defaultValue: '*******',
47
+ defaultValue: '#Changethis123',
48
48
  id: `account-password`,
49
49
  rules: [{ type: 'isStrongPassword' }],
50
50
  },
@@ -53,7 +53,7 @@ const Chat = {
53
53
  html`
54
54
  <div class="in">
55
55
  <span class="chat-message-header">${getIsoDate(new Date())} | ${id}:</span><br />
56
- ${message}
56
+ <span class="chat-message-body"> ${message}</span>
57
57
  </div>
58
58
  `,
59
59
  );
@@ -822,70 +822,72 @@ const generateRandomPasswordSelection = (length) => {
822
822
 
823
823
  const commitData = {
824
824
  feat: {
825
- description: 'A new feature',
825
+ description: 'New feature or enhancement (frontend, backend, API, or UX)',
826
826
  title: 'Features',
827
827
  emoji: '✨',
828
828
  },
829
829
  fix: {
830
- description: 'A bug fix',
830
+ description: 'Fix a bug',
831
831
  title: 'Bug Fixes',
832
832
  emoji: '🐛',
833
833
  },
834
834
  docs: {
835
- description: 'Documentation only changes',
835
+ description: 'Documentation changes',
836
836
  title: 'Documentation',
837
837
  emoji: '📚',
838
838
  },
839
839
  style: {
840
- description:
841
- 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)',
840
+ description: 'Formatting, whitespace, semicolons, code style',
842
841
  title: 'Styles',
843
842
  emoji: '💎',
844
843
  },
845
844
  refactor: {
846
- description: 'A code change that neither fixes a bug nor adds a feature',
845
+ description: 'Code refactor.',
847
846
  title: 'Code Refactoring',
848
847
  emoji: '📦',
849
848
  },
850
849
  perf: {
851
- description: 'A code change that improves performance',
850
+ description: 'Performance improvements across the stack.',
852
851
  title: 'Performance Improvements',
853
852
  emoji: '⚡️',
854
853
  },
854
+ ci: {
855
+ description: 'CI pipeline changes (GitHub Actions, runners, caching)',
856
+ title: 'Continuous Integration',
857
+ emoji: '⚙️',
858
+ },
855
859
  cd: {
856
- description:
857
- 'Changes to our Continuous Delivery configuration files and scripts (example scopes: Jenkins, Spinnaker, ArgoCD)',
860
+ description: 'CD / deployment changes (Remote ssh deployment scripts)',
858
861
  title: 'Continuous Delivery',
859
862
  emoji: '🚀',
860
863
  },
861
- test: {
862
- description: 'Adding missing tests or correcting existing tests',
863
- title: 'Tests',
864
- emoji: '🚨',
864
+ infra: {
865
+ description: 'Infrastructure changes (MAAS, LXD, cloud infra, networking, provisioning).',
866
+ title: 'Infrastructure',
867
+ emoji: '🏗️',
865
868
  },
866
869
  build: {
867
- description: 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)',
870
+ description: 'Build system or dependency changes (tooling, bundler, build scripts).',
868
871
  title: 'Builds',
869
872
  emoji: '🛠',
870
873
  },
871
- ci: {
872
- description:
873
- 'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)',
874
- title: 'Continuous Integrations',
875
- emoji: '⚙️',
874
+ test: {
875
+ description: 'Unit or integration tests added/updated, test helpers, flake fixes.',
876
+ title: 'Tests',
877
+ emoji: '🚨',
876
878
  },
877
879
  chore: {
878
- description: "Other changes that don't modify src or test files",
880
+ description: "Other changes that don't modify src or tests (automation, housekeeping).",
879
881
  title: 'Chores',
880
882
  emoji: '♻️',
881
883
  },
882
884
  revert: {
883
- description: 'Reverts a previous commit',
885
+ description: 'Revert a previous commit or change.',
884
886
  title: 'Reverts',
885
887
  emoji: '🗑',
886
888
  },
887
889
  backup: {
888
- description: 'Changes related to backups, including creation, restoration, and maintenance.',
890
+ description: 'Backups, snapshotting, restore scripts, or backup docs.',
889
891
  title: 'Backups',
890
892
  emoji: '💾',
891
893
  },