@underpostnet/underpost 2.8.0 → 2.8.4

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 (91) hide show
  1. package/.dockerignore +1 -0
  2. package/.github/workflows/ghpkg.yml +14 -11
  3. package/.github/workflows/pwa-microservices-template.page.yml +10 -3
  4. package/.vscode/extensions.json +17 -71
  5. package/.vscode/settings.json +12 -5
  6. package/AUTHORS.md +16 -5
  7. package/CHANGELOG.md +63 -3
  8. package/Dockerfile +41 -62
  9. package/README.md +1 -28
  10. package/bin/build.js +278 -0
  11. package/bin/db.js +2 -24
  12. package/bin/deploy.js +107 -71
  13. package/bin/file.js +33 -4
  14. package/bin/index.js +35 -54
  15. package/bin/ssl.js +19 -11
  16. package/bin/util.js +27 -89
  17. package/bin/vs.js +25 -2
  18. package/conf.js +32 -132
  19. package/docker-compose.yml +1 -1
  20. package/manifests/core/kustomization.yaml +11 -0
  21. package/manifests/core/underpost-engine-backup-access.yaml +16 -0
  22. package/manifests/core/underpost-engine-backup-pv-pvc.yaml +22 -0
  23. package/manifests/core/underpost-engine-headless-service.yaml +10 -0
  24. package/manifests/core/underpost-engine-mongodb-backup-cronjob.yaml +40 -0
  25. package/manifests/core/underpost-engine-mongodb-configmap.yaml +26 -0
  26. package/manifests/core/underpost-engine-pv-pvc.yaml +23 -0
  27. package/manifests/core/underpost-engine-statefulset.yaml +91 -0
  28. package/manifests/deployment/mongo-express.yaml +60 -0
  29. package/manifests/deployment/phpmyadmin.yaml +54 -0
  30. package/manifests/kind-config.yaml +12 -0
  31. package/manifests/letsencrypt-prod.yaml +15 -0
  32. package/manifests/mariadb/config.yaml +10 -0
  33. package/manifests/mariadb/kustomization.yaml +9 -0
  34. package/manifests/mariadb/pv.yaml +12 -0
  35. package/manifests/mariadb/pvc.yaml +10 -0
  36. package/manifests/mariadb/secret.yaml +8 -0
  37. package/manifests/mariadb/service.yaml +10 -0
  38. package/manifests/mariadb/statefulset.yaml +55 -0
  39. package/manifests/valkey/kustomization.yaml +7 -0
  40. package/manifests/valkey/underpost-engine-valkey-service.yaml +17 -0
  41. package/manifests/valkey/underpost-engine-valkey-statefulset.yaml +39 -0
  42. package/package.json +16 -35
  43. package/src/api/user/user.model.js +16 -3
  44. package/src/api/user/user.service.js +1 -1
  45. package/src/client/components/core/Account.js +4 -2
  46. package/src/client/components/core/Auth.js +2 -2
  47. package/src/client/components/core/CalendarCore.js +115 -49
  48. package/src/client/components/core/CommonJs.js +150 -19
  49. package/src/client/components/core/Css.js +1 -1
  50. package/src/client/components/core/CssCore.js +6 -0
  51. package/src/client/components/core/Docs.js +2 -1
  52. package/src/client/components/core/DropDown.js +5 -1
  53. package/src/client/components/core/Input.js +17 -3
  54. package/src/client/components/core/JoyStick.js +8 -5
  55. package/src/client/components/core/Modal.js +17 -11
  56. package/src/client/components/core/Panel.js +85 -25
  57. package/src/client/components/core/PanelForm.js +11 -19
  58. package/src/client/components/core/SignUp.js +4 -1
  59. package/src/client/components/core/Translate.js +57 -9
  60. package/src/client/components/core/Validator.js +9 -1
  61. package/src/client/public/default/plantuml/client-conf.svg +1 -1
  62. package/src/client/public/default/plantuml/server-conf.svg +1 -1
  63. package/src/client/public/default/plantuml/server-schema.svg +1 -1
  64. package/src/client/public/default/plantuml/ssr-conf.svg +1 -1
  65. package/src/client/public/default/plantuml/ssr-schema.svg +1 -1
  66. package/src/client/services/core/core.service.js +2 -0
  67. package/src/client/services/default/default.management.js +4 -2
  68. package/src/client/ssr/body/CacheControl.js +2 -1
  69. package/src/client/ssr/body/DefaultSplashScreen.js +3 -3
  70. package/src/client/ssr/offline/Maintenance.js +63 -0
  71. package/src/client/sw/default.sw.js +23 -3
  72. package/src/db/mongo/MongooseDB.js +13 -1
  73. package/src/index.js +15 -0
  74. package/src/runtime/lampp/Lampp.js +1 -13
  75. package/src/runtime/xampp/Xampp.js +0 -13
  76. package/src/server/auth.js +3 -3
  77. package/src/server/client-build.js +8 -17
  78. package/src/server/client-icons.js +1 -1
  79. package/src/server/conf.js +299 -32
  80. package/src/server/dns.js +2 -3
  81. package/src/server/logger.js +18 -11
  82. package/src/server/network.js +0 -36
  83. package/src/server/process.js +25 -2
  84. package/src/server/project.js +39 -0
  85. package/src/server/proxy.js +4 -26
  86. package/src/server/runtime.js +6 -7
  87. package/src/server/ssl.js +1 -1
  88. package/src/server/valkey.js +3 -0
  89. package/startup.cjs +12 -0
  90. package/src/server/prompt-optimizer.js +0 -28
  91. package/startup.js +0 -11
@@ -0,0 +1,63 @@
1
+ const s = (el) => document.querySelector(el);
2
+
3
+ const append = (el, html) => s(el).insertAdjacentHTML('beforeend', html);
4
+
5
+ const getLang = () =>
6
+ (localStorage.getItem('lang') || navigator.language || navigator.userLanguage || s('html').lang)
7
+ .slice(0, 2)
8
+ .toLowerCase();
9
+
10
+ const main = () => {
11
+ const Translate = {
12
+ Data: {
13
+ ['server-maintenance']: {
14
+ en: "The server is under maintenance <br> we'll be back soon.",
15
+ es: 'El servidor está en mantenimiento <br> volveremos pronto.',
16
+ },
17
+ },
18
+ Render: function (id) {
19
+ return this.Data[id][getLang()] ? this.Data[id][getLang()] : this.Data[id]['en'];
20
+ },
21
+ };
22
+ const icon = html`<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 24 24">
23
+ <path
24
+ fill="none"
25
+ stroke="currentColor"
26
+ stroke-linecap="round"
27
+ stroke-linejoin="round"
28
+ stroke-width="2"
29
+ d="M3 7a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3zm9 13H6a3 3 0 0 1-3-3v-2a3 3 0 0 1 3-3h10.5m-.5 6a2 2 0 1 0 4 0a2 2 0 1 0-4 0m2-3.5V16m0 4v1.5m3.032-5.25l-1.299.75m-3.463 2l-1.3.75m0-3.5l1.3.75m3.463 2l1.3.75M7 8v.01M7 16v.01"
30
+ />
31
+ </svg>`;
32
+
33
+ append(
34
+ 'body',
35
+ html` <style>
36
+ body {
37
+ font-family: arial;
38
+ font-size: 20px;
39
+ background-color: #d8d8d8;
40
+ color: #333;
41
+ }
42
+ a {
43
+ color: black;
44
+ }
45
+ </style>
46
+
47
+ <div class="abs center" style="top: 45%">
48
+ ${icon}
49
+ <br />
50
+ <br />${Translate.Render('server-maintenance')}
51
+ </div>`,
52
+ );
53
+ };
54
+
55
+ SrrComponent = () => html`<script>
56
+ {
57
+ const s = ${s};
58
+ const append = ${append};
59
+ const getLang = ${getLang};
60
+ const main = ${main};
61
+ window.onload = main;
62
+ }
63
+ </script>`;
@@ -62,15 +62,35 @@ self.addEventListener('fetch', (event) => {
62
62
  if (!preCachedResponse) throw new Error(error.message);
63
63
  return preCachedResponse;
64
64
  } catch (error) {
65
- console.error('Error opening cache for pre cached page', event.request.url, error);
65
+ console.error('Error opening cache for pre cached page', {
66
+ url: event.request.url,
67
+ error,
68
+ onLine: navigator.onLine,
69
+ });
66
70
  try {
71
+ if (!navigator.onLine) {
72
+ if (event.request.method.toUpperCase() === 'GET') {
73
+ const cache = await caches.open(CACHE_NAME);
74
+ const preCachedResponse = await cache.match(
75
+ `${PROXY_PATH === '/' ? '' : PROXY_PATH}/offline/index.html`,
76
+ );
77
+ if (!preCachedResponse) throw new Error(error.message);
78
+ return preCachedResponse;
79
+ }
80
+ const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
81
+ // response.status = 200;
82
+ response.headers.set('Content-Type', 'application/json');
83
+ return response;
84
+ }
67
85
  if (event.request.method.toUpperCase() === 'GET') {
68
86
  const cache = await caches.open(CACHE_NAME);
69
- const preCachedResponse = await cache.match(`${PROXY_PATH === '/' ? '' : PROXY_PATH}/offline/index.html`);
87
+ const preCachedResponse = await cache.match(
88
+ `${PROXY_PATH === '/' ? '' : PROXY_PATH}/maintenance/index.html`,
89
+ );
70
90
  if (!preCachedResponse) throw new Error(error.message);
71
91
  return preCachedResponse;
72
92
  }
73
- const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
93
+ const response = new Response(JSON.stringify({ status: 'error', message: 'server in maintenance' }));
74
94
  // response.status = 200;
75
95
  response.headers.set('Content-Type', 'application/json');
76
96
  return response;
@@ -9,7 +9,12 @@ const MongooseDB = {
9
9
  connect: async (host, name) => {
10
10
  const uri = `${host}/${name}`;
11
11
  // logger.info('MongooseDB connect', { host, name, uri });
12
- return await mongoose.createConnection(uri).asPromise();
12
+ return await mongoose
13
+ .createConnection(uri, {
14
+ // useNewUrlParser: true,
15
+ // useUnifiedTopology: true,
16
+ })
17
+ .asPromise();
13
18
  return new Promise((resolve, reject) =>
14
19
  mongoose
15
20
  .connect(
@@ -67,8 +72,15 @@ const MongooseDB = {
67
72
  shellExec(`sudo rm -r /var/lib/mongodb`);
68
73
  // restore lib
69
74
  // shellExec(`sudo chown -R mongodb:mongodb /var/lib/mongodb/*`);
75
+ // mongod --repair
70
76
 
71
77
  if (process.argv.includes('legacy')) {
78
+ // TODO:
79
+ if (process.argv.includes('rocky')) {
80
+ // https://github.com/mongodb/mongodb-selinux
81
+ // https://www.mongodb.com/docs/v7.0/tutorial/install-mongodb-enterprise-on-red-hat/
82
+ shellExec(`sudo chown -R mongod:mongod /var/lib/mongo`);
83
+ }
72
84
  logger.info('install legacy 4.4');
73
85
  shellExec(`wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -`);
74
86
 
package/src/index.js CHANGED
@@ -4,7 +4,9 @@
4
4
  * @namespace Underpost
5
5
  */
6
6
 
7
+ import { runTest } from './server/conf.js';
7
8
  import { loggerFactory, setUpInfo } from './server/logger.js';
9
+ import Project from './server/project.js';
8
10
 
9
11
  const logger = loggerFactory(import.meta);
10
12
 
@@ -14,6 +16,15 @@ const logger = loggerFactory(import.meta);
14
16
  * @memberof Underpost
15
17
  */
16
18
  class Underpost {
19
+ /**
20
+ * Underpost engine version
21
+ * @static
22
+ * @type {String}
23
+ * @memberof Underpost
24
+ */
25
+ static version = 'v2.8.4';
26
+ static project = Project;
27
+
17
28
  constructor() {}
18
29
 
19
30
  /**
@@ -32,6 +43,10 @@ class Underpost {
32
43
  static async setUpInfo() {
33
44
  return await setUpInfo(logger);
34
45
  }
46
+
47
+ static runTest() {
48
+ return runTest(Underpost.version);
49
+ }
35
50
  }
36
51
 
37
52
  const up = Underpost;
@@ -1,5 +1,4 @@
1
1
  import fs from 'fs-extra';
2
- import { network } from '../../server/network.js';
3
2
  import { shellCd, shellExec } from '../../server/process.js';
4
3
  import { timer } from '../../client/components/core/CommonJs.js';
5
4
  import { loggerFactory } from '../../server/logger.js';
@@ -39,20 +38,9 @@ const Lampp = {
39
38
  );
40
39
 
41
40
  shellExec(cmd);
42
- await network.port.portClean(3306);
43
- for (const port of this.ports) await network.port.portClean(port);
44
41
  cmd = `sudo /opt/lampp/lampp start`;
45
42
  if (this.router) fs.writeFileSync(`./tmp/lampp-router.conf`, this.router, 'utf-8');
46
- shellExec(cmd, { async: true });
47
- if (options && options.daemon) this.daemon();
48
- },
49
- daemon: async function () {
50
- await timer(1000 * 60 * 2); // 2 minutes
51
- for (const port of this.ports) {
52
- const [portStatus] = await network.port.status([port]);
53
- if (!portStatus.open) return await this.initService();
54
- }
55
- this.daemon();
43
+ shellExec(cmd);
56
44
  },
57
45
  enabled: () => fs.existsSync(`/opt/lampp/apache2/conf/httpd.conf`),
58
46
  appendRouter: function (render) {
@@ -1,7 +1,5 @@
1
1
  import fs from 'fs-extra';
2
- import { network } from '../../server/network.js';
3
2
  import { shellExec } from '../../server/process.js';
4
- import { timer } from '../../client/components/core/CommonJs.js';
5
3
 
6
4
  const Xampp = {
7
5
  ports: [],
@@ -16,20 +14,9 @@ const Xampp = {
16
14
  fs.writeFileSync(`C:/xampp/apache/conf/extra/httpd-ssl.conf`, this.router || '', 'utf8');
17
15
  cmd = `C:/xampp/xampp_stop.exe`;
18
16
  shellExec(cmd);
19
- await network.port.portClean(3306);
20
- for (const port of this.ports) await network.port.portClean(port);
21
17
  cmd = `C:/xampp/xampp_start.exe`;
22
18
  if (this.router) fs.writeFileSync(`./tmp/xampp-router.conf`, this.router, 'utf-8');
23
19
  shellExec(cmd);
24
- if (options && options.daemon) this.daemon();
25
- },
26
- daemon: async function () {
27
- await timer(1000 * 60 * 2); // 2 minutes
28
- for (const port of this.ports) {
29
- const [portStatus] = await network.port.status([port]);
30
- if (!portStatus.open) return await this.initService();
31
- }
32
- this.daemon();
33
20
  },
34
21
  enabled: () => fs.existsSync(`C:/xampp/apache/conf/httpd.conf`),
35
22
  appendRouter: function (render) {
@@ -9,7 +9,7 @@ import jwt from 'jsonwebtoken';
9
9
  import { loggerFactory } from './logger.js';
10
10
  import crypto from 'crypto';
11
11
  import { userRoleEnum } from '../api/user/user.model.js';
12
- import { validatePassword } from '../client/components/core/CommonJs.js';
12
+ import { commonAdminGuard, commonModeratorGuard, validatePassword } from '../client/components/core/CommonJs.js';
13
13
 
14
14
  dotenv.config();
15
15
 
@@ -162,7 +162,7 @@ const authMiddleware = (req, res, next) => {
162
162
  */
163
163
  const adminGuard = (req, res, next) => {
164
164
  try {
165
- if (!(userRoleEnum.indexOf(req.auth.user.role) === userRoleEnum.indexOf('admin')))
165
+ if (!commonAdminGuard(req.auth.user.role))
166
166
  return res.status(403).json({ status: 'error', message: 'Insufficient permission' });
167
167
  return next();
168
168
  } catch (error) {
@@ -194,7 +194,7 @@ const adminGuard = (req, res, next) => {
194
194
  */
195
195
  const moderatorGuard = (req, res, next) => {
196
196
  try {
197
- if (!(userRoleEnum.indexOf(req.auth.user.role) <= userRoleEnum.indexOf('moderator')))
197
+ if (!commonModeratorGuard(req.auth.user.role))
198
198
  return res.status(403).json({ status: 'error', message: 'Insufficient permission' });
199
199
  return next();
200
200
  } catch (error) {
@@ -50,10 +50,10 @@ const fullBuild = async ({
50
50
  buildAcmeChallengePath(acmeChallengeFullPath);
51
51
 
52
52
  if (publicClientId && publicClientId.startsWith('html-website-templates')) {
53
- if (!fs.existsSync(`/dd/html-website-templates/`))
54
- shellExec(`cd /dd && git clone https://github.com/designmodo/html-website-templates.git`);
53
+ if (!fs.existsSync(`/home/dd/html-website-templates/`))
54
+ shellExec(`cd /home/dd && git clone https://github.com/designmodo/html-website-templates.git`);
55
55
  if (!fs.existsSync(`${rootClientPath}/index.php`)) {
56
- fs.copySync(`/dd/html-website-templates/${publicClientId.split('-publicClientId-')[1]}`, rootClientPath);
56
+ fs.copySync(`/home/dd/html-website-templates/${publicClientId.split('-publicClientId-')[1]}`, rootClientPath);
57
57
  shellExec(`cd ${rootClientPath} && git init && git add . && git commit -m "Base template implementation"`);
58
58
  // git remote add origin git@github.com:<username>/<repo>.git
59
59
  fs.writeFileSync(`${rootClientPath}/.git/.htaccess`, `Deny from all`, 'utf8');
@@ -465,10 +465,11 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
465
465
  case 'DefaultSplashScreen':
466
466
  if (backgroundImage) {
467
467
  ssrHeadComponents += SrrComponent({
468
- base64BackgroundImage: `data:image/${backgroundImage.split('.').pop()};base64,${fs
469
- .readFileSync(backgroundImage)
470
- .toString('base64')}`,
468
+ backgroundImage: (path === '/' ? path : `${path}/`) + backgroundImage,
471
469
  });
470
+ // `data:image/${backgroundImage.split('.').pop()};base64,${fs
471
+ // .readFileSync()
472
+ // .toString('base64')}`,
472
473
  break;
473
474
  } else {
474
475
  ssrHeadComponents += SrrComponent({ metadata });
@@ -480,7 +481,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
480
481
  bgColor: metadata?.themeColor ? metadata.themeColor : '#ececec',
481
482
  });
482
483
  ssrHeadComponents += SrrComponent({
483
- base64BackgroundImage: `data:image/png;base64,${bufferBackgroundImage.toString('base64')}`,
484
+ backgroundImage: `data:image/png;base64,${bufferBackgroundImage.toString('base64')}`,
484
485
  });
485
486
  }
486
487
 
@@ -544,17 +545,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
544
545
  let siteMapSrc = await new Promise((resolve) =>
545
546
  streamToPromise(Readable.from(siteMapLinks).pipe(siteMapStream)).then((data) => resolve(data.toString())),
546
547
  );
547
- switch (publicClientId) {
548
- case 'underpost':
549
- siteMapSrc = siteMapSrc.replaceAll(
550
- `</urlset>`,
551
- `${fs.readFileSync(`./src/client/public/underpost/sitemap-template.txt`, 'utf8')} </urlset>`,
552
- );
553
- break;
554
548
 
555
- default:
556
- break;
557
- }
558
549
  // Return a promise that resolves with your XML string
559
550
  fs.writeFileSync(`${rootClientPath}/sitemap.xml`, siteMapSrc, 'utf8');
560
551
  if (xslUrl)
@@ -141,7 +141,7 @@ const buildIcons = async ({
141
141
  for (const file of response.files)
142
142
  fs.writeFileSync(`./src/client/public/${publicClientId}/${file.name}`, file.contents, 'utf8');
143
143
 
144
- const ssrPath = `./src/client/ssr/components/head/Pwa${getCapVariableName(publicClientId)}.js`;
144
+ const ssrPath = `./src/client/ssr/head/Pwa${getCapVariableName(publicClientId)}.js`;
145
145
  if (!fs.existsSync(ssrPath))
146
146
  fs.writeFileSync(ssrPath, 'SrrComponent = () => html`' + response.html.join(`\n`) + '`;', 'utf8');
147
147
  } catch (error) {