underpost 2.8.85 → 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 (52) hide show
  1. package/.env.development +1 -1
  2. package/.env.production +1 -1
  3. package/.env.test +1 -1
  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 -44
  7. package/bin/cyberia0.js +78 -0
  8. package/bin/db.js +1 -3
  9. package/bin/deploy.js +13 -350
  10. package/bin/file.js +11 -1
  11. package/cli.md +39 -19
  12. package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
  13. package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
  14. package/manifests/grafana/deployment.yaml +57 -0
  15. package/manifests/grafana/kustomization.yaml +7 -0
  16. package/manifests/grafana/pvc.yaml +12 -0
  17. package/manifests/grafana/service.yaml +14 -0
  18. package/manifests/maas/ssh-cluster-info.sh +14 -0
  19. package/manifests/prometheus/deployment.yaml +82 -0
  20. package/package.json +1 -2
  21. package/src/api/user/user.service.js +8 -34
  22. package/src/cli/cluster.js +41 -2
  23. package/src/cli/cron.js +12 -45
  24. package/src/cli/db.js +149 -0
  25. package/src/cli/deploy.js +20 -81
  26. package/src/cli/index.js +20 -6
  27. package/src/cli/monitor.js +1 -4
  28. package/src/cli/repository.js +12 -5
  29. package/src/cli/run.js +77 -14
  30. package/src/client/Default.index.js +0 -2
  31. package/src/client/components/core/Account.js +6 -2
  32. package/src/client/components/core/Content.js +11 -7
  33. package/src/client/components/core/Css.js +5 -1
  34. package/src/client/components/core/Input.js +6 -1
  35. package/src/client/components/core/LogIn.js +3 -0
  36. package/src/client/components/core/LogOut.js +1 -1
  37. package/src/client/components/core/Modal.js +7 -4
  38. package/src/client/components/core/Recover.js +5 -2
  39. package/src/client/components/core/Scroll.js +65 -120
  40. package/src/client/components/core/SignUp.js +1 -0
  41. package/src/client/components/core/VanillaJs.js +48 -2
  42. package/src/client/components/default/MenuDefault.js +2 -2
  43. package/src/client/components/default/RoutesDefault.js +3 -3
  44. package/src/index.js +1 -1
  45. package/src/mailer/MailerProvider.js +37 -0
  46. package/src/server/client-build-live.js +1 -1
  47. package/src/server/client-dev-server.js +1 -1
  48. package/src/server/conf.js +2 -272
  49. package/src/server/proxy.js +1 -2
  50. package/src/server/start.js +3 -3
  51. package/docker-compose.yml +0 -67
  52. 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"`, {
@@ -548,6 +474,19 @@ node bin/deploy build-full-client ${deployId}
548
474
  }
549
475
  return { ready: notReadyPods.length === 0, notReadyPods, readyPods };
550
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
+ },
551
490
  };
552
491
  }
553
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
 
@@ -123,6 +124,11 @@ program
123
124
  .option('--full', 'Initializes the cluster with all available statefulsets and services.')
124
125
  .option('--ns-use <ns-name>', 'Switches the current Kubernetes context to the specified namespace.')
125
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
+ )
126
132
  .option('--dev', 'Initializes a development-specific cluster configuration.')
127
133
  .option('--list-pods', 'Displays detailed information about all pods.')
128
134
  .option('--info-capacity', 'Displays the current total machine capacity information.')
@@ -155,7 +161,6 @@ program
155
161
  '--build-manifest',
156
162
  'Builds Kubernetes YAML manifests, including deployments, services, proxies, and secrets.',
157
163
  )
158
- .option('--dashboard-update', 'Updates dashboard instance data with the current router configuration.')
159
164
  .option('--replicas <replicas>', 'Sets a custom number of replicas for deployments.')
160
165
  .option('--versions <deployment-versions>', 'A comma-separated list of custom deployment versions.')
161
166
  .option('--traffic <traffic-versions>', 'A comma-separated list of custom deployment traffic weights.')
@@ -163,10 +168,6 @@ program
163
168
  .option('--info-traffic', 'Retrieves traffic configuration from current resource deployments.')
164
169
  .option('--kubeadm', 'Enables the kubeadm context for deployment operations.')
165
170
  .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
171
  .description('Manages application deployments, defaulting to deploying development pods.')
171
172
  .action(Underpost.deploy.callback);
172
173
 
@@ -240,6 +241,20 @@ program
240
241
  .description('Manages database operations, including import, export, and collection management.')
241
242
  .action(Underpost.db.callback);
242
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
+
243
258
  // 'script' command: Execute scripts
244
259
  program
245
260
  .command('script')
@@ -268,7 +283,6 @@ program
268
283
  .option('--itc', 'Executes cron jobs within the container execution context.')
269
284
  .option('--init', 'Initializes cron jobs for the default deployment ID.')
270
285
  .option('--git', 'Uploads cron job configurations to GitHub.')
271
- .option('--dashboard-update', 'Updates dashboard cron data with the current job configurations.')
272
286
  .description('Manages cron jobs, including initialization, execution, and configuration updates.')
273
287
  .action(Underpost.cron.callback);
274
288
 
@@ -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}`);
@@ -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,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`,
@@ -76,6 +79,11 @@ class UnderpostRun {
76
79
  shellExec(`${baseCommand} deploy --expose mongo`, { async: true });
77
80
  shellExec(`${baseCommand} deploy --expose valkey`, { async: true });
78
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
+ },
79
87
  'cyberia-ide': (path, options = UnderpostRun.DEFAULT_OPTION) => {
80
88
  const baseCommand = options.dev ? 'node bin' : 'underpost';
81
89
  shellExec(`${baseCommand} run ide /home/dd/cyberia-server`);
@@ -93,6 +101,25 @@ class UnderpostRun {
93
101
  shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
94
102
  shellExec(`${baseCommand} push . underpostnet/engine`);
95
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
+ },
96
123
  'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
97
124
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
98
125
  shellCd('/home/dd/engine');
@@ -104,6 +131,16 @@ class UnderpostRun {
104
131
  const { underpostRoot } = options;
105
132
  shellExec(`node ${underpostRoot}/bin/vs ${path}`);
106
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
+ },
107
144
  monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
108
145
  const pid = getTerminalPid();
109
146
  logger.info('monitor pid', pid);
@@ -181,6 +218,34 @@ class UnderpostRun {
181
218
  const { underpostRoot } = options;
182
219
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/.`);
183
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
+
184
249
  cluster: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
185
250
  const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
186
251
  const env = 'production';
@@ -216,25 +281,23 @@ class UnderpostRun {
216
281
  const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
217
282
  shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
218
283
 
219
- let secondsElapsed = 0;
220
- logger.info('Deployment init', { deployId, env, targetTraffic });
284
+ let checkStatusIteration = 0;
285
+ const checkStatusIterationMsDelay = 1000;
286
+ const iteratorTag = `[${deployId}-${env}-${targetTraffic}]`;
287
+ logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay });
221
288
 
222
289
  while (!UnderpostDeploy.API.checkDeploymentReadyStatus(deployId, env, targetTraffic, ignorePods).ready) {
223
- await timer(1000);
224
- secondsElapsed++;
225
- logger.info(`Deployment in progress, seconds elapsed: ${secondsElapsed}`);
290
+ await timer(checkStatusIterationMsDelay);
291
+ checkStatusIteration++;
292
+ logger.info(
293
+ `${iteratorTag} | Deployment in progress... | Delay number check iterations: ${checkStatusIteration}`,
294
+ );
226
295
  }
227
296
 
228
- logger.info(`Deployment ready, seconds elapsed: ${secondsElapsed}`);
297
+ logger.info(`${iteratorTag} | Deployment ready. | Total delay number check iterations: ${checkStatusIteration}`);
229
298
 
230
- UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, targetTraffic);
299
+ UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic);
231
300
 
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
301
  shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${currentTraffic}`);
239
302
  },
240
303
  'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
@@ -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))}
@@ -1026,7 +1026,8 @@ const imageShimmer = () => html`<div
1026
1026
  </div>
1027
1027
  </div>`;
1028
1028
 
1029
- const renderChessPattern = () => `background: repeating-conic-gradient(#808080 0 25%, #0000 0 50%) 50% / 20px 20px`;
1029
+ const renderChessPattern = (patternSize = 20) =>
1030
+ `background: repeating-conic-gradient(#808080 0 25%, #0000 0 50%) 50% / ${patternSize}px ${patternSize}px`;
1030
1031
 
1031
1032
  const extractBackgroundImageUrl = (element) => {
1032
1033
  const style = window.getComputedStyle(element);
@@ -1044,6 +1045,8 @@ const simpleIconsRender = (selector) => {
1044
1045
  });
1045
1046
  };
1046
1047
 
1048
+ const styleFactory = (payload, plain = '') => `style="${renderCssAttr({ style: payload })} ${plain}"`;
1049
+
1047
1050
  export {
1048
1051
  Css,
1049
1052
  Themes,
@@ -1083,4 +1086,5 @@ export {
1083
1086
  lightenHex,
1084
1087
  darkenHex,
1085
1088
  adjustHex,
1089
+ styleFactory,
1086
1090
  };
@@ -359,4 +359,9 @@ const InputFile = {
359
359
  },
360
360
  };
361
361
 
362
- export { Input, InputFile, fileFormDataFactory, getSrcFromFileData, getFileFromFileData };
362
+ function isTextInputFocused() {
363
+ const active = document.activeElement;
364
+ return active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA');
365
+ }
366
+
367
+ export { Input, InputFile, fileFormDataFactory, getSrcFromFileData, getFileFromFileData, isTextInputFocused };
@@ -189,6 +189,9 @@ const LogIn = {
189
189
  </form>
190
190
  `;
191
191
  },
192
+ cleanMainUser: () => {
193
+ LogIn.Scope.user.main.model.user = {};
194
+ },
192
195
  };
193
196
 
194
197
  export { LogIn };
@@ -8,7 +8,7 @@ import { Webhook } from './Webhook.js';
8
8
  const LogOut = {
9
9
  Event: {},
10
10
  Trigger: async function (options) {
11
- LogIn.Scope.user.main.model.user = {};
11
+ LogIn.cleanMainUser();
12
12
  await Webhook.unregister();
13
13
  for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
14
14
  if (s(`.session`))
@@ -28,7 +28,7 @@ import { setDocTitle, closeModalRouteChangeEvent, handleModalViewRoute } from '.
28
28
  import { NotificationManager } from './NotificationManager.js';
29
29
  import { EventsUI } from './EventsUI.js';
30
30
  import { Translate } from './Translate.js';
31
- import { Input } from './Input.js';
31
+ import { Input, isTextInputFocused } from './Input.js';
32
32
  import { Validator } from './Validator.js';
33
33
  import { DropDown } from './DropDown.js';
34
34
  import { Keyboard } from './Keyboard.js';
@@ -186,7 +186,7 @@ const Modal = {
186
186
  `.default-slide-menu-top-bar-fix-title-container`,
187
187
  html`
188
188
  <div class="inl default-slide-menu-top-bar-fix-title-container-text">
189
- ${options.RouterInstance.NameApp}
189
+ ${options.RouterInstance.BannerAppTemplate}
190
190
  </div>
191
191
  `,
192
192
  );
@@ -502,7 +502,7 @@ const Modal = {
502
502
  class="abs modal slide-menu-top-bar-fix"
503
503
  style="height: ${options.heightTopBar}px; top: 0px"
504
504
  >
505
- <a class="a-link-top-banner">
505
+ <a class="a-link-top-banner fl">
506
506
  <div class="inl">${await options.slideMenuTopBarBannerFix()}</div></a
507
507
  >
508
508
  </div>`
@@ -608,6 +608,7 @@ const Modal = {
608
608
  }),
609
609
  );
610
610
  s(`.search-result-btn-${result.routerId}`).onclick = () => {
611
+ if (!s(`.html-${searchBoxHistoryId}`) || !s(`.html-${searchBoxHistoryId}`).hasChildNodes()) return;
611
612
  s(`.html-${searchBoxHistoryId}`).childNodes[currentKeyBoardSearchBoxIndex].classList.remove(
612
613
  `main-btn-menu-active`,
613
614
  );
@@ -621,6 +622,7 @@ const Modal = {
621
622
  };
622
623
 
623
624
  const getResultSearchBox = (validatorData) => {
625
+ if (!s(`.html-${searchBoxHistoryId}`) || !s(`.html-${searchBoxHistoryId}`).hasChildNodes()) return;
624
626
  const { model, id } = validatorData;
625
627
  switch (model) {
626
628
  case 'search-box':
@@ -998,6 +1000,7 @@ const Modal = {
998
1000
  ['Alt', 'k'],
999
1001
  ],
1000
1002
  eventCallBack: () => {
1003
+ if (isTextInputFocused()) return;
1001
1004
  if (s(`.top-bar-search-box`)) {
1002
1005
  if (s(`.main-body-btn-ui-close`).classList.contains('hide')) {
1003
1006
  s(`.main-body-btn-ui-open`).click();
@@ -1157,7 +1160,7 @@ const Modal = {
1157
1160
  if (s(`.slide-menu-top-bar-fix`)) {
1158
1161
  htmls(
1159
1162
  `.slide-menu-top-bar-fix`,
1160
- html`<a class="a-link-top-banner">${await options.slideMenuTopBarBannerFix()}</a>`,
1163
+ html`<a class="a-link-top-banner fl">${await options.slideMenuTopBarBannerFix()}</a>`,
1161
1164
  );
1162
1165
  Modal.setTopBannerLink();
1163
1166
  }
@@ -7,7 +7,7 @@ import { LogIn } from './LogIn.js';
7
7
  import { NotificationManager } from './NotificationManager.js';
8
8
  import { Translate } from './Translate.js';
9
9
  import { Validator } from './Validator.js';
10
- import { getQueryParams, s } from './VanillaJs.js';
10
+ import { getProxyPath, getQueryParams, s } from './VanillaJs.js';
11
11
 
12
12
  const Recover = {
13
13
  Event: {},
@@ -80,7 +80,10 @@ const Recover = {
80
80
  }
81
81
  switch (mode) {
82
82
  case 'recover-verify-email': {
83
- const result = await UserService.post({ id: 'recover-verify-email', body });
83
+ const result = await UserService.post({
84
+ id: 'recover-verify-email',
85
+ body: { ...body, proxyPath: getProxyPath(), hostname: `${location.hostname}` },
86
+ });
84
87
  NotificationManager.Push({
85
88
  html:
86
89
  result.status === 'error' ? result.message : Translate.Render(`${result.status}-recover-verify-email`),