@underpostnet/underpost 3.0.2 → 3.1.0
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.
- package/{.env.production → .env.example} +20 -2
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +22 -7
- package/.github/workflows/publish.ci.yml +5 -5
- package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -3
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +3 -2
- package/.vscode/extensions.json +9 -8
- package/.vscode/settings.json +3 -2
- package/CHANGELOG.md +468 -290
- package/CLI-HELP.md +72 -52
- package/README.md +2 -2
- package/bin/build.js +4 -2
- package/bin/deploy.js +150 -208
- package/bin/file.js +2 -1
- package/bin/vs.js +3 -3
- package/conf.js +30 -13
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +52 -52
- package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
- package/manifests/pv-pvc-dd.yaml +1 -1
- package/package.json +53 -44
- package/scripts/k3s-node-setup.sh +1 -1
- package/src/api/document/document.service.js +1 -1
- package/src/api/file/file.controller.js +3 -1
- package/src/api/file/file.service.js +28 -5
- package/src/api/user/user.router.js +10 -5
- package/src/api/user/user.service.js +7 -7
- package/src/cli/baremetal.js +6 -10
- package/src/cli/cloud-init.js +0 -3
- package/src/cli/db.js +54 -71
- package/src/cli/deploy.js +64 -12
- package/src/cli/env.js +4 -4
- package/src/cli/fs.js +0 -2
- package/src/cli/image.js +0 -3
- package/src/cli/index.js +33 -13
- package/src/cli/monitor.js +5 -6
- package/src/cli/repository.js +322 -35
- package/src/cli/run.js +148 -71
- package/src/cli/secrets.js +0 -3
- package/src/cli/ssh.js +1 -1
- package/src/client/components/core/AgGrid.js +20 -5
- package/src/client/components/core/Content.js +22 -3
- package/src/client/components/core/Docs.js +21 -4
- package/src/client/components/core/FileExplorer.js +71 -4
- package/src/client/components/core/Input.js +1 -1
- package/src/client/components/core/Modal.js +22 -6
- package/src/client/components/core/PublicProfile.js +3 -3
- package/src/client/components/core/Router.js +34 -1
- package/src/client/components/core/Worker.js +1 -1
- package/src/client/public/default/sitemap +3 -3
- package/src/client/public/test/sitemap +3 -3
- package/src/client.build.js +0 -3
- package/src/client.dev.js +0 -3
- package/src/db/DataBaseProvider.js +17 -2
- package/src/db/mariadb/MariaDB.js +14 -9
- package/src/db/mongo/MongooseDB.js +17 -1
- package/src/index.js +1 -1
- package/src/proxy.js +0 -3
- package/src/runtime/express/Express.js +7 -1
- package/src/runtime/lampp/Lampp.js +6 -13
- package/src/server/auth.js +6 -9
- package/src/server/backup.js +2 -3
- package/src/server/client-build-docs.js +178 -3
- package/src/server/client-build-live.js +9 -18
- package/src/server/client-build.js +175 -38
- package/src/server/client-dev-server.js +14 -13
- package/src/server/conf.js +357 -149
- package/src/server/cron.js +2 -1
- package/src/server/dns.js +28 -12
- package/src/server/downloader.js +0 -2
- package/src/server/logger.js +27 -9
- package/src/server/peer.js +0 -2
- package/src/server/process.js +1 -50
- package/src/server/proxy.js +4 -8
- package/src/server/runtime.js +5 -8
- package/src/server/ssr.js +0 -3
- package/src/server/start.js +5 -5
- package/src/server/tls.js +0 -2
- package/src/server.js +0 -4
- package/.env.development +0 -43
- package/.env.test +0 -43
package/src/cli/run.js
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* @namespace UnderpostRun
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { daemonProcess, getTerminalPid,
|
|
7
|
+
import { daemonProcess, getTerminalPid, shellCd, shellExec } from '../server/process.js';
|
|
8
8
|
import {
|
|
9
9
|
awaitDeployMonitor,
|
|
10
10
|
buildKindPorts,
|
|
11
11
|
Config,
|
|
12
12
|
getNpmRootPath,
|
|
13
13
|
isDeployRunnerContext,
|
|
14
|
+
loadConfServerJson,
|
|
14
15
|
writeEnv,
|
|
15
16
|
} from '../server/conf.js';
|
|
16
17
|
import { actionInitLog, loggerFactory } from '../server/logger.js';
|
|
@@ -32,7 +33,6 @@ const logger = loggerFactory(import.meta);
|
|
|
32
33
|
* @property {string} podName - The name of the pod to run.
|
|
33
34
|
* @property {string} nodeName - The name of the node to run.
|
|
34
35
|
* @property {number} port - Custom port to use.
|
|
35
|
-
* @property {boolean} etcHosts - Whether to modify /etc/hosts.
|
|
36
36
|
* @property {string} volumeHostPath - The host path for the volume.
|
|
37
37
|
* @property {string} volumeMountPath - The mount path for the volume.
|
|
38
38
|
* @property {string} imageName - The name of the image to run.
|
|
@@ -56,7 +56,6 @@ const logger = loggerFactory(import.meta);
|
|
|
56
56
|
* @property {string} apiVersion - The API version for the container.
|
|
57
57
|
* @property {string} claimName - The claim name for the volume.
|
|
58
58
|
* @property {string} kindType - The kind of resource to create.
|
|
59
|
-
* @property {boolean} terminal - Whether to open a terminal.
|
|
60
59
|
* @property {number} devProxyPortOffset - The port offset for the development proxy.
|
|
61
60
|
* @property {boolean} hostNetwork - Whether to use host networking.
|
|
62
61
|
* @property {string} requestsMemory - The memory request for the container.
|
|
@@ -85,9 +84,12 @@ const logger = loggerFactory(import.meta);
|
|
|
85
84
|
* @property {string} monitorStatusKindType - The monitor status kind type option.
|
|
86
85
|
* @property {string} monitorStatusDeltaMs - The monitor status delta in milliseconds.
|
|
87
86
|
* @property {string} monitorStatusMaxAttempts - The maximum number of attempts for monitor status.
|
|
87
|
+
* @property {boolean} logs - Whether to enable logs.
|
|
88
88
|
* @property {boolean} dryRun - Whether to perform a dry run.
|
|
89
89
|
* @property {boolean} createJobNow - Whether to create the job immediately.
|
|
90
|
-
* @property {
|
|
90
|
+
* @property {string|Array<{ip: string, hostnames: string[]}>} hostAliases - Adds entries to the Pod /etc/hosts via Kubernetes hostAliases.
|
|
91
|
+
* As a string (CLI): semicolon-separated entries of "ip=hostname1,hostname2" (e.g., "127.0.0.1=foo.local,bar.local;10.1.2.3=foo.remote").
|
|
92
|
+
* As an array (programmatic): objects with `ip` and `hostnames` fields (e.g., [{ ip: "127.0.0.1", hostnames: ["foo.local"] }]).
|
|
91
93
|
* @memberof UnderpostRun
|
|
92
94
|
*/
|
|
93
95
|
const DEFAULT_OPTION = {
|
|
@@ -118,7 +120,6 @@ const DEFAULT_OPTION = {
|
|
|
118
120
|
apiVersion: '',
|
|
119
121
|
claimName: '',
|
|
120
122
|
kindType: '',
|
|
121
|
-
terminal: false,
|
|
122
123
|
devProxyPortOffset: 0,
|
|
123
124
|
hostNetwork: false,
|
|
124
125
|
requestsMemory: '',
|
|
@@ -150,6 +151,7 @@ const DEFAULT_OPTION = {
|
|
|
150
151
|
logs: false,
|
|
151
152
|
dryRun: false,
|
|
152
153
|
createJobNow: false,
|
|
154
|
+
hostAliases: '',
|
|
153
155
|
};
|
|
154
156
|
|
|
155
157
|
/**
|
|
@@ -192,7 +194,7 @@ class UnderpostRun {
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
{
|
|
195
|
-
// Detect MongoDB primary pod using
|
|
197
|
+
// Detect MongoDB primary pod using method
|
|
196
198
|
let primaryMongoHost = 'mongodb-0.mongodb-service';
|
|
197
199
|
try {
|
|
198
200
|
const primaryPodName = Underpost.db.getMongoPrimaryPodName({
|
|
@@ -352,22 +354,35 @@ class UnderpostRun {
|
|
|
352
354
|
},
|
|
353
355
|
/**
|
|
354
356
|
* @method template-deploy
|
|
355
|
-
* @description
|
|
357
|
+
* @description Pushes `engine-private`, dispatches CI workflow to build `pwa-microservices-template`, and optionally dispatches CD sync workflow.
|
|
356
358
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
357
359
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
358
360
|
* @memberof UnderpostRun
|
|
359
361
|
*/
|
|
360
362
|
'template-deploy': (path = '', options = DEFAULT_OPTION) => {
|
|
361
363
|
const baseCommand = options.dev ? 'node bin' : 'underpost';
|
|
364
|
+
shellExec(`npm run security:secrets`);
|
|
365
|
+
const reportPath = './gitleaks-report.json';
|
|
366
|
+
if (fs.existsSync(reportPath) && JSON.parse(fs.readFileSync(reportPath, 'utf8')).length > 0) {
|
|
367
|
+
logger.error('Secrets detected in gitleaks-report.json, aborting template-deploy');
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
shellExec(`${baseCommand} run pull`);
|
|
362
371
|
const message = shellExec(`node bin cmt --changelog --changelog-no-hash`, { silent: true, stdout: true }).trim();
|
|
363
|
-
shellExec(`${baseCommand} run clean`);
|
|
364
372
|
shellExec(
|
|
365
373
|
`${baseCommand} push ./engine-private ${options.force ? '-f ' : ''}${
|
|
366
374
|
process.env.GITHUB_USERNAME
|
|
367
375
|
}/engine-private`,
|
|
368
376
|
);
|
|
369
377
|
shellCd('/home/dd/engine');
|
|
370
|
-
|
|
378
|
+
|
|
379
|
+
// Store deploy boundary hash for changelog before dispatch
|
|
380
|
+
const deployBoundaryHash = shellExec('git rev-parse HEAD', {
|
|
381
|
+
stdout: true,
|
|
382
|
+
silent: true,
|
|
383
|
+
disableLog: true,
|
|
384
|
+
}).trim();
|
|
385
|
+
|
|
371
386
|
function replaceNthNewline(str, n, replacement = ' ') {
|
|
372
387
|
let count = 0;
|
|
373
388
|
return str.replace(/\r\n?|\n/g, (match) => {
|
|
@@ -375,35 +390,54 @@ class UnderpostRun {
|
|
|
375
390
|
return count === n ? replacement : match;
|
|
376
391
|
});
|
|
377
392
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
)}"`
|
|
387
|
-
: ''
|
|
388
|
-
}`,
|
|
389
|
-
);
|
|
393
|
+
const sanitizedMessage = message
|
|
394
|
+
? replaceNthNewline(message.replaceAll('"', '').replaceAll('`', '').replaceAll('#', '').replaceAll('- ', ''), 2)
|
|
395
|
+
.replace(/\r\n?|\n/g, ' ')
|
|
396
|
+
.trim()
|
|
397
|
+
: '';
|
|
398
|
+
|
|
399
|
+
// Push engine repo so workflow YAML changes reach GitHub
|
|
400
|
+
shellExec(`git reset`);
|
|
390
401
|
shellExec(`${baseCommand} push . ${options.force ? '-f ' : ''}${process.env.GITHUB_USERNAME}/engine`);
|
|
402
|
+
|
|
403
|
+
// Dispatch CI workflow instead of empty commit + push
|
|
404
|
+
const repo = `${process.env.GITHUB_USERNAME}/engine`;
|
|
405
|
+
Underpost.repo.dispatchWorkflow({
|
|
406
|
+
repo,
|
|
407
|
+
workflowFile: 'npmpkg.ci.yml',
|
|
408
|
+
ref: 'master',
|
|
409
|
+
inputs: sanitizedMessage ? { message: sanitizedMessage } : {},
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Dispatch CD sync-and-deploy if path starts with 'sync'
|
|
413
|
+
if (path.startsWith('sync')) {
|
|
414
|
+
const confId = path.replace(/^sync-/, '');
|
|
415
|
+
Underpost.repo.dispatchWorkflow({
|
|
416
|
+
repo,
|
|
417
|
+
workflowFile: `${confId}.cd.yml`,
|
|
418
|
+
ref: 'master',
|
|
419
|
+
inputs: { job: 'sync-and-deploy' },
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Store deploy boundary for changelog
|
|
424
|
+
shellExec(`${baseCommand} config set LAST_CI_DEPLOY_HASH ${deployBoundaryHash}`);
|
|
391
425
|
},
|
|
392
426
|
|
|
393
427
|
/**
|
|
394
428
|
* @method template-deploy-image
|
|
395
|
-
* @description
|
|
429
|
+
* @description Dispatches the Docker image CI workflow for the `engine` repository.
|
|
396
430
|
* @param {string} path - The input value, identifier, or path for the operation.
|
|
397
431
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
398
432
|
* @memberof UnderpostRun
|
|
399
433
|
*/
|
|
400
434
|
'template-deploy-image': (path, options = DEFAULT_OPTION) => {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
);
|
|
435
|
+
Underpost.repo.dispatchWorkflow({
|
|
436
|
+
repo: `${process.env.GITHUB_USERNAME}/engine`,
|
|
437
|
+
workflowFile: 'docker-image.ci.yml',
|
|
438
|
+
ref: 'master',
|
|
439
|
+
inputs: {},
|
|
440
|
+
});
|
|
407
441
|
},
|
|
408
442
|
/**
|
|
409
443
|
* @method clean
|
|
@@ -464,18 +498,30 @@ class UnderpostRun {
|
|
|
464
498
|
},
|
|
465
499
|
/**
|
|
466
500
|
* @method ssh-deploy
|
|
467
|
-
* @description
|
|
468
|
-
* @param {string} path - The
|
|
501
|
+
* @description Dispatches the corresponding CD workflow for SSH-based deployment, replacing empty commits with workflow_dispatch.
|
|
502
|
+
* @param {string} path - The deployment identifier (e.g., 'engine-core', 'sync-engine-core', 'init-engine-core').
|
|
469
503
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
470
504
|
* @memberof UnderpostRun
|
|
471
505
|
*/
|
|
472
506
|
'ssh-deploy': (path, options = DEFAULT_OPTION) => {
|
|
473
507
|
actionInitLog();
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
508
|
+
|
|
509
|
+
let job = 'deploy';
|
|
510
|
+
let confId = path;
|
|
511
|
+
if (path.startsWith('sync-')) {
|
|
512
|
+
job = 'sync-and-deploy';
|
|
513
|
+
confId = path.replace(/^sync-/, '');
|
|
514
|
+
} else if (path.startsWith('init-')) {
|
|
515
|
+
job = 'init';
|
|
516
|
+
confId = path.replace(/^init-/, '');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
Underpost.repo.dispatchWorkflow({
|
|
520
|
+
repo: `${process.env.GITHUB_USERNAME}/engine`,
|
|
521
|
+
workflowFile: `${confId}.cd.yml`,
|
|
522
|
+
ref: 'master',
|
|
523
|
+
inputs: { job },
|
|
524
|
+
});
|
|
479
525
|
},
|
|
480
526
|
/**
|
|
481
527
|
* @method ide
|
|
@@ -485,15 +531,25 @@ class UnderpostRun {
|
|
|
485
531
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
486
532
|
* @memberof UnderpostRun
|
|
487
533
|
*/
|
|
488
|
-
ide: (path, options = DEFAULT_OPTION) => {
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
shellExec(
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
534
|
+
ide: (path = '', options = DEFAULT_OPTION) => {
|
|
535
|
+
const underpostRoot = options.dev ? '.' : options.underpostRoot;
|
|
536
|
+
const [projectPath, customIde] = path.split(',');
|
|
537
|
+
if (projectPath === 'install') {
|
|
538
|
+
if (customIde === 'zed') shellExec(`sudo curl -f https://zed.dev/install.sh | sh`);
|
|
539
|
+
else if (customIde === 'subl') {
|
|
540
|
+
shellExec(
|
|
541
|
+
`sudo dnf config-manager --add-repo https://download.sublimetext.com/rpm/stable/x86_64/sublime-text.repo`,
|
|
542
|
+
);
|
|
543
|
+
shellExec(`sudo dnf install -y sublime-text`);
|
|
544
|
+
} else {
|
|
545
|
+
shellExec(`sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc &&
|
|
546
|
+
echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\nautorefresh=1\ntype=rpm-md\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" | sudo tee /etc/yum.repos.d/vscode.repo > /dev/null`);
|
|
547
|
+
shellExec(`sudo dnf install -y code`);
|
|
548
|
+
}
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (customIde === 'zed') shellExec(`node ${underpostRoot}/bin/zed ${projectPath}`);
|
|
552
|
+
else shellExec(`node ${underpostRoot}/bin/vs ${projectPath}`);
|
|
497
553
|
},
|
|
498
554
|
/**
|
|
499
555
|
* @method crypto-policy
|
|
@@ -522,7 +578,7 @@ class UnderpostRun {
|
|
|
522
578
|
options.replicas,
|
|
523
579
|
``,
|
|
524
580
|
``,
|
|
525
|
-
options.
|
|
581
|
+
!options.kubeadm && !options.k3s ? 'kind-control-plane' : os.hostname(),
|
|
526
582
|
];
|
|
527
583
|
let [deployId, replicas, versions, image, node] = path ? path.split(',') : defaultPath;
|
|
528
584
|
deployId = deployId ? deployId : defaultPath[0];
|
|
@@ -1005,9 +1061,7 @@ EOF
|
|
|
1005
1061
|
volumeMountPath: volumeHostPath,
|
|
1006
1062
|
...(options.dev ? { volumeHostPath } : { claimName }),
|
|
1007
1063
|
on: {
|
|
1008
|
-
init: async () => {
|
|
1009
|
-
// openTerminal(`kubectl logs -f ${podName}`);
|
|
1010
|
-
},
|
|
1064
|
+
init: async () => {},
|
|
1011
1065
|
},
|
|
1012
1066
|
args: [daemonProcess(path ? path : `cd /home/dd/engine && npm install && npm run test`)],
|
|
1013
1067
|
};
|
|
@@ -1142,7 +1196,7 @@ EOF
|
|
|
1142
1196
|
const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
|
|
1143
1197
|
let hosts = [];
|
|
1144
1198
|
for (const deployId of deployList) {
|
|
1145
|
-
const confServer =
|
|
1199
|
+
const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
|
|
1146
1200
|
hosts = hosts.concat(Object.keys(confServer));
|
|
1147
1201
|
}
|
|
1148
1202
|
shellExec(`node bin cluster --prom ${hosts.join(',')}`);
|
|
@@ -1315,34 +1369,31 @@ EOF
|
|
|
1315
1369
|
if (!subConf) subConf = 'local';
|
|
1316
1370
|
if (options.reset && fs.existsSync(`./engine-private/conf/${deployId}`))
|
|
1317
1371
|
fs.removeSync(`./engine-private/conf/${deployId}`);
|
|
1318
|
-
if (!fs.existsSync(`./engine-private/conf/${deployId}`)) Config.deployIdFactory(deployId, { subConf });
|
|
1319
1372
|
if (options.devProxyPortOffset) {
|
|
1320
1373
|
const envPath = `./engine-private/conf/${deployId}/.env.development`;
|
|
1321
1374
|
const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
1322
1375
|
envObj.DEV_PROXY_PORT_OFFSET = options.devProxyPortOffset;
|
|
1323
1376
|
writeEnv(envPath, envObj);
|
|
1324
1377
|
}
|
|
1378
|
+
dotenv.config({ path: `./engine-private/conf/${deployId}/.env.development`, override: true });
|
|
1325
1379
|
shellExec(`node bin run dev-cluster --expose --namespace ${options.namespace}`, { async: true });
|
|
1326
1380
|
{
|
|
1327
|
-
const cmd = `npm run dev
|
|
1381
|
+
const cmd = `npm run dev:api ${deployId} ${subConf} ${host} ${_path} ${clientHostPort} proxy${
|
|
1328
1382
|
options.tls ? ' tls' : ''
|
|
1329
1383
|
}`;
|
|
1330
|
-
|
|
1384
|
+
shellExec(cmd, { async: true });
|
|
1331
1385
|
}
|
|
1332
1386
|
await awaitDeployMonitor(true);
|
|
1333
1387
|
{
|
|
1334
|
-
const cmd = `npm run dev
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
:
|
|
1338
|
-
|
|
1339
|
-
});
|
|
1388
|
+
const cmd = `npm run dev:client ${deployId} ${subConf} ${host} ${_path} proxy${options.tls ? ' tls' : ''}`;
|
|
1389
|
+
|
|
1390
|
+
shellExec(cmd, {
|
|
1391
|
+
async: true,
|
|
1392
|
+
});
|
|
1340
1393
|
}
|
|
1341
1394
|
await awaitDeployMonitor(true);
|
|
1342
1395
|
shellExec(
|
|
1343
|
-
|
|
1344
|
-
options.tls ? ' tls' : ''
|
|
1345
|
-
}`,
|
|
1396
|
+
`NODE_ENV=development node src/proxy proxy ${deployId} ${subConf} ${host} ${_path}${options.tls ? ' tls' : ''}`,
|
|
1346
1397
|
);
|
|
1347
1398
|
},
|
|
1348
1399
|
|
|
@@ -1361,7 +1412,7 @@ EOF
|
|
|
1361
1412
|
let [deployId, serviceId, host, _path, replicas, image, node] = path.split(',');
|
|
1362
1413
|
if (!replicas) replicas = options.replicas;
|
|
1363
1414
|
// const confClient = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.client.json`, 'utf8'));
|
|
1364
|
-
const confServer =
|
|
1415
|
+
const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
|
|
1365
1416
|
// const confSSR = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.ssr.json`, 'utf8'));
|
|
1366
1417
|
// const packageData = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/package.json`, 'utf8'));
|
|
1367
1418
|
const services = fs.existsSync(`./engine-private/deploy/${deployId}/conf.services.json`)
|
|
@@ -1452,9 +1503,7 @@ EOF
|
|
|
1452
1503
|
'etc-hosts': async (path = '', options = DEFAULT_OPTION) => {
|
|
1453
1504
|
const hosts = path ? path.split(',') : [];
|
|
1454
1505
|
if (options.deployId) {
|
|
1455
|
-
const confServer =
|
|
1456
|
-
fs.readFileSync(`./engine-private/conf/${options.deployId}/conf.server.json`, 'utf8'),
|
|
1457
|
-
);
|
|
1506
|
+
const confServer = loadConfServerJson(`./engine-private/conf/${options.deployId}/conf.server.json`);
|
|
1458
1507
|
hosts.push(...Object.keys(confServer));
|
|
1459
1508
|
}
|
|
1460
1509
|
const hostListenResult = Underpost.deploy.etcHostFactory(hosts);
|
|
@@ -1569,7 +1618,7 @@ EOF
|
|
|
1569
1618
|
`npm install -g npm@11.2.0`,
|
|
1570
1619
|
`npm install -g underpost`,
|
|
1571
1620
|
`${baseCommand} secret underpost --create-from-file /etc/config/.env.${env}`,
|
|
1572
|
-
`${baseCommand} start --build --run ${deployId} ${env}
|
|
1621
|
+
`${baseCommand} start --build --run ${deployId} ${env}`,
|
|
1573
1622
|
];
|
|
1574
1623
|
shellExec(`node bin run sync${baseClusterCommand} --deploy-id-cron-jobs none dd-test --cmd "${cmd}"`);
|
|
1575
1624
|
},
|
|
@@ -1588,19 +1637,22 @@ EOF
|
|
|
1588
1637
|
for (let deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
|
|
1589
1638
|
deployId = deployId.trim();
|
|
1590
1639
|
const _path = '/single-replica';
|
|
1591
|
-
const confServer =
|
|
1640
|
+
const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
|
|
1592
1641
|
shellExec(`${baseCommand} env ${deployId} ${env}`);
|
|
1593
1642
|
for (const host of Object.keys(confServer))
|
|
1594
|
-
if (_path in confServer[host])
|
|
1643
|
+
if (_path in confServer[host])
|
|
1644
|
+
await Underpost.repo.client(deployId, '', host, _path, {
|
|
1645
|
+
singleReplica: true,
|
|
1646
|
+
});
|
|
1595
1647
|
const node = options.nodeName
|
|
1596
1648
|
? options.nodeName
|
|
1597
|
-
: options.
|
|
1649
|
+
: !options.kubeadm && !options.k3s
|
|
1598
1650
|
? 'kind-control-plane'
|
|
1599
1651
|
: os.hostname();
|
|
1600
1652
|
// deployId, replicas, versions, image, node
|
|
1601
1653
|
let defaultPath = [deployId, 1, ``, ``, node];
|
|
1602
1654
|
shellExec(`${baseCommand} run${options.dev === true ? ' --dev' : ''} --build sync ${defaultPath}`);
|
|
1603
|
-
|
|
1655
|
+
await Underpost.repo.client(deployId);
|
|
1604
1656
|
}
|
|
1605
1657
|
if (isDeployRunnerContext(path, options)) shellExec(`${baseCommand} run promote ${path} production`);
|
|
1606
1658
|
},
|
|
@@ -1677,7 +1729,7 @@ EOF
|
|
|
1677
1729
|
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${fromPath} ${toPath}`);
|
|
1678
1730
|
}
|
|
1679
1731
|
|
|
1680
|
-
|
|
1732
|
+
shellExec(`firefox ${outsPaths.join(' ')}`);
|
|
1681
1733
|
process.exit(0);
|
|
1682
1734
|
}
|
|
1683
1735
|
})();
|
|
@@ -1840,6 +1892,30 @@ EOF
|
|
|
1840
1892
|
const imagePullPolicy = options.imagePullPolicy || 'IfNotPresent';
|
|
1841
1893
|
const hostNetwork = options.hostNetwork ? options.hostNetwork : '';
|
|
1842
1894
|
const apiVersion = options.apiVersion || 'v1';
|
|
1895
|
+
// Parse hostAliases option:
|
|
1896
|
+
// - string from CLI: "ip1=host1,host2;ip2=host3,host4"
|
|
1897
|
+
// - array from programmatic callers: [{ ip: "127.0.0.1", hostnames: ["foo.local"] }]
|
|
1898
|
+
const hostAliases = options.hostAliases
|
|
1899
|
+
? Array.isArray(options.hostAliases)
|
|
1900
|
+
? options.hostAliases
|
|
1901
|
+
: options.hostAliases
|
|
1902
|
+
.split(';')
|
|
1903
|
+
.filter((entry) => entry.trim())
|
|
1904
|
+
.map((entry) => {
|
|
1905
|
+
const [ip, hostnamesStr] = entry.split('=');
|
|
1906
|
+
const hostnames = hostnamesStr ? hostnamesStr.split(',').map((h) => h.trim()) : [];
|
|
1907
|
+
return { ip: ip.trim(), hostnames };
|
|
1908
|
+
})
|
|
1909
|
+
: [];
|
|
1910
|
+
const hostAliasesYaml =
|
|
1911
|
+
hostAliases.length > 0
|
|
1912
|
+
? ` hostAliases:\n${hostAliases
|
|
1913
|
+
.map(
|
|
1914
|
+
(alias) =>
|
|
1915
|
+
` - ip: "${alias.ip}"\n hostnames:\n${alias.hostnames.map((h) => ` - "${h}"`).join('\n')}`,
|
|
1916
|
+
)
|
|
1917
|
+
.join('\n')}`
|
|
1918
|
+
: '';
|
|
1843
1919
|
const labels = options.labels
|
|
1844
1920
|
? options.labels
|
|
1845
1921
|
.split(',')
|
|
@@ -1870,6 +1946,7 @@ spec:
|
|
|
1870
1946
|
restartPolicy: ${restartPolicy}
|
|
1871
1947
|
${runtimeClassName ? ` runtimeClassName: ${runtimeClassName}` : ''}
|
|
1872
1948
|
${hostNetwork ? ` hostNetwork: ${hostNetwork}` : ''}
|
|
1949
|
+
${hostAliasesYaml}
|
|
1873
1950
|
containers:
|
|
1874
1951
|
- name: ${containerName}
|
|
1875
1952
|
image: ${imageName}
|
package/src/cli/secrets.js
CHANGED
|
@@ -4,13 +4,10 @@
|
|
|
4
4
|
* @namespace UnderpostSecret
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import dotenv from 'dotenv';
|
|
8
7
|
import { shellExec } from '../server/process.js';
|
|
9
8
|
import fs from 'fs-extra';
|
|
10
9
|
import Underpost from '../index.js';
|
|
11
10
|
|
|
12
|
-
dotenv.config();
|
|
13
|
-
|
|
14
11
|
/**
|
|
15
12
|
* @class UnderpostSecret
|
|
16
13
|
* @description Manages the secrets of the application.
|
package/src/cli/ssh.js
CHANGED
|
@@ -497,7 +497,7 @@ EOF`);
|
|
|
497
497
|
},
|
|
498
498
|
|
|
499
499
|
/**
|
|
500
|
-
* Generic SSH remote command runner that
|
|
500
|
+
* Generic SSH remote command runner that SSH execution logic.
|
|
501
501
|
* Executes arbitrary shell commands on a remote server via SSH with proper credential handling.
|
|
502
502
|
* @async
|
|
503
503
|
* @function sshRemoteRunner
|
|
@@ -13,20 +13,32 @@ const AgGrid = {
|
|
|
13
13
|
Render: async function (options) {
|
|
14
14
|
let { id, paginationOptions } = options;
|
|
15
15
|
setTimeout(() => {
|
|
16
|
+
// Normalize rowSelection from deprecated string form to object form (AG Grid v32.2.1+)
|
|
17
|
+
let gridOptionsOverrides = { ...(options.gridOptions || {}) };
|
|
18
|
+
if (typeof gridOptionsOverrides.rowSelection === 'string') {
|
|
19
|
+
const mode = gridOptionsOverrides.rowSelection; // 'single' or 'multiple'
|
|
20
|
+
gridOptionsOverrides.rowSelection = {
|
|
21
|
+
mode: mode === 'multiple' ? 'multiRow' : 'singleRow',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
// Grid Options: Contains all of the grid configurations
|
|
17
26
|
const gridOptions = {
|
|
27
|
+
// Use legacy CSS theme mode to avoid conflict with Theming API (AG Grid v33+)
|
|
28
|
+
theme: 'legacy',
|
|
18
29
|
// Row Data: The data to be displayed.
|
|
19
30
|
pagination: false, // Disabled by default, will be handled by the management view
|
|
20
31
|
// paginationPageSize: 100,
|
|
21
32
|
// suppressPaginationPanel: true, // We are using our own custom pagination component
|
|
22
33
|
// rowHeight: 60,
|
|
23
|
-
enableCellChangeFlash
|
|
34
|
+
// enableCellChangeFlash was removed in v35; use enableCellChangeFlash on defaultColDef instead
|
|
24
35
|
defaultColDef: {
|
|
25
36
|
editable: false,
|
|
26
37
|
flex: 1,
|
|
27
38
|
minWidth: 50,
|
|
28
39
|
filter: true,
|
|
29
40
|
autoHeight: true,
|
|
41
|
+
enableCellChangeFlash: true,
|
|
30
42
|
},
|
|
31
43
|
rowClassRules: {
|
|
32
44
|
'row-new-highlight': (params) => {
|
|
@@ -76,7 +88,7 @@ const AgGrid = {
|
|
|
76
88
|
return { field };
|
|
77
89
|
})
|
|
78
90
|
: [],
|
|
79
|
-
...
|
|
91
|
+
...gridOptionsOverrides,
|
|
80
92
|
};
|
|
81
93
|
|
|
82
94
|
// Your Javascript code to create the grid
|
|
@@ -86,8 +98,11 @@ const AgGrid = {
|
|
|
86
98
|
// myGridElement.style.setProperty('width', '100%');
|
|
87
99
|
ThemeEvents[id] = () => {
|
|
88
100
|
if (s(`.${id}`)) {
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
// darkTheme has already been updated by Css.js when this event fires
|
|
102
|
+
// If darkTheme is true: remove light class, add dark class
|
|
103
|
+
// If darkTheme is false: remove dark class, add light class
|
|
104
|
+
s(`.${id}`).classList.remove(this.theme, this.theme + '-dark');
|
|
105
|
+
s(`.${id}`).classList.add(darkTheme ? this.theme + '-dark' : this.theme);
|
|
91
106
|
} else {
|
|
92
107
|
// console.warn('change theme: grid not found');
|
|
93
108
|
delete ThemeEvents[id];
|
|
@@ -112,7 +127,7 @@ const AgGrid = {
|
|
|
112
127
|
: '';
|
|
113
128
|
return html`
|
|
114
129
|
<div
|
|
115
|
-
class="${id} ${this.theme
|
|
130
|
+
class="${id} ${darkTheme ? this.theme + '-dark' : this.theme}"
|
|
116
131
|
style="${options?.style
|
|
117
132
|
? Object.keys(options.style).map((styleKey) => `${styleKey}: ${options.style[styleKey]}; `)
|
|
118
133
|
: ''}"
|
|
@@ -56,7 +56,7 @@ const attachMarkdownLinkHandlers = (containerSelector) => {
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
const Content = {
|
|
59
|
-
Render: async function (options = { idModal: '' }) {
|
|
59
|
+
Render: async function (options = { idModal: '', titleIcon: '' }) {
|
|
60
60
|
const { idModal } = options;
|
|
61
61
|
setTimeout(async () => {
|
|
62
62
|
try {
|
|
@@ -111,11 +111,30 @@ const Content = {
|
|
|
111
111
|
throw new Error(`no-preview-available`);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// Use custom titleIcon from options, or extract from the modal's original title HTML, or fall back to default
|
|
115
|
+
const titleIcon = options.titleIcon
|
|
116
|
+
? options.titleIcon
|
|
117
|
+
: Modal.Data[idModal] &&
|
|
118
|
+
Modal.Data[idModal].options &&
|
|
119
|
+
Modal.Data[idModal].options.title &&
|
|
120
|
+
Modal.Data[idModal].options.title.includes &&
|
|
121
|
+
Modal.Data[idModal].options.title.includes('<img')
|
|
122
|
+
? Modal.Data[idModal].options.title.match(/<img[^>]*>/)?.[0] || html`<i class="inl far fa-file"></i>`
|
|
123
|
+
: html`<i class="inl far fa-file"></i>`;
|
|
124
|
+
|
|
125
|
+
// Preserve the original text wrapper class if present in the modal's stored title
|
|
126
|
+
const originalTitle = Modal.Data[idModal]?.options?.title || '';
|
|
127
|
+
const hasCustomTextClass = originalTitle.includes && originalTitle.includes('underpost-text-title-modal');
|
|
128
|
+
const docTitle = documentObj.title ? documentObj.title : documentObj.location;
|
|
129
|
+
const titleText = hasCustomTextClass
|
|
130
|
+
? `<span class='inl underpost-text-title-modal'>${docTitle}</span>`
|
|
131
|
+
: docTitle;
|
|
132
|
+
|
|
114
133
|
htmls(
|
|
115
134
|
`.title-modal-${idModal}`,
|
|
116
135
|
html`${renderViewTitle({
|
|
117
|
-
icon:
|
|
118
|
-
text:
|
|
136
|
+
icon: titleIcon,
|
|
137
|
+
text: titleText,
|
|
119
138
|
})} `,
|
|
120
139
|
);
|
|
121
140
|
htmls(`.content-render-${idModal}`, ``);
|
|
@@ -12,6 +12,10 @@ const Docs = {
|
|
|
12
12
|
const docData = this.Data.find((d) => d.type === type);
|
|
13
13
|
const ModalId = `modal-docs-${docData.type}`;
|
|
14
14
|
const { barConfig } = await Themes[Css.currentTheme]();
|
|
15
|
+
const parentBarMode =
|
|
16
|
+
Modal.Data['modal-docs'] && Modal.Data['modal-docs'].options.barMode
|
|
17
|
+
? Modal.Data['modal-docs'].options.barMode
|
|
18
|
+
: undefined;
|
|
15
19
|
|
|
16
20
|
await Modal.Render({
|
|
17
21
|
barConfig,
|
|
@@ -34,7 +38,7 @@ const Docs = {
|
|
|
34
38
|
route: 'docs',
|
|
35
39
|
slideMenu: 'modal-menu',
|
|
36
40
|
observer: true,
|
|
37
|
-
barMode:
|
|
41
|
+
barMode: parentBarMode,
|
|
38
42
|
query: true,
|
|
39
43
|
RouterInstance: Modal.Data['modal-docs'].options.RouterInstance,
|
|
40
44
|
});
|
|
@@ -166,6 +170,8 @@ const Docs = {
|
|
|
166
170
|
icon: html`<i class="fab fa-github"></i>`,
|
|
167
171
|
text: `Last Release`,
|
|
168
172
|
url: function () {
|
|
173
|
+
const tokenOpts = Docs.Tokens['modal-docs'];
|
|
174
|
+
if (tokenOpts && tokenOpts.lastReleaseUrl) return tokenOpts.lastReleaseUrl();
|
|
169
175
|
return `https://github.com/underpostnet/pwa-microservices-template-ghpkg/`;
|
|
170
176
|
},
|
|
171
177
|
},
|
|
@@ -180,6 +186,8 @@ const Docs = {
|
|
|
180
186
|
</svg>`,
|
|
181
187
|
text: html`Demo`,
|
|
182
188
|
url: function () {
|
|
189
|
+
const tokenOpts = Docs.Tokens['modal-docs'];
|
|
190
|
+
if (tokenOpts && tokenOpts.demoUrl) return tokenOpts.demoUrl();
|
|
183
191
|
return `https://underpostnet.github.io/pwa-microservices-template-ghpkg/`;
|
|
184
192
|
},
|
|
185
193
|
},
|
|
@@ -204,6 +212,8 @@ const Docs = {
|
|
|
204
212
|
icon: html`<img height="20" width="20" class="doc-icon-coverage" />`,
|
|
205
213
|
text: `Coverage report`,
|
|
206
214
|
url: function () {
|
|
215
|
+
const tokenOpts = Docs.Tokens['modal-docs'];
|
|
216
|
+
if (tokenOpts && tokenOpts.coverageUrl) return tokenOpts.coverageUrl();
|
|
207
217
|
return `${getProxyPath()}docs/coverage`;
|
|
208
218
|
},
|
|
209
219
|
themeEvent: () => {
|
|
@@ -223,7 +233,7 @@ const Docs = {
|
|
|
223
233
|
},
|
|
224
234
|
],
|
|
225
235
|
Tokens: {},
|
|
226
|
-
Init: async function (options) {
|
|
236
|
+
Init: async function (options = {}) {
|
|
227
237
|
const { idModal } = options;
|
|
228
238
|
this.Tokens[idModal] = options;
|
|
229
239
|
setTimeout(() => {
|
|
@@ -284,10 +294,14 @@ const Docs = {
|
|
|
284
294
|
break;
|
|
285
295
|
}
|
|
286
296
|
tabHref = docData.url();
|
|
297
|
+
const subMenuIcon =
|
|
298
|
+
options.subMenuIcon && typeof options.subMenuIcon === 'function'
|
|
299
|
+
? options.subMenuIcon(docData.type)
|
|
300
|
+
: docData.icon;
|
|
287
301
|
docMenuRender += html`
|
|
288
302
|
${await BtnIcon.Render({
|
|
289
303
|
class: `in wfa main-btn-menu submenu-btn btn-docs btn-docs-${docData.type}`,
|
|
290
|
-
label: html`<span class="inl menu-btn-icon">${
|
|
304
|
+
label: html`<span class="inl menu-btn-icon">${subMenuIcon}</span
|
|
291
305
|
><span class="menu-label-text menu-label-text-docs"> ${docData.text} </span>`,
|
|
292
306
|
tabHref,
|
|
293
307
|
tooltipHtml: await Badge.Render(buildBadgeToolTipMenuOption(docData.text, 'right')),
|
|
@@ -447,7 +461,10 @@ const Docs = {
|
|
|
447
461
|
if (s(`.docs-card-container-${item.id}`)) {
|
|
448
462
|
s(`.docs-card-container-${item.id}`).onclick = () => {
|
|
449
463
|
if (item.id.match('demo')) {
|
|
450
|
-
|
|
464
|
+
const demoData = Docs.Data.find((d) => d.type === 'demo');
|
|
465
|
+
location.href = demoData
|
|
466
|
+
? demoData.url()
|
|
467
|
+
: 'https://underpostnet.github.io/pwa-microservices-template-ghpkg/';
|
|
451
468
|
} else if (item.id.match('api')) {
|
|
452
469
|
if (s(`.btn-docs-api`)) s(`.btn-docs-api`).click();
|
|
453
470
|
} else {
|