underpost 2.8.85 → 2.8.87

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 (70) hide show
  1. package/.env.development +7 -2
  2. package/.env.production +7 -2
  3. package/.env.test +7 -2
  4. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
  5. package/.github/workflows/release.cd.yml +37 -0
  6. package/README.md +7 -24
  7. package/bin/build.js +1 -0
  8. package/bin/db.js +1 -3
  9. package/bin/deploy.js +43 -368
  10. package/bin/file.js +16 -3
  11. package/bin/util.js +1 -56
  12. package/cli.md +46 -21
  13. package/conf.js +3 -3
  14. package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
  15. package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
  16. package/manifests/deployment/mongo-express/deployment.yaml +12 -12
  17. package/manifests/grafana/deployment.yaml +57 -0
  18. package/manifests/grafana/kustomization.yaml +7 -0
  19. package/manifests/grafana/pvc.yaml +12 -0
  20. package/manifests/grafana/service.yaml +14 -0
  21. package/manifests/maas/nvim.sh +91 -0
  22. package/manifests/maas/ssh-cluster-info.sh +14 -0
  23. package/manifests/prometheus/deployment.yaml +82 -0
  24. package/package.json +3 -12
  25. package/src/api/file/file.service.js +28 -8
  26. package/src/api/user/user.router.js +31 -5
  27. package/src/api/user/user.service.js +11 -38
  28. package/src/cli/cluster.js +45 -25
  29. package/src/cli/cron.js +12 -45
  30. package/src/cli/db.js +149 -19
  31. package/src/cli/deploy.js +41 -110
  32. package/src/cli/fs.js +1 -0
  33. package/src/cli/index.js +24 -7
  34. package/src/cli/monitor.js +1 -4
  35. package/src/cli/repository.js +15 -6
  36. package/src/cli/run.js +94 -16
  37. package/src/client/Default.index.js +0 -2
  38. package/src/client/components/core/Account.js +6 -2
  39. package/src/client/components/core/Content.js +11 -7
  40. package/src/client/components/core/Css.js +5 -1
  41. package/src/client/components/core/CssCore.js +12 -0
  42. package/src/client/components/core/FullScreen.js +19 -28
  43. package/src/client/components/core/Input.js +7 -1
  44. package/src/client/components/core/LogIn.js +3 -0
  45. package/src/client/components/core/LogOut.js +1 -1
  46. package/src/client/components/core/Modal.js +32 -43
  47. package/src/client/components/core/ObjectLayerEngine.js +229 -4
  48. package/src/client/components/core/ObjectLayerEngineModal.js +441 -0
  49. package/src/client/components/core/Recover.js +5 -2
  50. package/src/client/components/core/Scroll.js +65 -120
  51. package/src/client/components/core/SignUp.js +1 -0
  52. package/src/client/components/core/ToggleSwitch.js +15 -1
  53. package/src/client/components/core/VanillaJs.js +48 -2
  54. package/src/client/components/default/MenuDefault.js +2 -2
  55. package/src/client/components/default/RoutesDefault.js +3 -3
  56. package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
  57. package/src/index.js +1 -1
  58. package/src/mailer/MailerProvider.js +37 -0
  59. package/src/server/client-build-docs.js +1 -1
  60. package/src/server/client-build-live.js +1 -1
  61. package/src/server/client-build.js +4 -12
  62. package/src/server/client-dev-server.js +1 -1
  63. package/src/server/client-icons.js +6 -78
  64. package/src/server/conf.js +83 -408
  65. package/src/server/proxy.js +2 -3
  66. package/src/server/runtime.js +1 -2
  67. package/src/server/start.js +5 -5
  68. package/test/api.test.js +3 -2
  69. package/docker-compose.yml +0 -67
  70. package/prometheus.yml +0 -36
package/src/cli/deploy.js CHANGED
@@ -22,13 +22,13 @@ const logger = loggerFactory(import.meta);
22
22
  class UnderpostDeploy {
23
23
  static NETWORK = {};
24
24
  static API = {
25
- sync(deployList, { versions, replicas, kubeadm = false }) {
25
+ sync(deployList, { versions, replicas, node }) {
26
26
  const deployGroupId = 'dd.router';
27
27
  fs.writeFileSync(`./engine-private/deploy/${deployGroupId}`, deployList, 'utf8');
28
28
  const totalPods = deployList.split(',').length * versions.split(',').length * parseInt(replicas);
29
29
  const limitFactor = 0.8;
30
30
  const reserveFactor = 0.05;
31
- const resources = UnderpostCluster.API.getResourcesCapacity(kubeadm);
31
+ const resources = UnderpostCluster.API.getResourcesCapacity(node);
32
32
  const memory = parseInt(resources.memory.value / totalPods);
33
33
  const cpu = parseInt(resources.cpu.value / totalPods);
34
34
  UnderpostRootEnv.API.set(
@@ -49,7 +49,7 @@ class UnderpostDeploy {
49
49
  const initEnvObj = dotenv.parse(fs.readFileSync(initEnvPath, 'utf8'));
50
50
  process.env.PORT = initEnvObj.PORT;
51
51
  process.env.NODE_ENV = env;
52
- await Config.build(undefined, 'proxy', deployList);
52
+ await Config.build('proxy', deployList);
53
53
  return buildPortProxyRouter(env === 'development' ? 80 : 443, buildProxyRouter());
54
54
  },
55
55
  deploymentYamlServiceFactory({ deployId, env, port, deploymentVersions }) {
@@ -242,12 +242,12 @@ spec:
242
242
  cert: false,
243
243
  versions: '',
244
244
  traffic: '',
245
- dashboardUpdate: false,
246
245
  replicas: '',
246
+ node: '',
247
247
  restoreHosts: false,
248
248
  disableUpdateDeployment: false,
249
249
  infoTraffic: false,
250
- rebuildClientsBundle: false,
250
+ etcHosts: false,
251
251
  },
252
252
  ) {
253
253
  if (options.infoUtil === true)
@@ -311,31 +311,26 @@ Password: <Your Key>
311
311
  deployId,
312
312
  env,
313
313
  traffic: UnderpostDeploy.API.getCurrentTraffic(deployId),
314
+ router: await UnderpostDeploy.API.routerFactory(deployId, env),
315
+ pods: await UnderpostDeploy.API.get(deployId),
314
316
  });
315
317
  }
316
318
  return;
317
319
  }
318
- if (options.rebuildClientsBundle === true) await UnderpostDeploy.API.rebuildClientsBundle(deployList);
319
320
  if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
320
321
  if (!options.replicas) options.replicas = 1;
321
322
  if (options.sync) UnderpostDeploy.API.sync(deployList, options);
322
323
  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
- );
324
+ if (options.infoRouter === true) {
325
+ logger.info('router', await UnderpostDeploy.API.routerFactory(deployList, env));
326
+ return;
327
+ }
328
+ UnderpostDeploy.API.configMap(env);
330
329
  let renderHosts = '';
331
- let concatHots = '';
332
- const etcHost = (
333
- concat,
334
- ) => `127.0.0.1 ${concat} localhost localhost.localdomain localhost4 localhost4.localdomain4
335
- ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
330
+ let etcHosts = [];
336
331
  if (options.restoreHosts === true) {
337
- renderHosts = etcHost(concatHots);
338
- fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
332
+ const factoryResult = UnderpostDeploy.API.etcHostFactory(etcHosts);
333
+ renderHosts = factoryResult.renderHosts;
339
334
  logger.info(renderHosts);
340
335
  return;
341
336
  }
@@ -364,7 +359,7 @@ Password: <Your Key>
364
359
  for (const host of Object.keys(confServer)) {
365
360
  shellExec(`sudo kubectl delete HTTPProxy ${host}`);
366
361
  if (env === 'production' && options.cert === true) shellExec(`sudo kubectl delete Certificate ${host}`);
367
- if (!options.remove === true && env === 'development') concatHots += ` ${host}`;
362
+ if (!options.remove === true && env === 'development') etcHosts.push(host);
368
363
  }
369
364
 
370
365
  const manifestsPath =
@@ -379,24 +374,9 @@ Password: <Your Key>
379
374
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml`);
380
375
  }
381
376
  }
382
- switch (process.platform) {
383
- case 'linux':
384
- {
385
- switch (env) {
386
- case 'development':
387
- renderHosts = etcHost(concatHots);
388
- fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
389
-
390
- break;
391
-
392
- default:
393
- break;
394
- }
395
- }
396
- break;
397
-
398
- default:
399
- break;
377
+ if (options.etcHosts === true) {
378
+ const factoryResult = UnderpostDeploy.API.etcHostFactory(etcHosts);
379
+ renderHosts = factoryResult.renderHosts;
400
380
  }
401
381
  if (renderHosts)
402
382
  logger.info(
@@ -435,23 +415,6 @@ Password: <Your Key>
435
415
 
436
416
  return result;
437
417
  },
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
418
  resourcesFactory() {
456
419
  return {
457
420
  requests: {
@@ -465,60 +428,6 @@ node bin/deploy build-full-client ${deployId}
465
428
  totalPods: UnderpostRootEnv.API.get('total-pods'),
466
429
  };
467
430
  },
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
431
  existsContainerFile({ podName, path }) {
523
432
  return JSON.parse(
524
433
  shellExec(`kubectl exec ${podName} -- test -f ${path} && echo "true" || echo "false"`, {
@@ -548,6 +457,28 @@ node bin/deploy build-full-client ${deployId}
548
457
  }
549
458
  return { ready: notReadyPods.length === 0, notReadyPods, readyPods };
550
459
  },
460
+ configMap(env) {
461
+ shellExec(`kubectl delete configmap underpost-config`);
462
+ shellExec(
463
+ `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
464
+ );
465
+ },
466
+ switchTraffic(deployId, env, targetTraffic, replicas = 1) {
467
+ UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
468
+ shellExec(
469
+ `node bin deploy --info-router --build-manifest --traffic ${targetTraffic} --replicas ${replicas} ${deployId} ${env}`,
470
+ );
471
+ shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
472
+ },
473
+ etcHostFactory(hosts = []) {
474
+ const renderHosts = `127.0.0.1 ${hosts.join(
475
+ ' ',
476
+ )} localhost localhost.localdomain localhost4 localhost4.localdomain4
477
+ ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
478
+
479
+ fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
480
+ return { renderHosts };
481
+ },
551
482
  };
552
483
  }
553
484
 
package/src/cli/fs.js CHANGED
@@ -61,6 +61,7 @@ class UnderpostFileStorage {
61
61
  await UnderpostFileStorage.API.pull(_path, options);
62
62
  } else logger.warn(`Pull path already exists`, _path);
63
63
  }
64
+ shellExec(`cd ${path} && git init && git add . && git commit -m "Base pull state"`);
64
65
  } else {
65
66
  const files =
66
67
  options.git === true
package/src/cli/index.js CHANGED
@@ -22,7 +22,9 @@ program.name('underpost').description(`underpost ci/cd cli ${Underpost.version}`
22
22
  // 'new' command: Create a new project
23
23
  program
24
24
  .command('new')
25
- .argument('<app-name>', 'The name of the application to create.')
25
+ .argument('<app-name>', 'The name or deploy-id of the application to create.')
26
+ .option('--deploy-id', 'Crete deploy ID conf env files')
27
+ .option('--dev', 'Sets the development cli context')
26
28
  .description('Initializes a new Underpost project with a predefined structure.')
27
29
  .action(Underpost.repo.new);
28
30
 
@@ -123,6 +125,11 @@ program
123
125
  .option('--full', 'Initializes the cluster with all available statefulsets and services.')
124
126
  .option('--ns-use <ns-name>', 'Switches the current Kubernetes context to the specified namespace.')
125
127
  .option('--kubeadm', 'Initializes the cluster using kubeadm for control plane management.')
128
+ .option('--grafana', 'Initializes the cluster with a Grafana deployment.')
129
+ .option(
130
+ '--prom [hosts]',
131
+ 'Initializes the cluster with a Prometheus Operator deployment and monitor scrap for specified hosts.',
132
+ )
126
133
  .option('--dev', 'Initializes a development-specific cluster configuration.')
127
134
  .option('--list-pods', 'Displays detailed information about all pods.')
128
135
  .option('--info-capacity', 'Displays the current total machine capacity information.')
@@ -151,22 +158,19 @@ program
151
158
  .option('--expose', 'Exposes services matching the provided deployment ID list.')
152
159
  .option('--info-util', 'Displays useful `kubectl` utility management commands.')
153
160
  .option('--cert', 'Resets TLS/SSL certificate secrets for deployments.')
161
+ .option('--node <node>', 'Sets optional node for deployment operations.')
154
162
  .option(
155
163
  '--build-manifest',
156
164
  'Builds Kubernetes YAML manifests, including deployments, services, proxies, and secrets.',
157
165
  )
158
- .option('--dashboard-update', 'Updates dashboard instance data with the current router configuration.')
159
166
  .option('--replicas <replicas>', 'Sets a custom number of replicas for deployments.')
160
167
  .option('--versions <deployment-versions>', 'A comma-separated list of custom deployment versions.')
161
168
  .option('--traffic <traffic-versions>', 'A comma-separated list of custom deployment traffic weights.')
162
169
  .option('--disable-update-deployment', 'Disables updates to deployments.')
163
170
  .option('--info-traffic', 'Retrieves traffic configuration from current resource deployments.')
164
171
  .option('--kubeadm', 'Enables the kubeadm context for deployment operations.')
172
+ .option('--etc-hosts', 'Enables the etc-hosts context for deployment operations.')
165
173
  .option('--restore-hosts', 'Restores default `/etc/hosts` entries.')
166
- .option(
167
- '--rebuild-clients-bundle',
168
- 'Inside the container, rebuilds client bundles (only static public or storage client files).',
169
- )
170
174
  .description('Manages application deployments, defaulting to deploying development pods.')
171
175
  .action(Underpost.deploy.callback);
172
176
 
@@ -240,6 +244,20 @@ program
240
244
  .description('Manages database operations, including import, export, and collection management.')
241
245
  .action(Underpost.db.callback);
242
246
 
247
+ program
248
+ .command('metadata')
249
+ .argument('[deploy-id]', 'The deployment ID to manage metadata.')
250
+ .argument('[host]', 'The host to manage metadata.')
251
+ .argument('[path]', 'The path to manage metadata.')
252
+ .option('--import', 'Imports from local storage.')
253
+ .option('--export', 'Exports to local storage.')
254
+ .option('--crons', 'Apply to cron data collection')
255
+ .option('--instances', 'Apply to instance data collection')
256
+ .option('--generate', 'Generate cluster metadata')
257
+ .option('--itc', 'Apply under container execution context')
258
+ .description('Manages cluster metadata operations, including import and export.')
259
+ .action(Underpost.db.clusterMetadataBackupCallback);
260
+
243
261
  // 'script' command: Execute scripts
244
262
  program
245
263
  .command('script')
@@ -268,7 +286,6 @@ program
268
286
  .option('--itc', 'Executes cron jobs within the container execution context.')
269
287
  .option('--init', 'Initializes cron jobs for the default deployment ID.')
270
288
  .option('--git', 'Uploads cron job configurations to GitHub.')
271
- .option('--dashboard-update', 'Updates dashboard cron data with the current job configurations.')
272
289
  .description('Manages cron jobs, including initialization, execution, and configuration updates.')
273
290
  .action(Underpost.cron.callback);
274
291
 
@@ -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}`);
@@ -5,6 +5,7 @@ import { actionInitLog, loggerFactory } from '../server/logger.js';
5
5
  import fs from 'fs-extra';
6
6
  import { getNpmRootPath } from '../server/conf.js';
7
7
  import UnderpostStartUp from '../server/start.js';
8
+ import { Config } from '../server/conf.js';
8
9
 
9
10
  dotenv.config();
10
11
 
@@ -80,24 +81,32 @@ class UnderpostRepository {
80
81
  );
81
82
  },
82
83
 
83
- new(repositoryName) {
84
+ new(repositoryName, options = { dev: false, deployId: false }) {
84
85
  return new Promise(async (resolve, reject) => {
85
86
  try {
86
87
  await logger.setUpInfo();
88
+ actionInitLog();
87
89
  if (repositoryName === 'service')
88
90
  return resolve(
89
91
  await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':'),
90
92
  );
91
- else actionInitLog();
92
- const exeRootPath = `${getNpmRootPath()}/underpost`;
93
+ if (options.deployId === true) return Config.deployIdFactory(repositoryName);
94
+ const npmRoot = getNpmRootPath();
95
+ const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
93
96
  const destFolder = `./${repositoryName}`;
94
97
  logger.info('Note: This process may take several minutes to complete');
95
98
  logger.info('build app', { destFolder });
96
99
  if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
97
100
  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"`);
101
+ if (!options.dev) {
102
+ fs.copySync(underpostRoot, destFolder);
103
+ fs.writeFileSync(
104
+ `${destFolder}/.gitignore`,
105
+ fs.readFileSync(`${underpostRoot}/.dockerignore`, 'utf8'),
106
+ 'utf8',
107
+ );
108
+ shellExec(`cd ${destFolder} && git init && git add . && git commit -m "Base template implementation"`);
109
+ }
101
110
  shellExec(`cd ${destFolder} && npm run build`);
102
111
  shellExec(`cd ${destFolder} && npm run dev`);
103
112
  return resolve();
package/src/cli/run.js CHANGED
@@ -1,7 +1,7 @@
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';
@@ -54,6 +54,9 @@ class UnderpostRun {
54
54
  }`,
55
55
  );
56
56
  },
57
+ 'underpost-config': (path, options = UnderpostRun.DEFAULT_OPTION) => {
58
+ UnderpostDeploy.API.configMap(path ?? 'production');
59
+ },
57
60
  'gpu-env': (path, options = UnderpostRun.DEFAULT_OPTION) => {
58
61
  shellExec(
59
62
  `node bin cluster --dev --reset && node bin cluster --dev --dedicated-gpu --kubeadm && kubectl get pods --all-namespaces -o wide -w`,
@@ -69,12 +72,24 @@ class UnderpostRun {
69
72
  const baseCommand = options.dev ? 'node bin' : 'underpost';
70
73
  shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --reset`);
71
74
  shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''}`);
75
+ const mongoHosts = ['mongodb-0.mongodb-service'];
72
76
  shellExec(
73
- `${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${'127.0.0.1'} --pull-image`,
77
+ `${baseCommand} cluster${options.dev ? ' --dev' : ''} --mongodb --mongo-db-host ${mongoHosts.join(
78
+ ',',
79
+ )} --pull-image`,
74
80
  );
75
81
  shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
76
82
  shellExec(`${baseCommand} deploy --expose mongo`, { async: true });
77
83
  shellExec(`${baseCommand} deploy --expose valkey`, { async: true });
84
+ {
85
+ const hostListenResult = UnderpostDeploy.API.etcHostFactory(mongoHosts);
86
+ logger.info(hostListenResult.renderHosts);
87
+ }
88
+ },
89
+ 'ssh-cluster-info': (path, options = UnderpostRun.DEFAULT_OPTION) => {
90
+ const { underpostRoot } = options;
91
+ shellExec(`chmod +x ${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
92
+ shellExec(`${underpostRoot}/manifests/maas/ssh-cluster-info.sh`);
78
93
  },
79
94
  'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
80
95
  const baseCommand = options.dev ? 'node bin' : 'underpost';
@@ -93,6 +108,25 @@ class UnderpostRun {
93
108
  shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
94
109
  shellExec(`${baseCommand} push . underpostnet/engine`);
95
110
  },
111
+ clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
112
+ shellCd(path ?? `/home/dd/engine`);
113
+ shellExec(`node bin/deploy clean-core-repo`);
114
+ },
115
+ pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
116
+ shellCd(`/home/dd/engine`);
117
+ shellExec(`node bin/deploy clean-core-repo`);
118
+ shellExec(`underpost pull . underpostnet/engine`);
119
+ shellExec(`underpost pull engine-private underpostnet/engine-private`, { silent: true });
120
+ },
121
+ 'release-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
122
+ actionInitLog();
123
+ shellExec(`underpost --version`);
124
+ shellCd(`/home/dd/engine`);
125
+ for (const _deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
126
+ const deployId = _deployId.trim();
127
+ shellExec(`underpost run deploy ${deployId}`, { async: true });
128
+ }
129
+ },
96
130
  'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
97
131
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
98
132
  shellCd('/home/dd/engine');
@@ -104,6 +138,24 @@ class UnderpostRun {
104
138
  const { underpostRoot } = options;
105
139
  shellExec(`node ${underpostRoot}/bin/vs ${path}`);
106
140
  },
141
+ 'dev-client': (_path, options = UnderpostRun.DEFAULT_OPTION) => {
142
+ let [deployId, hostpath, subConf, lite] = _path.split(',');
143
+ let [host, path] = hostpath.split('/');
144
+ if (!path) path = '/';
145
+ shellExec(`npm run dev-client ${deployId} ${host} ${path} ${subConf} static${lite === 'l' ? ' l' : ''}`);
146
+ },
147
+ 'dev-api': (path, options = UnderpostRun.DEFAULT_OPTION) => {
148
+ let [deployId, subConf] = path.split(',');
149
+ shellExec(`npm run dev-api ${deployId} ${subConf}`);
150
+ },
151
+ 'router-sync': (path, options = UnderpostRun.DEFAULT_OPTION) => {
152
+ const baseCommand = options.dev || true ? 'node bin' : 'underpost';
153
+ const defaultPaht = ['dd', 'kind-control-plane'];
154
+ let [deployId, node] = path ? path.split(',') : defaultPaht;
155
+ deployId = deployId ?? defaultPaht[0];
156
+ node = node ?? defaultPaht[1];
157
+ shellExec(`${baseCommand} deploy --sync --node ${node} --build-manifest --info-router ${deployId} production`);
158
+ },
107
159
  monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
108
160
  const pid = getTerminalPid();
109
161
  logger.info('monitor pid', pid);
@@ -181,6 +233,34 @@ class UnderpostRun {
181
233
  const { underpostRoot } = options;
182
234
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/.`);
183
235
  },
236
+ promote: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
237
+ let [inputDeployId, inputEnv, inputReplicas] = path.split(',');
238
+ if (!inputEnv) inputEnv = 'production';
239
+ if (!inputReplicas) inputReplicas = 1;
240
+ if (inputDeployId === 'dd') {
241
+ for (const deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
242
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
243
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
244
+ UnderpostDeploy.API.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas);
245
+ }
246
+ } else {
247
+ const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(inputDeployId);
248
+ const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
249
+ UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas);
250
+ }
251
+ },
252
+
253
+ metrics: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
254
+ const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
255
+ let hosts = [];
256
+ for (const deployId of deployList) {
257
+ const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
258
+ hosts = hosts.concat(Object.keys(confServer));
259
+ }
260
+ shellExec(`node bin cluster --prom ${hosts.join(',')}`);
261
+ shellExec(`node bin cluster --grafana`);
262
+ },
263
+
184
264
  cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
185
265
  const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
186
266
  const env = 'production';
@@ -216,26 +296,24 @@ class UnderpostRun {
216
296
  const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
217
297
  shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
218
298
 
219
- let secondsElapsed = 0;
220
- logger.info('Deployment init', { deployId, env, targetTraffic });
299
+ let checkStatusIteration = 0;
300
+ const checkStatusIterationMsDelay = 1000;
301
+ const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
302
+ logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
221
303
 
222
304
  while (!UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods).ready) {
223
- await timer(1000);
224
- secondsElapsed++;
225
- logger.info(`Deployment in progress, seconds elapsed: ${secondsElapsed}`);
305
+ await timer(checkStatusIterationMsDelay);
306
+ checkStatusIteration++;
307
+ logger.info(
308
+ `${iteratorTag} | Deployment in progress... | Delay number check iterations: ${checkStatusIteration}`,
309
+ );
226
310
  }
227
311
 
228
- logger.info(`Deployment ready, seconds elapsed: ${secondsElapsed}`);
312
+ logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
229
313
 
230
- UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
314
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
231
315
 
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}`);
316
+ // shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
239
317
  },
240
318
  'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
241
319
  const { underpostRoot } = options;
@@ -15,7 +15,6 @@ 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';
19
18
  import { CssDefaultDark, CssDefaultLight } from './components/default/CssDefault.js';
20
19
 
21
20
  const htmlMainBody = async () => {
@@ -39,7 +38,6 @@ window.onload = () =>
39
38
  await LogInDefault();
40
39
  await LogOutDefault();
41
40
  await SignUpDefault();
42
- await Scroll.pullTopRefresh();
43
41
  await Keyboard.Init({ callBackTime: DefaultParams.EVENT_CALLBACK_TIME });
44
42
  },
45
43
  });
@@ -10,7 +10,7 @@ import { Modal } from './Modal.js';
10
10
  import { NotificationManager } from './NotificationManager.js';
11
11
  import { Translate } from './Translate.js';
12
12
  import { Validator } from './Validator.js';
13
- import { append, htmls, s } from './VanillaJs.js';
13
+ import { append, getProxyPath, htmls, s } from './VanillaJs.js';
14
14
 
15
15
  const Account = {
16
16
  UpdateEvent: {},
@@ -103,7 +103,11 @@ const Account = {
103
103
  e.preventDefault();
104
104
  const result = await UserService.post({
105
105
  id: 'mailer/verify-email',
106
- body: { email: s(`.account-email`).value },
106
+ body: {
107
+ email: s(`.account-email`).value,
108
+ hostname: `${location.hostname}`,
109
+ proxyPath: getProxyPath(),
110
+ },
107
111
  });
108
112
  NotificationManager.Push({
109
113
  html: result.status === 'error' ? result.message : Translate.Render(`email send`),
@@ -7,7 +7,7 @@ import { Modal, renderViewTitle } from './Modal.js';
7
7
  import { DocumentService } from '../../services/document/document.service.js';
8
8
  import { CoreService, getApiBaseUrl } from '../../services/core/core.service.js';
9
9
  import { loggerFactory } from './Logger.js';
10
- import { imageShimmer, renderCssAttr } from './Css.js';
10
+ import { imageShimmer, renderChessPattern, renderCssAttr, styleFactory } from './Css.js';
11
11
 
12
12
  const logger = loggerFactory(import.meta);
13
13
 
@@ -114,7 +114,6 @@ const Content = {
114
114
  width: '100%',
115
115
  border: 'none',
116
116
  };
117
- options.style = `style="${renderCssAttr(options)}"`;
118
117
  if (!options.class) options.class = ``;
119
118
  const { container, file } = options;
120
119
  const ext = file.name.split('.')[file.name.split('.').length - 1];
@@ -126,7 +125,7 @@ const Content = {
126
125
  const content = options.url
127
126
  ? await CoreService.getRaw({ url: options.url })
128
127
  : await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
129
- render += html`<div class="${options.class}" ${options.style}>${marked.parse(content)}</div>`;
128
+ render += html`<div class="${options.class}" ${styleFactory(options.style)}>${marked.parse(content)}</div>`;
130
129
  }
131
130
 
132
131
  break;
@@ -135,13 +134,18 @@ const Content = {
135
134
  case 'jpeg':
136
135
  case 'webp':
137
136
  case 'svg':
137
+ case 'gif':
138
138
  case 'png': {
139
139
  const url = options.url
140
140
  ? options.url
141
141
  : file._id
142
142
  ? getApiBaseUrl({ id: file._id, endpoint: 'file/blob' })
143
143
  : URL.createObjectURL(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
144
- const imgRender = html`<img class="in ${options.class}" ${options.style} src="${url}" />`;
144
+ const imgRender = html`<img
145
+ class="in ${options.class}"
146
+ ${styleFactory(options.style, `${renderChessPattern(50)}`)}
147
+ src="${url}"
148
+ />`;
145
149
  render += imgRender;
146
150
  break;
147
151
  }
@@ -153,14 +157,14 @@ const Content = {
153
157
  : URL.createObjectURL(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
154
158
  render += html`<iframe
155
159
  class="in ${options.class} iframe-${options.idModal}"
156
- ${options.style}
160
+ ${styleFactory(options.style)}
157
161
  src="${url}"
158
162
  ></iframe>`;
159
163
  break;
160
164
  }
161
165
 
162
166
  case 'json':
163
- render += html`<pre class="in ${options.class}" ${options.style}>
167
+ render += html`<pre class="in ${options.class}" ${styleFactory(options.style)}>
164
168
  ${JSON.stringify(
165
169
  JSON.parse(
166
170
  options.url
@@ -174,7 +178,7 @@ const Content = {
174
178
  break;
175
179
 
176
180
  default:
177
- render += html`<div class="in ${options.class}" ${options.style}>
181
+ render += html`<div class="in ${options.class}" ${styleFactory(options.style)}>
178
182
  ${options.url
179
183
  ? await CoreService.getRaw({ url: options.url })
180
184
  : await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype))}