@underpostnet/underpost 3.1.2 → 3.1.3
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/.github/workflows/npmpkg.ci.yml +15 -1
- package/CHANGELOG.md +42 -1
- package/CLI-HELP.md +8 -2
- package/README.md +6 -3
- package/bin/deploy.js +29 -102
- package/conf.js +1 -8
- package/jsdoc.json +1 -1
- 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 +2 -2
- package/package.json +2 -2
- package/scripts/ports-ls.sh +2 -0
- package/src/cli/index.js +8 -0
- package/src/cli/repository.js +37 -37
- package/src/cli/run.js +99 -40
- package/src/client/components/core/Alert.js +2 -2
- package/src/client/components/core/Docs.js +9 -2
- package/src/client/components/core/RichText.js +1 -2
- package/src/client/ssr/body/404.js +15 -11
- package/src/client/ssr/body/500.js +15 -11
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
- package/src/client/ssr/pages/Test.js +11 -10
- package/src/index.js +1 -1
- package/src/runtime/express/Express.js +8 -8
- package/src/server/auth.js +6 -5
- package/src/server/client-build-docs.js +44 -110
- package/src/server/client-build.js +29 -38
- package/src/server/conf.js +19 -15
- package/src/server/start.js +15 -8
package/src/cli/run.js
CHANGED
|
@@ -87,6 +87,7 @@ const logger = loggerFactory(import.meta);
|
|
|
87
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 {number} fromNCommit - Number of commits back to use for message propagation (default: 1, last commit only).
|
|
90
91
|
* @property {string|Array<{ip: string, hostnames: string[]}>} hostAliases - Adds entries to the Pod /etc/hosts via Kubernetes hostAliases.
|
|
91
92
|
* 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
93
|
* As an array (programmatic): objects with `ip` and `hostnames` fields (e.g., [{ ip: "127.0.0.1", hostnames: ["foo.local"] }]).
|
|
@@ -151,6 +152,7 @@ const DEFAULT_OPTION = {
|
|
|
151
152
|
logs: false,
|
|
152
153
|
dryRun: false,
|
|
153
154
|
createJobNow: false,
|
|
155
|
+
fromNCommit: 0,
|
|
154
156
|
hostAliases: '',
|
|
155
157
|
};
|
|
156
158
|
|
|
@@ -354,8 +356,10 @@ class UnderpostRun {
|
|
|
354
356
|
},
|
|
355
357
|
/**
|
|
356
358
|
* @method template-deploy
|
|
357
|
-
* @description Pushes `engine-private`, dispatches CI workflow to build `pwa-microservices-template`,
|
|
358
|
-
*
|
|
359
|
+
* @description Pushes `engine-private`, dispatches CI workflow to build `pwa-microservices-template`,
|
|
360
|
+
* and optionally triggers engine-<conf-id> CI with sync/init which in turn dispatches the CD workflow
|
|
361
|
+
* after the build chain completes (template → ghpkg → engine-<conf-id> → CD).
|
|
362
|
+
* @param {string} path - The deployment path identifier (e.g., 'sync-engine-core', 'init-engine-core', or empty for build-only).
|
|
359
363
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
360
364
|
* @memberof UnderpostRun
|
|
361
365
|
*/
|
|
@@ -368,7 +372,14 @@ class UnderpostRun {
|
|
|
368
372
|
return;
|
|
369
373
|
}
|
|
370
374
|
shellExec(`${baseCommand} run pull`);
|
|
371
|
-
|
|
375
|
+
|
|
376
|
+
// Capture last N commit messages for propagation (default: last 1 commit)
|
|
377
|
+
const fromN = options.fromNCommit && parseInt(options.fromNCommit) > 0 ? parseInt(options.fromNCommit) : 1;
|
|
378
|
+
const message = shellExec(`node bin cmt --changelog ${fromN} --changelog-no-hash`, {
|
|
379
|
+
silent: true,
|
|
380
|
+
stdout: true,
|
|
381
|
+
}).trim();
|
|
382
|
+
|
|
372
383
|
shellExec(
|
|
373
384
|
`${baseCommand} push ./engine-private ${options.force ? '-f ' : ''}${
|
|
374
385
|
process.env.GITHUB_USERNAME
|
|
@@ -376,52 +387,40 @@ class UnderpostRun {
|
|
|
376
387
|
);
|
|
377
388
|
shellCd('/home/dd/engine');
|
|
378
389
|
|
|
379
|
-
|
|
380
|
-
const deployBoundaryHash = shellExec('git rev-parse HEAD', {
|
|
381
|
-
stdout: true,
|
|
382
|
-
silent: true,
|
|
383
|
-
disableLog: true,
|
|
384
|
-
}).trim();
|
|
385
|
-
|
|
386
|
-
function replaceNthNewline(str, n, replacement = ' ') {
|
|
387
|
-
let count = 0;
|
|
388
|
-
return str.replace(/\r\n?|\n/g, (match) => {
|
|
389
|
-
count++;
|
|
390
|
-
return count === n ? replacement : match;
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
const sanitizedMessage = message
|
|
394
|
-
? replaceNthNewline(message.replaceAll('"', '').replaceAll('`', '').replaceAll('#', '').replaceAll('- ', ''), 2)
|
|
395
|
-
.replace(/\r\n?|\n/g, ' ')
|
|
396
|
-
.trim()
|
|
397
|
-
: '';
|
|
390
|
+
const sanitizedMessage = Underpost.repo.sanitizeChangelogMessage(message);
|
|
398
391
|
|
|
399
392
|
// Push engine repo so workflow YAML changes reach GitHub
|
|
400
393
|
shellExec(`git reset`);
|
|
401
394
|
shellExec(`${baseCommand} push . ${options.force ? '-f ' : ''}${process.env.GITHUB_USERNAME}/engine`);
|
|
402
395
|
|
|
403
|
-
//
|
|
396
|
+
// Determine deploy conf and type from path (sync-engine-core, init-engine-core, etc.)
|
|
397
|
+
let deployConfId = '';
|
|
398
|
+
let deployType = '';
|
|
399
|
+
if (path.startsWith('sync-')) {
|
|
400
|
+
deployConfId = path.replace(/^sync-/, '');
|
|
401
|
+
deployType = 'sync-and-deploy';
|
|
402
|
+
} else if (path.startsWith('init-')) {
|
|
403
|
+
deployConfId = path.replace(/^init-/, '');
|
|
404
|
+
deployType = 'init';
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Dispatch npmpkg CI workflow — this builds pwa-microservices-template first.
|
|
408
|
+
// If deployConfId is set, npmpkg.ci.yml will dispatch the engine-<conf-id> CI
|
|
409
|
+
// with sync=true after template build completes. The engine CI then dispatches
|
|
410
|
+
// the CD workflow after the engine repo build finishes — ensuring correct sequence:
|
|
411
|
+
// npmpkg.ci → engine-<id>.ci → engine-<id>.cd
|
|
404
412
|
const repo = `${process.env.GITHUB_USERNAME}/engine`;
|
|
413
|
+
const inputs = {};
|
|
414
|
+
if (sanitizedMessage) inputs.message = sanitizedMessage;
|
|
415
|
+
if (deployConfId) inputs.deploy_conf_id = deployConfId;
|
|
416
|
+
if (deployType) inputs.deploy_type = deployType;
|
|
417
|
+
|
|
405
418
|
Underpost.repo.dispatchWorkflow({
|
|
406
419
|
repo,
|
|
407
420
|
workflowFile: 'npmpkg.ci.yml',
|
|
408
421
|
ref: 'master',
|
|
409
|
-
inputs
|
|
422
|
+
inputs,
|
|
410
423
|
});
|
|
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}`);
|
|
425
424
|
},
|
|
426
425
|
|
|
427
426
|
/**
|
|
@@ -1573,13 +1572,73 @@ EOF
|
|
|
1573
1572
|
},
|
|
1574
1573
|
|
|
1575
1574
|
/**
|
|
1576
|
-
* @method
|
|
1575
|
+
* @method pid-info
|
|
1576
|
+
* @description Displays detailed information about a process by PID, including service details, command line, executable path, working directory, environment variables, and parent process tree.
|
|
1577
|
+
* @param {string} path - The PID of the process to inspect.
|
|
1578
|
+
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1579
|
+
* @memberof UnderpostRun
|
|
1580
|
+
*/
|
|
1581
|
+
'pid-info': (path, options = DEFAULT_OPTION) => {
|
|
1582
|
+
const pid = path;
|
|
1583
|
+
if (!pid) {
|
|
1584
|
+
logger.error('PID is required. Usage: underpost run pid-info <pid>');
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// Services
|
|
1589
|
+
logger.info('Process info');
|
|
1590
|
+
shellExec(`sudo ps -p ${pid} -o pid,ppid,user,stime,etime,cmd`);
|
|
1591
|
+
logger.info('Command line');
|
|
1592
|
+
shellExec(`sudo cat /proc/${pid}/cmdline | tr '\\0' ' ' ; echo`);
|
|
1593
|
+
logger.info('Executable path');
|
|
1594
|
+
shellExec(`sudo readlink -f /proc/${pid}/exe`);
|
|
1595
|
+
logger.info('Working directory');
|
|
1596
|
+
shellExec(`sudo readlink -f /proc/${pid}/cwd`);
|
|
1597
|
+
logger.info('Environment variables (first 200)');
|
|
1598
|
+
shellExec(`sudo tr '\\0' '\\n' </proc/${pid}/environ | head -200`);
|
|
1599
|
+
|
|
1600
|
+
// Parent
|
|
1601
|
+
logger.info('Parent process');
|
|
1602
|
+
const parentInfo = shellExec(`sudo ps -o pid,ppid,user,cmd -p ${pid}`, { stdout: true, silent: true });
|
|
1603
|
+
console.log(parentInfo);
|
|
1604
|
+
const ppidMatch = parentInfo.split('\n').find((l) => l.trim().startsWith(pid));
|
|
1605
|
+
if (ppidMatch) {
|
|
1606
|
+
const ppid = ppidMatch.trim().split(/\s+/)[1];
|
|
1607
|
+
logger.info(`Parent PID: ${ppid}`);
|
|
1608
|
+
shellExec(`ps -fp ${ppid}`);
|
|
1609
|
+
}
|
|
1610
|
+
logger.info('Process tree');
|
|
1611
|
+
shellExec(`pstree -s ${pid}`);
|
|
1612
|
+
},
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
* @method background
|
|
1616
|
+
* @description Runs a custom command in the background using nohup, logging output to `/var/log/<id>.log` and saving the PID to `/var/run/<id>.pid`.
|
|
1617
|
+
* @param {string} path - The command to run in the background (e.g. 'npm run prod:container dd-cyberia-r3').
|
|
1618
|
+
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1619
|
+
* @memberof UnderpostRun
|
|
1620
|
+
*/
|
|
1621
|
+
background: (path, options = DEFAULT_OPTION) => {
|
|
1622
|
+
if (!path) {
|
|
1623
|
+
logger.error('Command is required. Usage: underpost run background <command>');
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const id = path.split(/\s+/).pop();
|
|
1627
|
+
const logFile = `/var/log/${id}.log`;
|
|
1628
|
+
const pidFile = `/var/run/${id}.pid`;
|
|
1629
|
+
logger.info(`Starting background process`, { id, logFile, pidFile });
|
|
1630
|
+
shellExec(`nohup ${path} > ${logFile} 2>&1 & pid=$!; echo $pid > ${pidFile}; disown`);
|
|
1631
|
+
logger.info(`Background process started for '${id}'`);
|
|
1632
|
+
},
|
|
1633
|
+
|
|
1634
|
+
/**
|
|
1635
|
+
* @method ports
|
|
1577
1636
|
* @description Set on ~/.bashrc alias: ports <port> Command to list listening ports that match the given keyword.
|
|
1578
1637
|
* @param {string} path - The input value, identifier, or path for the operation (used as a keyword to filter listening ports).
|
|
1579
1638
|
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1580
1639
|
* @memberof UnderpostRun
|
|
1581
1640
|
*/
|
|
1582
|
-
|
|
1641
|
+
ports: async (path = '', options = DEFAULT_OPTION) => {
|
|
1583
1642
|
shellExec(`chmod +x ${options.underpostRoot}/scripts/ports-ls.sh`);
|
|
1584
1643
|
shellExec(`${options.underpostRoot}/scripts/ports-ls.sh`);
|
|
1585
1644
|
},
|
|
@@ -49,7 +49,7 @@ const e404 = async () => {
|
|
|
49
49
|
<br />
|
|
50
50
|
<br />${Translate.Render('page-not-found')} <br />
|
|
51
51
|
<br />
|
|
52
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
52
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
53
53
|
</div>`;
|
|
54
54
|
};
|
|
55
55
|
|
|
@@ -68,7 +68,7 @@ const e500 = async () => {
|
|
|
68
68
|
<br />
|
|
69
69
|
<br />${Translate.Render('page-broken')} <br />
|
|
70
70
|
<br />
|
|
71
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
71
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
72
72
|
</div>`;
|
|
73
73
|
};
|
|
74
74
|
|
|
@@ -24,11 +24,18 @@ const Docs = {
|
|
|
24
24
|
html: async () => {
|
|
25
25
|
if (docData.renderHtml) return await docData.renderHtml();
|
|
26
26
|
return html`
|
|
27
|
+
<style>
|
|
28
|
+
.iframe-${ModalId} {
|
|
29
|
+
width: 100%;
|
|
30
|
+
border: none;
|
|
31
|
+
background: white;
|
|
32
|
+
display: block;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
27
35
|
<iframe
|
|
28
36
|
class="in iframe-${ModalId}"
|
|
29
|
-
style="width: 100%; border: none; background: white; display: block"
|
|
30
37
|
src="${docData.url()}"
|
|
31
|
-
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-popups-to-escape-sandbox"
|
|
38
|
+
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-popups-to-escape-sandbox allow-top-navigation"
|
|
32
39
|
>
|
|
33
40
|
</iframe>
|
|
34
41
|
`;
|
|
@@ -30,10 +30,9 @@ const RichText = {
|
|
|
30
30
|
return html` <style>
|
|
31
31
|
.md-container {
|
|
32
32
|
background: white;
|
|
33
|
-
color: black;
|
|
34
33
|
}
|
|
35
34
|
.md-container button {
|
|
36
|
-
color: black
|
|
35
|
+
color: black;
|
|
37
36
|
}
|
|
38
37
|
</style>
|
|
39
38
|
<div class="in md-container"><textarea class="${id}"></textarea></div>`;
|
|
@@ -47,9 +47,12 @@ const main = () => {
|
|
|
47
47
|
a {
|
|
48
48
|
color: black;
|
|
49
49
|
}
|
|
50
|
+
.main-body-ssr-404 {
|
|
51
|
+
top: 45%;
|
|
52
|
+
}
|
|
50
53
|
</style>
|
|
51
54
|
|
|
52
|
-
<div class="abs center
|
|
55
|
+
<div class="abs center main-body-ssr-404">
|
|
53
56
|
${icon}
|
|
54
57
|
<br />
|
|
55
58
|
<br />
|
|
@@ -57,17 +60,18 @@ const main = () => {
|
|
|
57
60
|
<br />
|
|
58
61
|
<br />${Translate.Render('page-not-found')} <br />
|
|
59
62
|
<br />
|
|
60
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
63
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
61
64
|
</div>`,
|
|
62
65
|
);
|
|
63
66
|
};
|
|
64
67
|
|
|
65
|
-
SrrComponent = () =>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
SrrComponent = () =>
|
|
69
|
+
html`<script>
|
|
70
|
+
{
|
|
71
|
+
const s = ${s};
|
|
72
|
+
const append = ${append};
|
|
73
|
+
const getLang = ${getLang};
|
|
74
|
+
const main = ${main};
|
|
75
|
+
window.onload = main;
|
|
76
|
+
}
|
|
77
|
+
</script>`;
|
|
@@ -46,9 +46,12 @@ const main = () => {
|
|
|
46
46
|
a {
|
|
47
47
|
color: black;
|
|
48
48
|
}
|
|
49
|
+
.main-body-ssr-500 {
|
|
50
|
+
top: 45%;
|
|
51
|
+
}
|
|
49
52
|
</style>
|
|
50
53
|
|
|
51
|
-
<div class="abs center
|
|
54
|
+
<div class="abs center main-body-ssr-500">
|
|
52
55
|
${icon}
|
|
53
56
|
<br />
|
|
54
57
|
<br />
|
|
@@ -56,17 +59,18 @@ const main = () => {
|
|
|
56
59
|
<br />
|
|
57
60
|
<br />${Translate.Render('page-broken')} <br />
|
|
58
61
|
<br />
|
|
59
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
62
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
60
63
|
</div>`,
|
|
61
64
|
);
|
|
62
65
|
};
|
|
63
66
|
|
|
64
|
-
SrrComponent = () =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
SrrComponent = () =>
|
|
68
|
+
html`<script>
|
|
69
|
+
{
|
|
70
|
+
const s = ${s};
|
|
71
|
+
const append = ${append};
|
|
72
|
+
const getLang = ${getLang};
|
|
73
|
+
const main = ${main};
|
|
74
|
+
window.onload = main;
|
|
75
|
+
}
|
|
76
|
+
</script>`;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
const swaggerDarkCss = css`
|
|
2
|
+
/* ── Toggle button ── */
|
|
3
|
+
.swagger-ui .topbar-wrapper {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
}
|
|
7
|
+
.swagger-theme-toggle-btn {
|
|
8
|
+
background: rgba(255, 255, 255, 0.12);
|
|
9
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
10
|
+
color: #fff;
|
|
11
|
+
padding: 5px 14px;
|
|
12
|
+
border-radius: 4px;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
font-size: 13px;
|
|
15
|
+
margin-left: auto;
|
|
16
|
+
margin-right: 16px;
|
|
17
|
+
white-space: nowrap;
|
|
18
|
+
transition: background 0.2s ease;
|
|
19
|
+
}
|
|
20
|
+
.swagger-theme-toggle-btn:hover {
|
|
21
|
+
background: rgba(255, 255, 255, 0.25);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* ── Dark mode overrides — black/gray gradients ── */
|
|
25
|
+
body.swagger-dark {
|
|
26
|
+
background: #0f0f0f;
|
|
27
|
+
}
|
|
28
|
+
body.swagger-dark .swagger-ui {
|
|
29
|
+
background: #0f0f0f;
|
|
30
|
+
}
|
|
31
|
+
body.swagger-dark .swagger-ui .wrapper {
|
|
32
|
+
background: #0f0f0f;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Info block */
|
|
36
|
+
body.swagger-dark .swagger-ui .info .title,
|
|
37
|
+
body.swagger-dark .swagger-ui .info h1,
|
|
38
|
+
body.swagger-dark .swagger-ui .info h2,
|
|
39
|
+
body.swagger-dark .swagger-ui .info h3,
|
|
40
|
+
body.swagger-dark .swagger-ui .info p,
|
|
41
|
+
body.swagger-dark .swagger-ui .info li,
|
|
42
|
+
body.swagger-dark .swagger-ui .info a {
|
|
43
|
+
color: #e8e8e8;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Scheme / server selector bar */
|
|
47
|
+
body.swagger-dark .swagger-ui .scheme-container {
|
|
48
|
+
background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
|
|
49
|
+
box-shadow: 0 1px 0 #383838;
|
|
50
|
+
}
|
|
51
|
+
body.swagger-dark .swagger-ui select {
|
|
52
|
+
background: #2a2a2a;
|
|
53
|
+
color: #e8e8e8;
|
|
54
|
+
border-color: #383838;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Operation tags */
|
|
58
|
+
body.swagger-dark .swagger-ui .opblock-tag {
|
|
59
|
+
color: #e8e8e8;
|
|
60
|
+
border-color: #383838;
|
|
61
|
+
}
|
|
62
|
+
body.swagger-dark .swagger-ui .opblock-tag small {
|
|
63
|
+
color: #a8a8a8;
|
|
64
|
+
}
|
|
65
|
+
body.swagger-dark .swagger-ui .opblock-tag:hover {
|
|
66
|
+
background: rgba(255, 255, 255, 0.04);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Operation blocks */
|
|
70
|
+
body.swagger-dark .swagger-ui .opblock {
|
|
71
|
+
background: linear-gradient(180deg, #161616 0%, #1e1e1e 100%);
|
|
72
|
+
border-color: #383838;
|
|
73
|
+
}
|
|
74
|
+
body.swagger-dark .swagger-ui .opblock .opblock-summary {
|
|
75
|
+
border-color: #383838;
|
|
76
|
+
}
|
|
77
|
+
body.swagger-dark .swagger-ui .opblock .opblock-summary-description,
|
|
78
|
+
body.swagger-dark .swagger-ui .opblock .opblock-summary-operation-id {
|
|
79
|
+
color: #a8a8a8;
|
|
80
|
+
}
|
|
81
|
+
body.swagger-dark .swagger-ui .opblock-body {
|
|
82
|
+
background: #0f0f0f;
|
|
83
|
+
}
|
|
84
|
+
body.swagger-dark .swagger-ui .opblock-description-wrapper p,
|
|
85
|
+
body.swagger-dark .swagger-ui .opblock-external-docs-wrapper p {
|
|
86
|
+
color: #a8a8a8;
|
|
87
|
+
}
|
|
88
|
+
body.swagger-dark .swagger-ui .opblock-section-header {
|
|
89
|
+
background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
|
|
90
|
+
border-color: #383838;
|
|
91
|
+
}
|
|
92
|
+
body.swagger-dark .swagger-ui .opblock-section-header h4 {
|
|
93
|
+
color: #e8e8e8;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Models section */
|
|
97
|
+
body.swagger-dark .swagger-ui section.models {
|
|
98
|
+
background: linear-gradient(180deg, #161616 0%, #1e1e1e 100%);
|
|
99
|
+
border-color: #383838;
|
|
100
|
+
}
|
|
101
|
+
body.swagger-dark .swagger-ui section.models h4,
|
|
102
|
+
body.swagger-dark .swagger-ui section.models h5 {
|
|
103
|
+
color: #e8e8e8;
|
|
104
|
+
border-color: #383838;
|
|
105
|
+
}
|
|
106
|
+
body.swagger-dark .swagger-ui .model-container {
|
|
107
|
+
background: #0f0f0f;
|
|
108
|
+
border-color: #2a2a2a;
|
|
109
|
+
}
|
|
110
|
+
body.swagger-dark .swagger-ui .model-box {
|
|
111
|
+
background: #1a1a1a;
|
|
112
|
+
}
|
|
113
|
+
body.swagger-dark .swagger-ui .model,
|
|
114
|
+
body.swagger-dark .swagger-ui .model-title,
|
|
115
|
+
body.swagger-dark .swagger-ui .model span,
|
|
116
|
+
body.swagger-dark .swagger-ui .model .property {
|
|
117
|
+
color: #e8e8e8;
|
|
118
|
+
}
|
|
119
|
+
body.swagger-dark .swagger-ui .model .property.primitive {
|
|
120
|
+
color: #a8a8a8;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Tables */
|
|
124
|
+
body.swagger-dark .swagger-ui table thead tr th {
|
|
125
|
+
background: linear-gradient(180deg, #222222 0%, #2a2a2a 100%);
|
|
126
|
+
color: #e8e8e8;
|
|
127
|
+
border-color: #383838;
|
|
128
|
+
}
|
|
129
|
+
body.swagger-dark .swagger-ui table tbody tr td {
|
|
130
|
+
color: #a8a8a8;
|
|
131
|
+
border-color: #2a2a2a;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Parameters */
|
|
135
|
+
body.swagger-dark .swagger-ui .parameter__name,
|
|
136
|
+
body.swagger-dark .swagger-ui .parameter__type,
|
|
137
|
+
body.swagger-dark .swagger-ui .parameter__in,
|
|
138
|
+
body.swagger-dark .swagger-ui .parameter__extension,
|
|
139
|
+
body.swagger-dark .swagger-ui .parameter__empty_value_toggle {
|
|
140
|
+
color: #a8a8a8;
|
|
141
|
+
}
|
|
142
|
+
body.swagger-dark .swagger-ui .parameter__name.required:after {
|
|
143
|
+
color: #ff6b6b;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Inputs & textareas */
|
|
147
|
+
body.swagger-dark .swagger-ui input[type='text'],
|
|
148
|
+
body.swagger-dark .swagger-ui input[type='email'],
|
|
149
|
+
body.swagger-dark .swagger-ui input[type='password'],
|
|
150
|
+
body.swagger-dark .swagger-ui input[type='search'],
|
|
151
|
+
body.swagger-dark .swagger-ui input[type='number'],
|
|
152
|
+
body.swagger-dark .swagger-ui textarea {
|
|
153
|
+
background: #2a2a2a;
|
|
154
|
+
color: #e8e8e8;
|
|
155
|
+
border-color: #383838;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Buttons */
|
|
159
|
+
body.swagger-dark .swagger-ui .btn {
|
|
160
|
+
color: #e8e8e8;
|
|
161
|
+
border-color: #383838;
|
|
162
|
+
background: #222222;
|
|
163
|
+
}
|
|
164
|
+
body.swagger-dark .swagger-ui .btn:hover {
|
|
165
|
+
background: #2a2a2a;
|
|
166
|
+
}
|
|
167
|
+
body.swagger-dark .swagger-ui .btn.authorize {
|
|
168
|
+
color: #49cc90;
|
|
169
|
+
border-color: #49cc90;
|
|
170
|
+
background: transparent;
|
|
171
|
+
}
|
|
172
|
+
body.swagger-dark .swagger-ui .btn.execute {
|
|
173
|
+
background: linear-gradient(180deg, #333333 0%, #262626 100%);
|
|
174
|
+
border-color: #484848;
|
|
175
|
+
color: #e8e8e8;
|
|
176
|
+
}
|
|
177
|
+
body.swagger-dark .swagger-ui .btn.cancel {
|
|
178
|
+
color: #ff6b6b;
|
|
179
|
+
border-color: #ff6b6b;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Responses */
|
|
183
|
+
body.swagger-dark .swagger-ui .responses-inner h4,
|
|
184
|
+
body.swagger-dark .swagger-ui .responses-inner h5 {
|
|
185
|
+
color: #a8a8a8;
|
|
186
|
+
}
|
|
187
|
+
body.swagger-dark .swagger-ui .response-col_status {
|
|
188
|
+
color: #e8e8e8;
|
|
189
|
+
}
|
|
190
|
+
body.swagger-dark .swagger-ui .response-col_description__inner p {
|
|
191
|
+
color: #a8a8a8;
|
|
192
|
+
}
|
|
193
|
+
body.swagger-dark .swagger-ui .response {
|
|
194
|
+
border-color: #2a2a2a;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Code / highlight */
|
|
198
|
+
body.swagger-dark .swagger-ui .highlight-code,
|
|
199
|
+
body.swagger-dark .swagger-ui .microlight {
|
|
200
|
+
background: #1a1a1a;
|
|
201
|
+
border: 1px solid #2a2a2a;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Markdown */
|
|
205
|
+
body.swagger-dark .swagger-ui .markdown p,
|
|
206
|
+
body.swagger-dark .swagger-ui .markdown li,
|
|
207
|
+
body.swagger-dark .swagger-ui .markdown a {
|
|
208
|
+
color: #a8a8a8;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Tabs */
|
|
212
|
+
body.swagger-dark .swagger-ui .tab li {
|
|
213
|
+
color: #a8a8a8;
|
|
214
|
+
}
|
|
215
|
+
body.swagger-dark .swagger-ui .tab li.active {
|
|
216
|
+
color: #e8e8e8;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Auth / modal dialog */
|
|
220
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux {
|
|
221
|
+
background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
|
|
222
|
+
border: 1px solid #383838;
|
|
223
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.7);
|
|
224
|
+
}
|
|
225
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-header {
|
|
226
|
+
border-bottom: 1px solid #383838;
|
|
227
|
+
background: linear-gradient(180deg, #222222 0%, #1a1a1a 100%);
|
|
228
|
+
}
|
|
229
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-header h3 {
|
|
230
|
+
color: #e8e8e8;
|
|
231
|
+
}
|
|
232
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content p,
|
|
233
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content label,
|
|
234
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content h4 {
|
|
235
|
+
color: #e8e8e8;
|
|
236
|
+
}
|
|
237
|
+
body.swagger-dark .swagger-ui .auth-container .errors {
|
|
238
|
+
color: #ff6b6b;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* Info / description */
|
|
242
|
+
body.swagger-dark .swagger-ui .info .base-url,
|
|
243
|
+
body.swagger-dark .swagger-ui .servers-title,
|
|
244
|
+
body.swagger-dark .swagger-ui .servers > label {
|
|
245
|
+
color: #a8a8a8;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* Expand/collapse arrows */
|
|
249
|
+
body.swagger-dark .swagger-ui svg.arrow path {
|
|
250
|
+
fill: #a8a8a8;
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
|
|
254
|
+
const swaggerDarkJs = `(function () {
|
|
255
|
+
function injectThemeToggle() {
|
|
256
|
+
var topbarWrapper = document.querySelector('.swagger-ui .topbar-wrapper');
|
|
257
|
+
if (!topbarWrapper || document.getElementById('swagger-theme-toggle')) return;
|
|
258
|
+
|
|
259
|
+
var savedTheme = localStorage.getItem('swagger-theme') || 'light';
|
|
260
|
+
if (savedTheme === 'dark') document.body.classList.add('swagger-dark');
|
|
261
|
+
|
|
262
|
+
var btn = document.createElement('button');
|
|
263
|
+
btn.id = 'swagger-theme-toggle';
|
|
264
|
+
btn.className = 'swagger-theme-toggle-btn';
|
|
265
|
+
btn.setAttribute('title', 'Toggle dark / light mode');
|
|
266
|
+
btn.textContent = savedTheme === 'dark' ? '\u2600\uFE0F Light Mode' : '\uD83C\uDF19 Dark Mode';
|
|
267
|
+
|
|
268
|
+
btn.addEventListener('click', function () {
|
|
269
|
+
var isDark = document.body.classList.toggle('swagger-dark');
|
|
270
|
+
localStorage.setItem('swagger-theme', isDark ? 'dark' : 'light');
|
|
271
|
+
btn.textContent = isDark ? '\u2600\uFE0F Light Mode' : '\uD83C\uDF19 Dark Mode';
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
topbarWrapper.appendChild(btn);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
var poll = setInterval(function () {
|
|
278
|
+
if (document.querySelector('.swagger-ui .topbar-wrapper')) {
|
|
279
|
+
injectThemeToggle();
|
|
280
|
+
clearInterval(poll);
|
|
281
|
+
}
|
|
282
|
+
}, 100);
|
|
283
|
+
})();`;
|
|
284
|
+
|
|
285
|
+
SrrComponent = () => ({ css: swaggerDarkCss, js: swaggerDarkJs });
|
|
@@ -51,17 +51,18 @@ const main = () => {
|
|
|
51
51
|
<br />
|
|
52
52
|
<br />${Translate.Render('no-internet-connection')} <br />
|
|
53
53
|
<br />
|
|
54
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
54
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
55
55
|
</div>`,
|
|
56
56
|
);
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
SrrComponent = () =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
SrrComponent = () =>
|
|
60
|
+
html`<script>
|
|
61
|
+
{
|
|
62
|
+
const s = ${s};
|
|
63
|
+
const append = ${append};
|
|
64
|
+
const getLang = ${getLang};
|
|
65
|
+
const main = ${main};
|
|
66
|
+
window.onload = main;
|
|
67
|
+
}
|
|
68
|
+
</script>`;
|
|
@@ -182,17 +182,18 @@ const main = () => {
|
|
|
182
182
|
<span class="bold">Test Page</span>
|
|
183
183
|
<br />
|
|
184
184
|
<br />
|
|
185
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
185
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
186
186
|
</div>`,
|
|
187
187
|
);
|
|
188
188
|
};
|
|
189
189
|
|
|
190
|
-
SrrComponent = () =>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
190
|
+
SrrComponent = () =>
|
|
191
|
+
html`<script>
|
|
192
|
+
{
|
|
193
|
+
const s = ${s};
|
|
194
|
+
const append = ${append};
|
|
195
|
+
const getLang = ${getLang};
|
|
196
|
+
const main = ${main};
|
|
197
|
+
window.onload = main;
|
|
198
|
+
}
|
|
199
|
+
</script>`;
|