@underpostnet/underpost 2.99.4 → 2.99.6

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 (38) hide show
  1. package/.env.development +0 -3
  2. package/.env.production +1 -3
  3. package/.env.test +0 -3
  4. package/README.md +3 -3
  5. package/baremetal/commission-workflows.json +93 -4
  6. package/bin/deploy.js +56 -45
  7. package/cli.md +45 -28
  8. package/examples/static-page/README.md +101 -357
  9. package/examples/static-page/ssr-components/CustomPage.js +1 -13
  10. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +40 -0
  11. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +40 -0
  12. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  13. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  14. package/package.json +3 -4
  15. package/scripts/disk-devices.sh +13 -0
  16. package/scripts/maas-setup.sh +13 -9
  17. package/scripts/rocky-kickstart.sh +294 -0
  18. package/src/cli/baremetal.js +657 -263
  19. package/src/cli/cloud-init.js +120 -120
  20. package/src/cli/env.js +4 -1
  21. package/src/cli/image.js +4 -37
  22. package/src/cli/index.js +56 -11
  23. package/src/cli/kickstart.js +149 -0
  24. package/src/cli/repository.js +3 -1
  25. package/src/cli/run.js +56 -10
  26. package/src/cli/secrets.js +0 -34
  27. package/src/cli/static.js +23 -23
  28. package/src/client/components/core/Docs.js +22 -3
  29. package/src/index.js +30 -5
  30. package/src/server/backup.js +11 -4
  31. package/src/server/client-build-docs.js +1 -1
  32. package/src/server/conf.js +0 -22
  33. package/src/server/cron.js +339 -130
  34. package/src/server/dns.js +10 -0
  35. package/src/server/logger.js +22 -27
  36. package/src/server/tls.js +14 -14
  37. package/examples/static-page/QUICK-REFERENCE.md +0 -481
  38. package/examples/static-page/STATIC-GENERATOR-GUIDE.md +0 -757
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Kickstart configuration generator for Underpost Engine
3
+ * @module src/cli/kickstart.js
4
+ * @namespace UnderpostKickStart
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { loggerFactory } from '../server/logger.js';
11
+
12
+ const logger = loggerFactory(import.meta);
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ class UnderpostKickStart {
17
+ static API = {
18
+ /**
19
+ * @method kickstartHeader
20
+ * @description Generates the kickstart header section with template variables.
21
+ * @param {object} options
22
+ * @param {string} [options.lang='en_US.UTF-8']
23
+ * @param {string} [options.keyboard='us']
24
+ * @param {string} [options.timezone='America/New_York']
25
+ * @param {string} [options.rootPassword]
26
+ * @memberof UnderpostKickStart
27
+ * @returns {string}
28
+ */
29
+ kickstartHeader: ({ lang = 'en_US.UTF-8', keyboard = 'us', timezone = 'America/New_York', rootPassword = '' }) => {
30
+ return [
31
+ '# Rocky Linux 9 Kickstart - Ephemeral Anaconda Live Environment with SSHD',
32
+ 'cmdline',
33
+ 'eula --agreed',
34
+ `keyboard --vckeymap=${keyboard} --xlayouts='${keyboard}'`,
35
+ `lang ${lang}`,
36
+ 'network --bootproto=dhcp --device=link --activate --onboot=yes',
37
+ `timezone ${timezone} --utc`,
38
+ rootPassword ? `rootpw --plaintext ${rootPassword}` : 'rootpw --lock',
39
+ 'firstboot --disable',
40
+ 'skipx',
41
+ ].join('\n');
42
+ },
43
+
44
+ /**
45
+ * @method kickstartPreVariables
46
+ * @description Generates the variable assignments block for the %pre script.
47
+ * @param {object} options
48
+ * @param {string} [options.rootPassword]
49
+ * @param {string} [options.authorizedKeys]
50
+ * @param {string} [options.adminUsername]
51
+ * @memberof UnderpostKickStart
52
+ * @returns {string}
53
+ */
54
+ kickstartPreVariables: ({ rootPassword = '', authorizedKeys = '', adminUsername = '' }) => {
55
+ const sanitizedKeys = (authorizedKeys || '').trim();
56
+ return [
57
+ `ROOT_PASS='${rootPassword || ''}'`,
58
+ `AUTHORIZED_KEYS='${sanitizedKeys}'`,
59
+ `ADMIN_USER='${adminUsername || process.env.MAAS_ADMIN_USERNAME || 'maas'}'`,
60
+ ].join('\n');
61
+ },
62
+
63
+ /**
64
+ * @method kickstartFactory
65
+ * @description Generates a complete kickstart configuration by combining the header,
66
+ * variable assignments, and the rocky-kickstart.sh script body.
67
+ * @param {object} options
68
+ * @param {string} [options.lang='en_US.UTF-8']
69
+ * @param {string} [options.keyboard='us']
70
+ * @param {string} [options.timezone='America/New_York']
71
+ * @param {string} [options.rootPassword]
72
+ * @param {string} [options.authorizedKeys]
73
+ * @memberof UnderpostKickStart
74
+ * @returns {string}
75
+ */
76
+ kickstartFactory: ({
77
+ lang = 'en_US.UTF-8',
78
+ keyboard = 'us',
79
+ timezone = 'America/New_York',
80
+ rootPassword = process.env.MAAS_ADMIN_PASS,
81
+ authorizedKeys = '',
82
+ }) => {
83
+ const adminUsername = process.env.MAAS_ADMIN_USERNAME || 'maas';
84
+ const header = UnderpostKickStart.API.kickstartHeader({ lang, keyboard, timezone, rootPassword });
85
+ const variables = UnderpostKickStart.API.kickstartPreVariables({ rootPassword, authorizedKeys, adminUsername });
86
+
87
+ const scriptPath = path.resolve(__dirname, '../../scripts/rocky-kickstart.sh');
88
+ const scriptBody = fs.readFileSync(scriptPath, 'utf8');
89
+
90
+ return [
91
+ header,
92
+ '',
93
+ '%pre --interpreter=/bin/bash --log=/tmp/ks-pre.log --erroronfail',
94
+ '#!/bin/bash',
95
+ variables,
96
+ '',
97
+ scriptBody,
98
+ '%end',
99
+ ].join('\n');
100
+ },
101
+
102
+ /**
103
+ * @method kernelParamsFactory
104
+ * @description Appends kickstart-specific kernel parameters (inst.ks, inst.repo, inst.text, inst.sshd).
105
+ * @param {string} macAddress - The MAC address of the target machine.
106
+ * @param {array} cmd - The existing array of kernel parameters.
107
+ * @param {object} options - Options for generating kernel parameters.
108
+ * @param {string} options.ipDhcpServer - The IP address of the DHCP server.
109
+ * @param {number} [options.bootstrapHttpServerPort=8888] - Port for the bootstrap HTTP server.
110
+ * @param {string} options.hostname - The hostname of the target machine.
111
+ * @param {string} [options.architecture='amd64'] - The target architecture.
112
+ * @memberof UnderpostKickStart
113
+ * @returns {array}
114
+ */
115
+ kernelParamsFactory(
116
+ macAddress,
117
+ cmd = [],
118
+ options = { ipDhcpServer: '', bootstrapHttpServerPort: 8888, hostname: '', architecture: 'amd64' },
119
+ ) {
120
+ const { ipDhcpServer, bootstrapHttpServerPort, hostname, architecture } = options;
121
+ const repoArch = architecture && architecture.match('arm64') ? 'aarch64' : 'x86_64';
122
+ return cmd.concat([
123
+ `inst.ks=http://${ipDhcpServer}:${bootstrapHttpServerPort}/${hostname}/ks.cfg`,
124
+ `inst.repo=http://dl.rockylinux.org/pub/rocky/9/BaseOS/${repoArch}/os/`,
125
+ `inst.text`,
126
+ `inst.sshd`,
127
+ ]);
128
+ },
129
+
130
+ /**
131
+ * @method httpServerStaticFactory
132
+ * @description Writes kickstart ks.cfg file to the bootstrap HTTP server path.
133
+ * @param {object} params
134
+ * @param {string} params.bootstrapHttpServerPath
135
+ * @param {string} params.hostname
136
+ * @param {string} params.kickstartSrc
137
+ * @memberof UnderpostKickStart
138
+ * @returns {void}
139
+ */
140
+ httpServerStaticFactory({ bootstrapHttpServerPath, hostname, kickstartSrc }) {
141
+ if (!kickstartSrc) return;
142
+ const dest = `${bootstrapHttpServerPath}/${hostname}/ks.cfg`;
143
+ fs.writeFileSync(dest, kickstartSrc, 'utf8');
144
+ logger.info(`Kickstart file written to ${dest}`);
145
+ },
146
+ };
147
+ }
148
+
149
+ export default UnderpostKickStart;
@@ -152,6 +152,7 @@ class UnderpostRepository {
152
152
  .map((commitData, i) => `${i === 0 ? '' : ' && '}git ${diffCmd} ${commitData.hash}`)
153
153
  .join('');
154
154
  if (history[0]) {
155
+ let index = history.length;
155
156
  for (const commit of history) {
156
157
  console.log(
157
158
  shellExec(`git show -s --format=%ci ${commit.hash}`, {
@@ -160,7 +161,8 @@ class UnderpostRepository {
160
161
  disableLog: true,
161
162
  }).trim().green,
162
163
  );
163
- console.log(commit.hash.yellow, commit.message);
164
+ console.log(`${index}`.magenta, commit.hash.yellow, commit.message);
165
+ index--;
164
166
  console.log(
165
167
  shellExec(`git show --name-status --pretty="" ${commit.hash}`, {
166
168
  stdout: true,
package/src/cli/run.js CHANGED
@@ -68,7 +68,8 @@ const logger = loggerFactory(import.meta);
68
68
  * @property {boolean} etcHosts - Whether to modify /etc/hosts.
69
69
  * @property {string} confServerPath - The configuration server path.
70
70
  * @property {string} underpostRoot - The root path of the Underpost installation.
71
- * @property {string} cronJobs - The cron jobs to run.
71
+ * @property {string} cmdCronJobs - Pre-script commands to run before cron job execution.
72
+ * @property {string} deployIdCronJobs - The deployment ID for cron jobs.
72
73
  * @property {string} timezone - The timezone to set.
73
74
  * @property {boolean} kubeadm - Whether to run in kubeadm mode.
74
75
  * @property {boolean} kind - Whether to run in kind mode.
@@ -84,6 +85,8 @@ const logger = loggerFactory(import.meta);
84
85
  * @property {string} monitorStatusKindType - The monitor status kind type option.
85
86
  * @property {string} monitorStatusDeltaMs - The monitor status delta in milliseconds.
86
87
  * @property {string} monitorStatusMaxAttempts - The maximum number of attempts for monitor status.
88
+ * @property {boolean} dryRun - Whether to perform a dry run.
89
+ * @property {boolean} createJobNow - Whether to create the job immediately.
87
90
  * @property {boolean} logs - Whether to enable logs.
88
91
  * @memberof UnderpostRun
89
92
  */
@@ -127,7 +130,8 @@ const DEFAULT_OPTION = {
127
130
  etcHosts: false,
128
131
  confServerPath: '',
129
132
  underpostRoot: '',
130
- cronJobs: '',
133
+ cmdCronJobs: '',
134
+ deployIdCronJobs: '',
131
135
  timezone: '',
132
136
  kubeadm: false,
133
137
  kind: false,
@@ -144,6 +148,8 @@ const DEFAULT_OPTION = {
144
148
  monitorStatusDeltaMs: '',
145
149
  monitorStatusMaxAttempts: '',
146
150
  logs: false,
151
+ dryRun: false,
152
+ createJobNow: false,
147
153
  };
148
154
 
149
155
  /**
@@ -502,7 +508,21 @@ class UnderpostRun {
502
508
  if (!validVersion) throw new Error('Version mismatch');
503
509
  }
504
510
  if (options.timezone !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} tz`);
505
- if (options.cronJobs !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} cron`);
511
+ if (options.deployIdCronJobs !== 'none') {
512
+ const cronClusterFlag = options.k3s
513
+ ? ' --k3s'
514
+ : options.kind
515
+ ? ' --kind'
516
+ : options.kubeadm
517
+ ? ' --kubeadm'
518
+ : '';
519
+ const cronCmdFlag = options.cmdCronJobs ? ` --cmd-cron-jobs "${options.cmdCronJobs}"` : '';
520
+ const cronCreateJobNowFlag = options.createJobNow ? ' --create-job-now' : '';
521
+ const cronDryRunFlag = options.dryRun ? ' --dry-run' : '';
522
+ shellExec(
523
+ `${baseCommand} run${baseClusterCommand} cron${cronClusterFlag}${cronCmdFlag}${cronCreateJobNowFlag}${cronDryRunFlag}${options.deployIdCronJobs ? ` ${options.deployIdCronJobs}` : ''}`,
524
+ );
525
+ }
506
526
  }
507
527
 
508
528
  const currentTraffic = isDeployRunnerContext(path, options)
@@ -710,16 +730,29 @@ class UnderpostRun {
710
730
 
711
731
  /**
712
732
  * @method cron
713
- * @description Sets up and starts the `dd-cron` environment by writing environment variables, starting the cron service, and cleaning up.
733
+ * @description Sets up cron jobs using `underpost cron --setup-start` command, which likely configures scheduled tasks for the application.
714
734
  * @param {string} path - The input value, identifier, or path for the operation.
715
735
  * @param {Object} options - The default underpost runner options for customizing workflow
716
736
  * @memberof UnderpostRun
717
737
  */
718
738
  cron: (path, options = DEFAULT_OPTION) => {
719
- const env = options.dev ? 'development' : 'production';
720
- shellExec(`node bin env ${path ? path : 'dd-cron'} ${env}`);
721
- shellExec(`npm start`);
722
- shellExec(`node bin env clean`);
739
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
740
+ const devFlag = options.dev ? ' --dev' : '';
741
+ const gitFlag = options.git ? ' --git' : '';
742
+ const namespaceFlag =
743
+ options.namespace && options.namespace !== 'default' ? ` --namespace ${options.namespace}` : '';
744
+ const imageFlag = options.image ? ` --image ${options.image}` : '';
745
+ const cmdFlag = options.cmdCronJobs ? ` --cmd "${options.cmdCronJobs}"` : '';
746
+ const ddCronPath = './engine-private/deploy/dd.cron';
747
+ const defaultDeployId = !path && fs.existsSync(ddCronPath) ? fs.readFileSync(ddCronPath, 'utf8').trim() : '';
748
+ const setupStartId = path || defaultDeployId;
749
+ const setupStart = setupStartId ? ` --setup-start ${setupStartId} --apply` : '';
750
+ const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : options.kubeadm ? ' --kubeadm' : '';
751
+ const createJobNowFlag = options.createJobNow ? ' --create-job-now' : '';
752
+ const dryRunFlag = options.dryRun ? ' --dry-run' : '';
753
+ shellExec(
754
+ `${baseCommand} cron${devFlag}${gitFlag}${namespaceFlag}${imageFlag}${cmdFlag}${clusterFlag}${createJobNowFlag}${dryRunFlag}${setupStart}`,
755
+ );
723
756
  },
724
757
 
725
758
  /**
@@ -991,7 +1024,7 @@ EOF
991
1024
  args: [daemonProcess(path ? path : `cd /home/dd/engine && npm install && npm run test`)],
992
1025
  };
993
1026
 
994
- await Underpost.run.RUNNERS['deploy-job'](path, payload);
1027
+ await Underpost.run.CALL('deploy-job', path, payload);
995
1028
  },
996
1029
 
997
1030
  /**
@@ -1234,6 +1267,19 @@ EOF
1234
1267
  shellExec(`./scripts/disk-clean.sh`);
1235
1268
  },
1236
1269
 
1270
+ /**
1271
+ * @method disk-devices
1272
+ * @description Executes the `disk-devices.sh` script to display information about disk devices.
1273
+ * @param {string} path - The input value, identifier, or path for the operation.
1274
+ * @param {Object} options - The default underpost runner options for customizing workflow
1275
+ * @memberof UnderpostRun
1276
+ */
1277
+ 'disk-devices': async (path = '/', options = DEFAULT_OPTION) => {
1278
+ const { underpostRoot } = options;
1279
+ shellExec(`chmod +x ${underpostRoot}/scripts/disk-devices.sh`);
1280
+ shellExec(`${underpostRoot}/scripts/disk-devices.sh`);
1281
+ },
1282
+
1237
1283
  /**
1238
1284
  * @method disk-usage
1239
1285
  * @description Displays disk usage statistics using the `du` command, sorted by size.
@@ -1537,7 +1583,7 @@ EOF
1537
1583
  `${baseCommand} secret underpost --create-from-file /etc/config/.env.${env}`,
1538
1584
  `${baseCommand} start --build --run ${deployId} ${env} --underpost-quickly-install`,
1539
1585
  ];
1540
- shellExec(`node bin run sync${baseClusterCommand} --cron-jobs none dd-test --cmd "${cmd}"`);
1586
+ shellExec(`node bin run sync${baseClusterCommand} --deploy-id-cron-jobs none dd-test --cmd "${cmd}"`);
1541
1587
  },
1542
1588
 
1543
1589
  /**
@@ -18,40 +18,6 @@ dotenv.config();
18
18
  */
19
19
  class UnderpostSecret {
20
20
  static API = {
21
- /**
22
- * @method docker
23
- * @description Manages the secrets of the application.
24
- * @memberof UnderpostSecret
25
- */
26
- docker: {
27
- /**
28
- * @method init
29
- * @description Initializes the docker secrets.
30
- * @memberof UnderpostSecret
31
- */
32
- init() {
33
- shellExec(`docker swarm init`);
34
- },
35
- /**
36
- * @method createFromEnvFile
37
- * @description Creates a secret from an env file.
38
- * @param {string} envPath - The path to the env file.
39
- * @memberof UnderpostSecret
40
- */
41
- createFromEnvFile(envPath) {
42
- const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
43
- for (const key of Object.keys(envObj)) {
44
- Underpost.secret.docker.set(key, envObj[key]);
45
- }
46
- },
47
- set(key, value) {
48
- shellExec(`docker secret rm ${key}`);
49
- shellExec(`echo "${value}" | docker secret create ${key} -`);
50
- },
51
- list() {
52
- shellExec(`docker secret ls`);
53
- },
54
- },
55
21
  /**
56
22
  * @method underpost
57
23
  * @description Manages the secrets of the application.
package/src/cli/static.js CHANGED
@@ -6,11 +6,12 @@
6
6
 
7
7
  import fs from 'fs-extra';
8
8
  import path from 'path';
9
+ import express from 'express';
9
10
  import { ssrFactory } from '../server/ssr.js';
10
11
  import { shellExec } from '../server/process.js';
11
12
  import Underpost from '../index.js';
12
13
  import { JSONweb } from '../server/client-formatted.js';
13
- import { loggerFactory } from '../server/logger.js';
14
+ import { loggerFactory, loggerMiddleware } from '../server/logger.js';
14
15
 
15
16
  const logger = loggerFactory(import.meta);
16
17
 
@@ -68,11 +69,8 @@ const logger = loggerFactory(import.meta);
68
69
  * @property {string} [page=''] - SSR component path to render
69
70
  * @property {string} [title='Home'] - Page title (deprecated: use metadata.title)
70
71
  * @property {string} [outputPath='.'] - Output file path
71
- * @property {string} [deployId=''] - Deployment identifier
72
- * @property {string} [buildHost=''] - Build host URL
73
72
  * @property {string} [buildPath='/'] - Build path
74
73
  * @property {string} [env='production'] - Environment (development/production)
75
- * @property {boolean} [build=false] - Whether to trigger build
76
74
  * @property {boolean} [dev=false] - Development mode flag
77
75
  * @property {boolean} [minify=true] - Minify HTML output
78
76
  * @property {MetadataOptions} [metadata={}] - Comprehensive metadata options
@@ -95,11 +93,8 @@ const DefaultStaticGenerationOptions = {
95
93
  page: '',
96
94
  title: '',
97
95
  outputPath: '',
98
- deployId: '',
99
- buildHost: '',
100
96
  buildPath: '/',
101
97
  env: 'production',
102
- build: false,
103
98
  dev: false,
104
99
  minify: true,
105
100
  metadata: {},
@@ -385,11 +380,8 @@ class UnderpostStatic {
385
380
  * @param {string} [options.page] - Path to the SSR component to render
386
381
  * @param {string} [options.title] - Page title (deprecated: use metadata.title)
387
382
  * @param {string} [options.outputPath] - Output file path
388
- * @param {string} [options.deployId] - Deployment identifier
389
- * @param {string} [options.buildHost] - Build host URL
390
383
  * @param {string} [options.buildPath='/'] - Build path
391
384
  * @param {string} [options.env='production'] - Environment (development/production)
392
- * @param {boolean} [options.build=false] - Whether to trigger build
393
385
  * @param {boolean} [options.minify=true] - Minify HTML output
394
386
  * @param {MetadataOptions} [options.metadata={}] - Comprehensive metadata options
395
387
  * @param {Object} [options.scripts={}] - Script injection options
@@ -602,21 +594,29 @@ class UnderpostStatic {
602
594
  }
603
595
  }
604
596
 
605
- // Trigger build if requested
606
- if (options.deployId && options.build) {
607
- try {
608
- logger.info(`Triggering build for deployment: ${options.deployId}`);
597
+ // Start standalone static file server if --run-sv is specified
598
+ if (options.runSv !== undefined) {
599
+ const port = typeof options.runSv === 'string' ? parseInt(options.runSv, 10) : 5000;
600
+ const servePath =
601
+ options.outputPath && options.outputPath !== '.'
602
+ ? path.dirname(path.resolve(options.outputPath))
603
+ : path.resolve('.');
604
+
605
+ if (!fs.existsSync(servePath)) {
606
+ logger.error(`Serve path does not exist: ${servePath}`);
607
+ return;
608
+ }
609
609
 
610
- shellExec(`underpost env ${options.deployId} ${options.env}`);
611
- shellExec(
612
- `npm run build ${options.deployId}${options.buildHost ? ` ${options.buildHost} ${options.buildPath}` : ``}`,
613
- );
610
+ const app = express();
614
611
 
615
- logger.info('Build completed successfully');
616
- } catch (error) {
617
- logger.error(`Build error: ${error.message}`);
618
- throw error;
619
- }
612
+ app.use(loggerMiddleware(import.meta, 'debug', () => false));
613
+
614
+ app.use('/', express.static(servePath));
615
+
616
+ app.listen(port, () => {
617
+ logger.info(`Static file server running at http://localhost:${port}`);
618
+ logger.info(`Serving files from: ${servePath}`);
619
+ });
620
620
  }
621
621
  },
622
622
 
@@ -22,8 +22,9 @@ const Docs = {
22
22
  return html`
23
23
  <iframe
24
24
  class="in iframe-${ModalId}"
25
- style="width: 100%; border: none; background: white"
25
+ style="width: 100%; border: none; background: white; display: block"
26
26
  src="${docData.url()}"
27
+ sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-popups-to-escape-sandbox"
27
28
  >
28
29
  </iframe>
29
30
  `;
@@ -37,9 +38,27 @@ const Docs = {
37
38
  query: true,
38
39
  RouterInstance: Modal.Data['modal-docs'].options.RouterInstance,
39
40
  });
41
+ const iframeEl = s(`.iframe-${ModalId}`);
42
+ if (iframeEl) {
43
+ iframeEl.addEventListener('load', () => {
44
+ try {
45
+ const iframeWin = iframeEl.contentWindow;
46
+ if (iframeWin) {
47
+ Object.defineProperty(iframeWin, 'parent', { get: () => iframeWin, configurable: false });
48
+ Object.defineProperty(iframeWin, 'top', { get: () => iframeWin, configurable: false });
49
+ }
50
+ } catch (e) {
51
+ // cross-origin or security restriction — safe to ignore
52
+ }
53
+ window.scrollTo(0, 0);
54
+ });
55
+ }
40
56
  Modal.Data[ModalId].onObserverListener[ModalId] = () => {
41
- if (s(`.iframe-${ModalId}`))
42
- s(`.iframe-${ModalId}`).style.height = `${s(`.${ModalId}`).offsetHeight - Modal.headerTitleHeight}px`;
57
+ if (s(`.iframe-${ModalId}`)) {
58
+ const barEl = s(`.bar-default-modal-${ModalId}`);
59
+ const barHeight = barEl ? barEl.offsetHeight : Modal.headerTitleHeight;
60
+ s(`.iframe-${ModalId}`).style.height = `${s(`.${ModalId}`).offsetHeight - barHeight}px`;
61
+ }
43
62
 
44
63
  if (type.match('coverage')) {
45
64
  simpleIconsRender(`.doc-icon-coverage`);
package/src/index.js CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import UnderpostBaremetal from './cli/baremetal.js';
8
8
  import UnderpostCloudInit from './cli/cloud-init.js';
9
+ import UnderpostKickStart from './cli/kickstart.js';
9
10
  import UnderpostCluster from './cli/cluster.js';
10
11
  import UnderpostDB from './cli/db.js';
11
12
  import UnderpostDeploy from './cli/deploy.js';
@@ -25,7 +26,7 @@ import UnderpostDns from './server/dns.js';
25
26
  import UnderpostBackup from './server/backup.js';
26
27
  import UnderpostCron from './server/cron.js';
27
28
  import UnderpostStartUp from './server/start.js';
28
- import UnderpostTls from './server/tls.js';
29
+ import UnderpostTLS from './server/tls.js';
29
30
 
30
31
  /**
31
32
  * Underpost main module methods
@@ -39,7 +40,16 @@ class Underpost {
39
40
  * @type {String}
40
41
  * @memberof Underpost
41
42
  */
42
- static version = 'v2.99.4';
43
+ static version = 'v2.99.6';
44
+
45
+ /**
46
+ * Required Node.js major version
47
+ * @static
48
+ * @type {String}
49
+ * @memberof Underpost
50
+ */
51
+ static majorNodejsVersion = 'v24';
52
+
43
53
  /**
44
54
  * Repository cli API
45
55
  * @static
@@ -170,6 +180,16 @@ class Underpost {
170
180
  return UnderpostCloudInit.API;
171
181
  }
172
182
 
183
+ /**
184
+ * KickStart cli API
185
+ * @static
186
+ * @type {UnderpostKickStart.API}
187
+ * @memberof Underpost
188
+ */
189
+ static get kickstart() {
190
+ return UnderpostKickStart.API;
191
+ }
192
+
173
193
  /**
174
194
  * Run cli API
175
195
  * @static
@@ -233,14 +253,19 @@ class Underpost {
233
253
  /**
234
254
  * TLS/SSL server utilities API
235
255
  * @static
236
- * @type {UnderpostTls.API}
256
+ * @type {UnderpostTLS.API}
237
257
  * @memberof Underpost
238
258
  */
239
259
  static get tls() {
240
- return UnderpostTls.API;
260
+ return UnderpostTLS.API;
241
261
  }
242
262
  }
243
263
 
264
+ if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
265
+ console.warn(
266
+ `${`Underpost Warning: Required Node.js version is `.red}${`${Underpost.majorNodejsVersion}.x`.bgBlue.bold.white}${`, you are using `.red}${process.version.bgRed.bold.white}`,
267
+ );
268
+
244
269
  const up = Underpost;
245
270
 
246
271
  const underpost = Underpost;
@@ -269,7 +294,7 @@ export {
269
294
  UnderpostBackup,
270
295
  UnderpostCron,
271
296
  UnderpostStartUp,
272
- UnderpostTls,
297
+ UnderpostTLS,
273
298
  };
274
299
 
275
300
  export default Underpost;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Manages backup operations for deployments.
3
3
  * @module src/server/backup.js
4
- * @namespace BackUp
4
+ * @namespace UnderpostBakcUp
5
5
  */
6
6
 
7
7
  import fs from 'fs-extra';
@@ -16,7 +16,7 @@ const logger = loggerFactory(import.meta);
16
16
  /**
17
17
  * @class BackUp
18
18
  * @description Manages backup operations for deployments.
19
- * @memberof BackUp
19
+ * @memberof UnderpostBakcUp
20
20
  */
21
21
  class BackUp {
22
22
  /**
@@ -25,7 +25,10 @@ class BackUp {
25
25
  * @param {string} deployList - The list of deployments to backup.
26
26
  * @param {Object} options - The options for the backup operation.
27
27
  * @param {boolean} options.git - Whether to backup data using Git.
28
- * @memberof BackUp
28
+ * @param {boolean} [options.k3s] - Use k3s cluster context.
29
+ * @param {boolean} [options.kind] - Use kind cluster context.
30
+ * @param {boolean} [options.kubeadm] - Use kubeadm cluster context.
31
+ * @memberof UnderpostBakcUp
29
32
  */
30
33
  static callback = async function (deployList, options = { git: false }) {
31
34
  if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
@@ -34,12 +37,16 @@ class BackUp {
34
37
  logger.info('init backups callback', deployList);
35
38
  await logger.setUpInfo();
36
39
 
40
+ const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : options.kubeadm ? ' --kubeadm' : '';
41
+
37
42
  for (const _deployId of deployList.split(',')) {
38
43
  const deployId = _deployId.trim();
39
44
  if (!deployId) continue;
40
45
 
41
46
  logger.info('Executing database export for', deployId);
42
- shellExec(`node bin db ${options.git ? '--git ' : ''}--export ${deployId}`);
47
+ shellExec(
48
+ `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod${clusterFlag} ${deployId}`,
49
+ );
43
50
  }
44
51
  };
45
52
  }
@@ -137,7 +137,7 @@ const buildJsDocs = async ({ host, path, metadata = {} }) => {
137
137
 
138
138
  jsDocsConfig.opts.destination = `./public/${host}${path === '/' ? path : `${path}/`}docs/`;
139
139
  jsDocsConfig.opts.theme_opts.title = metadata?.title ? metadata.title : undefined;
140
- jsDocsConfig.opts.theme_opts.favicon = `./public/${host}${path === '/' ? path : `${path}/favicon.ico`}`;
140
+ jsDocsConfig.opts.theme_opts.favicon = `./public/${host}${path === '/' ? '/' : `${path}/`}favicon.ico`;
141
141
 
142
142
  fs.writeFileSync(`./jsdoc.json`, JSON.stringify(jsDocsConfig, null, 4), 'utf8');
143
143
  logger.warn('build jsdoc view', jsDocsConfig.opts.destination);
@@ -1172,14 +1172,6 @@ const getPathsSSR = (conf) => {
1172
1172
  * @memberof ServerConfBuilder
1173
1173
  */
1174
1174
  const Cmd = {
1175
- /**
1176
- * @method delete
1177
- * @description Deletes the deploy.
1178
- * @param {string} deployId - The deploy ID.
1179
- * @returns {string} - The delete command.
1180
- * @memberof Cmd
1181
- */
1182
- delete: (deployId) => `pm2 delete ${deployId}`,
1183
1175
  /**
1184
1176
  * @method run
1185
1177
  * @description Runs the deploy.
@@ -1221,20 +1213,6 @@ const Cmd = {
1221
1213
  * @memberof Cmd
1222
1214
  */
1223
1215
  syncPorts: () => `node bin/deploy sync-env-port`,
1224
- /**
1225
- * @method cron
1226
- * @description Creates a cron job.
1227
- * @param {string} deployList - The deploy list.
1228
- * @param {string} jobList - The job list.
1229
- * @param {string} name - The name.
1230
- * @param {string} expression - The expression.
1231
- * @param {object} options - The options.
1232
- * @param {number} instances - The number of PM2 instances (default: 1).
1233
- * @returns {string} - The cron command.
1234
- * @memberof Cmd
1235
- */
1236
- cron: (deployList, jobList, name, expression, options, instances = 1) =>
1237
- `pm2 start ./bin/index.js --no-autorestart --instances ${instances} --cron "${expression}" --name ${name} -- cron ${options?.git ? `--git ` : ''}${deployList} ${jobList}`,
1238
1216
  };
1239
1217
 
1240
1218
  /**