underpost 3.1.1 → 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.
@@ -144,8 +144,6 @@ class UnderpostRepository {
144
144
  }
145
145
 
146
146
  if (options.changelog !== undefined || options.changelogBuild) {
147
- const ciIntegrationPrefix = 'ci(package-pwa-microservices-';
148
- const ciIntegrationVersionPrefix = 'New release v:';
149
147
  const releaseMatch = 'New release v:';
150
148
  // Helper: parse [<tag>] commits into grouped sections
151
149
  const buildSectionChangelog = (commits) => {
@@ -273,39 +271,13 @@ class UnderpostRepository {
273
271
  fs.writeFileSync(changelogPath, `# Changelog\n\n${changelog}`);
274
272
  logger.info('CHANGELOG.md built at', changelogPath);
275
273
  } else {
276
- // --changelog [latest-n]: print changelog of last N commits or since last release
274
+ // --changelog [latest-n]: print changelog of last N commits (default: 1)
277
275
  const hasExplicitCount =
278
276
  options.changelog !== undefined && options.changelog !== true && !isNaN(parseInt(options.changelog));
279
- const scanLimit = hasExplicitCount ? parseInt(options.changelog) : 500;
277
+ const scanLimit = hasExplicitCount ? parseInt(options.changelog) : 1;
280
278
  const allCommits = fetchHistory(scanLimit);
281
279
 
282
- let commits;
283
- if (!hasExplicitCount) {
284
- let boundaryIndex = -1;
285
-
286
- // New boundary: deploy hash stored in config via `underpost config set LAST_CI_DEPLOY_HASH`
287
- const lastDeployHash = shellExec('underpost config get LAST_CI_DEPLOY_HASH --plain', {
288
- stdout: true,
289
- silent: true,
290
- disableLog: true,
291
- }).trim();
292
- if (lastDeployHash && lastDeployHash !== 'undefined' && lastDeployHash !== 'null') {
293
- boundaryIndex = allCommits.findIndex((c) => c.fullHash === lastDeployHash || c.hash === lastDeployHash);
294
- }
295
-
296
- // Fallback: old CI integration commit boundary
297
- if (boundaryIndex < 0) {
298
- boundaryIndex = allCommits.findIndex(
299
- (c) => c.message.startsWith(ciIntegrationPrefix) && !c.message.match(ciIntegrationVersionPrefix),
300
- );
301
- }
302
-
303
- commits = boundaryIndex >= 0 ? allCommits.slice(0, boundaryIndex) : allCommits;
304
- } else {
305
- commits = allCommits;
306
- }
307
-
308
- const sections = buildVersionSections(commits);
280
+ const sections = buildVersionSections(allCommits);
309
281
  let changelog = renderSections(sections);
310
282
  console.log(changelog || `No changelog entries found.\n`);
311
283
  }
@@ -624,6 +596,9 @@ class UnderpostRepository {
624
596
  * @param {object} [options] - Build options.
625
597
  * @param {boolean} [options.syncEnvPort=false] - If true, syncs environment port assignments across all deploy IDs.
626
598
  * @param {boolean} [options.singleReplica=false] - If true, builds single replica folders instead of full client.
599
+ * @param {boolean} [options.buildZip=false] - If true, creates zip files of the builds.
600
+ * @param {boolean} [options.liteBuild=false] - If true, skips full build (default is full build).
601
+ * @param {boolean} [options.iconsBuild=false] - If true, builds icons.
627
602
  * @returns {Promise<boolean>} A promise that resolves when the build is complete.
628
603
  * @memberof UnderpostRepository
629
604
  */
@@ -635,6 +610,9 @@ class UnderpostRepository {
635
610
  options = {
636
611
  syncEnvPort: false,
637
612
  singleReplica: false,
613
+ buildZip: false,
614
+ liteBuild: false,
615
+ iconsBuild: false,
638
616
  },
639
617
  ) {
640
618
  return new Promise(async (resolve, reject) => {
@@ -784,8 +762,6 @@ class UnderpostRepository {
784
762
  if (argHost.length && argPath.length && (!argHost.includes(host) || !argPath.includes(path))) {
785
763
  delete serverConf[host][path];
786
764
  } else {
787
- serverConf[host][path].liteBuild = false;
788
- serverConf[host][path].minifyBuild = process.env.NODE_ENV === 'production' ? true : false;
789
765
  if (serverConf[host][path].singleReplica && serverConf[host][path].replicas) {
790
766
  singleReplicaHosts.push({ host, path });
791
767
  deployIdSingleReplicas = deployIdSingleReplicas.concat(
@@ -797,23 +773,13 @@ class UnderpostRepository {
797
773
  }
798
774
  }
799
775
  }
800
- if (confFilePath) fs.writeFileSync(confFilePath, JSON.stringify(serverConf, null, 4), 'utf-8');
801
- await buildClient();
802
- if (confFilePath && originalConfBackup) fs.writeFileSync(confFilePath, originalConfBackup, 'utf-8');
803
-
804
- if (singleReplicaHosts.length > 0) {
805
- for (const { host, path } of singleReplicaHosts) {
806
- await Underpost.repo.client(resolvedDeployId, '', host, path, {
807
- singleReplica: true,
808
- });
809
- }
810
- shellExec(`node bin env ${resolvedDeployId} ${process.env.NODE_ENV}`);
811
- }
776
+ await buildClient({
777
+ buildZip: options.buildZip || false,
778
+ fullBuild: options.liteBuild ? false : true,
779
+ iconsBuild: options.iconsBuild || false,
780
+ });
781
+ for (const replicaDeployId of deployIdSingleReplicas) await Underpost.repo.client(replicaDeployId);
812
782
 
813
- for (const replicaDeployId of deployIdSingleReplicas) {
814
- shellExec(`node bin env ${replicaDeployId} ${process.env.NODE_ENV}`);
815
- await Underpost.repo.client(replicaDeployId);
816
- }
817
783
  return resolve(true);
818
784
  }
819
785
  } catch (error) {
@@ -1218,6 +1184,29 @@ Prevent build private config repo.`,
1218
1184
  }
1219
1185
  logger.info('Dispatched workflow', `${repo} -> ${workflowFile}`, inputs.job ? `(job: ${inputs.job})` : '');
1220
1186
  },
1187
+
1188
+ /**
1189
+ * Sanitizes a markdown changelog string into a compact message format.
1190
+ * Strips date headers, converts section tags to `[tag]` prefixes, removes bullet markers and special characters.
1191
+ * @param {string} message - The raw markdown changelog output.
1192
+ * @returns {string} The sanitized single-line or multi-line compact message.
1193
+ * @memberof UnderpostRepository
1194
+ */
1195
+ sanitizeChangelogMessage(message) {
1196
+ if (!message) return '';
1197
+ return message
1198
+ .replace(/^##\s+\d{4}-\d{2}-\d{2}\s*/gm, '')
1199
+ .replace(/^###\s+(\S+)\s*/gm, '[$1] ')
1200
+ .replace(/^- /gm, '')
1201
+ .replaceAll('"', '')
1202
+ .replaceAll('`', '')
1203
+ .split('\n')
1204
+ .map((l) => l.trim())
1205
+ .filter(Boolean)
1206
+ .join('\n')
1207
+ .trim()
1208
+ .replaceAll('] - ', '] ');
1209
+ },
1221
1210
  };
1222
1211
  }
1223
1212
 
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`, and optionally dispatches CD sync workflow.
358
- * @param {string} path - The input value, identifier, or path for the operation.
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
- const message = shellExec(`node bin cmt --changelog --changelog-no-hash`, { silent: true, stdout: true }).trim();
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
- // 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
-
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
- // Dispatch CI workflow instead of empty commit + push
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: sanitizedMessage ? { message: sanitizedMessage } : {},
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
  /**
@@ -612,7 +611,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
612
611
  image ? ` --image ${image}` : ''
613
612
  }${versions ? ` --versions ${versions}` : ''}${
614
613
  options.namespace ? ` --namespace ${options.namespace}` : ''
615
- }${timeoutFlags}${cmdString} dd ${env}`,
614
+ }${timeoutFlags}${cmdString} ${deployId} ${env}`,
616
615
  );
617
616
 
618
617
  if (isDeployRunnerContext(path, options)) {
@@ -1573,29 +1572,75 @@ EOF
1573
1572
  },
1574
1573
 
1575
1574
  /**
1576
- * @method ptls
1577
- * @description Set on ~/.bashrc alias: ports <port> Command to list listening ports that match the given keyword.
1578
- * @param {string} path - The input value, identifier, or path for the operation (used as a keyword to filter listening ports).
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.
1579
1578
  * @param {Object} options - The default underpost runner options for customizing workflow
1580
1579
  * @memberof UnderpostRun
1581
1580
  */
1582
- ptls: async (path = '', options = DEFAULT_OPTION) => {
1583
- shellExec(`chmod +x ${options.underpostRoot}/scripts/ports-ls.sh`);
1584
- shellExec(`${options.underpostRoot}/scripts/ports-ls.sh`);
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}`);
1585
1612
  },
1613
+
1586
1614
  /**
1587
- * @method release-cmt
1588
- * @description Commits and pushes a new release for the `engine` repository with a message indicating the new version.
1589
- * @param {string} path - The input value, identifier, or path for the operation.
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').
1590
1618
  * @param {Object} options - The default underpost runner options for customizing workflow
1591
1619
  * @memberof UnderpostRun
1592
1620
  */
1593
- 'release-cmt': async (path, options = DEFAULT_OPTION) => {
1594
- shellExec(`underpost run pull`);
1595
- shellExec(`underpost run secret`);
1596
- shellCd(`/home/dd/engine`);
1597
- shellExec(`underpost cmt --empty . ci engine ' New engine release $(underpost --version)'`);
1598
- shellExec(`underpost push . ${process.env.GITHUB_USERNAME}/engine`, { silent: true });
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
1636
+ * @description Set on ~/.bashrc alias: ports <port> Command to list listening ports that match the given keyword.
1637
+ * @param {string} path - The input value, identifier, or path for the operation (used as a keyword to filter listening ports).
1638
+ * @param {Object} options - The default underpost runner options for customizing workflow
1639
+ * @memberof UnderpostRun
1640
+ */
1641
+ ports: async (path = '', options = DEFAULT_OPTION) => {
1642
+ shellExec(`chmod +x ${options.underpostRoot}/scripts/ports-ls.sh`);
1643
+ shellExec(`${options.underpostRoot}/scripts/ports-ls.sh`);
1599
1644
  },
1600
1645
 
1601
1646
  /**
@@ -1623,40 +1668,6 @@ EOF
1623
1668
  shellExec(`node bin run sync${baseClusterCommand} --deploy-id-cron-jobs none dd-test --cmd "${cmd}"`);
1624
1669
  },
1625
1670
 
1626
- /**
1627
- * @method sync-replica
1628
- * @description Syncs a replica for the dd.router
1629
- * @param {string} path - The input value, identifier, or path for the operation.
1630
- * @param {Object} options - The default underpost runner options for customizing workflow
1631
- * @memberof UnderpostRun
1632
- */
1633
- 'sync-replica': async (path, options = DEFAULT_OPTION) => {
1634
- const env = options.dev ? 'development' : 'production';
1635
- const baseCommand = options.dev ? 'node bin' : 'underpost';
1636
-
1637
- for (let deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
1638
- deployId = deployId.trim();
1639
- const _path = '/single-replica';
1640
- const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
1641
- shellExec(`${baseCommand} env ${deployId} ${env}`);
1642
- for (const host of Object.keys(confServer))
1643
- if (_path in confServer[host])
1644
- await Underpost.repo.client(deployId, '', host, _path, {
1645
- singleReplica: true,
1646
- });
1647
- const node = options.nodeName
1648
- ? options.nodeName
1649
- : !options.kubeadm && !options.k3s
1650
- ? 'kind-control-plane'
1651
- : os.hostname();
1652
- // deployId, replicas, versions, image, node
1653
- let defaultPath = [deployId, 1, ``, ``, node];
1654
- shellExec(`${baseCommand} run${options.dev === true ? ' --dev' : ''} --build sync ${defaultPath}`);
1655
- await Underpost.repo.client(deployId);
1656
- }
1657
- if (isDeployRunnerContext(path, options)) shellExec(`${baseCommand} run promote ${path} production`);
1658
- },
1659
-
1660
1671
  /**
1661
1672
  * @method tf-vae-test
1662
1673
  * @description Creates and runs a job pod (`tf-vae-test`) that installs TensorFlow dependencies, clones the TensorFlow docs, and runs the CVAE tutorial script, with a terminal monitor attached.
@@ -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 !important;
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" style="top: 45%">
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 = () => html`<script>
66
- {
67
- const s = ${s};
68
- const append = ${append};
69
- const getLang = ${getLang};
70
- const main = ${main};
71
- window.onload = main;
72
- }
73
- </script>`;
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" style="top: 45%">
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 = () => html`<script>
65
- {
66
- const s = ${s};
67
- const append = ${append};
68
- const getLang = ${getLang};
69
- const main = ${main};
70
- window.onload = main;
71
- }
72
- </script>`;
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>`;