underpost 2.8.85 → 2.8.86

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 (52) hide show
  1. package/.env.development +1 -1
  2. package/.env.production +1 -1
  3. package/.env.test +1 -1
  4. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
  5. package/.github/workflows/release.cd.yml +37 -0
  6. package/README.md +7 -44
  7. package/bin/cyberia0.js +78 -0
  8. package/bin/db.js +1 -3
  9. package/bin/deploy.js +13 -350
  10. package/bin/file.js +11 -1
  11. package/cli.md +39 -19
  12. package/manifests/deployment/{dd-template-development → dd-default-development}/deployment.yaml +16 -16
  13. package/manifests/deployment/{dd-template-development → dd-default-development}/proxy.yaml +3 -3
  14. package/manifests/grafana/deployment.yaml +57 -0
  15. package/manifests/grafana/kustomization.yaml +7 -0
  16. package/manifests/grafana/pvc.yaml +12 -0
  17. package/manifests/grafana/service.yaml +14 -0
  18. package/manifests/maas/ssh-cluster-info.sh +14 -0
  19. package/manifests/prometheus/deployment.yaml +82 -0
  20. package/package.json +1 -2
  21. package/src/api/user/user.service.js +8 -34
  22. package/src/cli/cluster.js +41 -2
  23. package/src/cli/cron.js +12 -45
  24. package/src/cli/db.js +149 -0
  25. package/src/cli/deploy.js +20 -81
  26. package/src/cli/index.js +20 -6
  27. package/src/cli/monitor.js +1 -4
  28. package/src/cli/repository.js +12 -5
  29. package/src/cli/run.js +77 -14
  30. package/src/client/Default.index.js +0 -2
  31. package/src/client/components/core/Account.js +6 -2
  32. package/src/client/components/core/Content.js +11 -7
  33. package/src/client/components/core/Css.js +5 -1
  34. package/src/client/components/core/Input.js +6 -1
  35. package/src/client/components/core/LogIn.js +3 -0
  36. package/src/client/components/core/LogOut.js +1 -1
  37. package/src/client/components/core/Modal.js +7 -4
  38. package/src/client/components/core/Recover.js +5 -2
  39. package/src/client/components/core/Scroll.js +65 -120
  40. package/src/client/components/core/SignUp.js +1 -0
  41. package/src/client/components/core/VanillaJs.js +48 -2
  42. package/src/client/components/default/MenuDefault.js +2 -2
  43. package/src/client/components/default/RoutesDefault.js +3 -3
  44. package/src/index.js +1 -1
  45. package/src/mailer/MailerProvider.js +37 -0
  46. package/src/server/client-build-live.js +1 -1
  47. package/src/server/client-dev-server.js +1 -1
  48. package/src/server/conf.js +2 -272
  49. package/src/server/proxy.js +1 -2
  50. package/src/server/start.js +3 -3
  51. package/docker-compose.yml +0 -67
  52. package/prometheus.yml +0 -36
@@ -1,131 +1,76 @@
1
- import { borderChar } from './Css.js';
2
- import { Modal } from './Modal.js';
3
- import { append, s } from './VanillaJs.js';
1
+ import { s } from './VanillaJs.js';
4
2
 
5
- const Scroll = {
6
- data: {},
7
- init: function (selector) {
8
- s(selector).addEventListener('scroll', Scroll.scrollHandler);
9
- Scroll.data[selector] = {
10
- element: s(selector),
3
+ class Scroll {
4
+ /**
5
+ * Attach scroll listener to an element (resolved with s(selector)).
6
+ * @param {string} selector - selector passed to s(selector)
7
+ * @param {function} [callback] - callback function to be called on scroll
8
+ * @param {object} options
9
+ * @param {number} [options.threshold=1] - px margin to treat as bottom
10
+ * @param {number} [options.precision=3] - decimal places for percentages
11
+ */
12
+ static setEvent(selector, callback = async () => {}, options = { threshold: 1, precision: 3 }) {
13
+ const el = s(selector);
14
+ if (!el) return;
15
+
16
+ const threshold = options.threshold ?? 1; // px tolerance for bottom detection
17
+ const precision = options.precision ?? 3;
18
+ let ticking = false;
19
+
20
+ const round = (v) => {
21
+ const m = Math.pow(10, precision);
22
+ return Math.round(v * m) / m;
11
23
  };
12
- return Scroll.data[selector];
13
- },
14
- getScrollPosition: function (selector) {
15
- // Scroll.data[selector].element.clientHeight -
16
- return Scroll.data[selector].element.scrollTop;
17
- },
18
- scrollHandler: async function () {
19
- for (const selector in Scroll.data) await Scroll.data[selector].callback(Scroll.getScrollPosition(selector));
20
- },
21
- addEvent: function (selector = '', callback = (position = 0) => {}) {
22
- Scroll.data[selector].callback = callback;
23
- },
24
- removeEvent: function (selector) {
25
- delete Scroll.data[selector];
26
- },
27
- to: function (elector = '', options = { top: 100, left: 100, behavior: 'smooth' }) {
28
- Scroll.data[selector].element.scrollTo({
29
- top: options.top || Scroll.getScrollPosition(selector),
30
- left: options.left || 0,
31
- behavior: options.behavior || 'smooth',
32
- });
33
- },
34
- topRefreshEvents: {},
35
- addTopRefreshEvent: function (options = { id: '', callback: () => {}, condition: () => {} }) {
36
- this.topRefreshEvents[options.id] = options;
37
- },
38
- removeTopRefreshEvent: function (id = '') {
39
- delete this.topRefreshEvents[id];
40
- },
41
- pullTopRefresh: function () {
42
- return;
43
- append(
44
- 'body',
45
- html` <style>
46
- .pull-refresh-icon-container {
47
- height: 60px;
48
- width: 100%;
49
- z-index: 10;
50
- transition: 0.3s;
51
- left: 0px;
52
- }
53
- .pull-refresh-icon {
54
- width: 60px;
55
- height: 60px;
56
- margin: auto;
57
- color: white;
58
- font-size: 30px;
59
- }
60
- </style>
61
- ${borderChar(2, 'black', [' .pull-refresh-icon-container'])}
62
- <div style="top: -60px" class="abs pull-refresh-icon-container">
63
- <div class="in pull-refresh-icon">
64
- <div class="abs center"><i class="fa-solid fa-arrows-rotate"></i></div>
65
- </div>
66
- </div>`,
67
- );
68
24
 
69
- let touchstartY = 0;
70
- let reload = false;
71
- const minHeightDragReload = 3;
72
- const maxHeightDragReload = 20;
25
+ const listener = (event) => {
26
+ if (ticking) return;
27
+ ticking = true;
28
+
29
+ requestAnimationFrame(() => {
30
+ const scrollHeight = el.scrollHeight;
31
+ const clientHeight = el.clientHeight;
32
+ const scrollTop = el.scrollTop;
33
+
34
+ // pixels left to scroll (clamped to >= 0)
35
+ const remaining = Math.max(0, scrollHeight - clientHeight - scrollTop);
73
36
 
74
- document.addEventListener('touchstart', (e) => {
75
- touchstartY = e.touches[0].clientY;
76
- // console.warn('touchstart', touchstartY);
77
- });
37
+ // maximum possible remaining (0 if content fits without scrolling)
38
+ const maxRemaining = Math.max(0, scrollHeight - clientHeight);
78
39
 
79
- document.addEventListener('touchmove', (e) => {
80
- if (
81
- !Object.keys(Scroll.topRefreshEvents).find((event) => Scroll.topRefreshEvents[event].condition()) ||
82
- (!s(`.btn-bar-center-icon-close`).classList.contains('hide') &&
83
- !s(
84
- `.btn-icon-menu-mode-${Modal.Data['modal-menu'].options.mode !== 'slide-menu-right' ? 'left' : 'right'}`,
85
- ).classList.contains('hide'))
86
- )
87
- return;
40
+ // percentRemaining: 1 = top (all remaining), 0 = bottom (none remaining)
41
+ let percentRemaining = maxRemaining === 0 ? 0 : remaining / maxRemaining;
42
+ percentRemaining = Math.max(0, Math.min(1, percentRemaining));
88
43
 
89
- const touchY = e.touches[0].clientY;
90
- const touchDiff = touchY - touchstartY;
44
+ // percentScrolled: complementary value (0 = top, 1 = bottom)
45
+ let percentScrolled = 1 - percentRemaining;
46
+ percentScrolled = Math.max(0, Math.min(1, percentScrolled));
91
47
 
92
- // console.warn('touchDiff', touchDiff, maxHeightDragReload);
48
+ const payload = {
49
+ scrollHeight,
50
+ clientHeight,
51
+ scrollTop,
52
+ remaining, // px left (>= 0)
53
+ scrollBottom: remaining <= threshold ? 0 : remaining,
54
+ atBottom: remaining <= threshold,
55
+ percentRemaining: round(percentRemaining), // 0..1
56
+ percentScrolled: round(percentScrolled), // 0..1
57
+ };
93
58
 
94
- if (touchDiff > maxHeightDragReload)
95
- s(`.pull-refresh-icon-container`).style.top = 60 + maxHeightDragReload + 'px';
96
- else s(`.pull-refresh-icon-container`).style.top = 60 + touchDiff + 'px';
59
+ // replace this with an event dispatch or callback if you prefer
60
+ // console.warn('scroll', event, JSON.stringify(payload, null, 2));
61
+ callback(payload);
97
62
 
98
- if (touchDiff > minHeightDragReload && window.scrollY === 0) {
99
- reload = true;
100
- } else {
101
- reload = false;
102
- }
103
- });
104
- document.addEventListener('touchend', (e) => {
105
- // console.warn('touchend');
106
- s(`.pull-refresh-icon-container`).style.top = '-60px';
107
- if (reload) {
108
- for (const event of Object.keys(Scroll.topRefreshEvents))
109
- if (Scroll.topRefreshEvents[event].condition()) Scroll.topRefreshEvents[event].callback();
110
- }
111
- reload = false;
112
- });
113
- Scroll.addTopRefreshEvent({
114
- id: 'main-body',
115
- callback: () => {
116
- location.reload();
117
- },
118
- condition: () => {
119
- return (
120
- s('.main-body') &&
121
- s('.main-body').scrollTop === 0 &&
122
- !Object.keys(Modal.Data).find(
123
- (idModal) => !['modal-menu', 'main-body', 'bottom-bar', 'main-body-top'].includes(idModal),
124
- )
125
- );
126
- },
127
- });
128
- },
129
- };
63
+ ticking = false;
64
+ });
65
+ };
66
+
67
+ el.addEventListener('scroll', listener, { passive: true });
68
+
69
+ return {
70
+ removeEvent: () => el.removeEventListener('scroll', listener),
71
+ };
72
+ }
73
+ }
130
74
 
131
75
  export { Scroll };
76
+ export default Scroll;
@@ -53,6 +53,7 @@ const SignUp = {
53
53
  status: result.status,
54
54
  });
55
55
  if (result.status === 'success') {
56
+ await Auth.sessionOut();
56
57
  await Auth.signUpToken(result);
57
58
  s(`.btn-close-${options.idModal}`).click();
58
59
  }
@@ -147,8 +147,40 @@ const pasteData = () => new Promise((resolve) => navigator.clipboard.readText().
147
147
  * @memberof VanillaJS
148
148
  */
149
149
  const setPath = (path = '/', stateStorage = {}, title = '') => {
150
- if (window.location.pathname === path || window.location.pathname === `${path}/`) return;
151
- return history.pushState(stateStorage, title, path);
150
+ if (!path) path = '/';
151
+
152
+ const [inputPath, inputSearch] = `${path}`.split('?');
153
+
154
+ let sanitizedPath = (inputPath[0] !== '/' ? `/${inputPath}` : inputPath)
155
+ .trim()
156
+ .replaceAll('//', '/')
157
+ .replaceAll(`\\`, '/');
158
+
159
+ if (sanitizedPath.length > 1 && sanitizedPath[sanitizedPath.length - 1] === '/')
160
+ sanitizedPath = sanitizedPath.slice(0, -1);
161
+
162
+ if (window.location.pathname === sanitizedPath && (!inputSearch || inputSearch === location.search)) {
163
+ console.warn('Prevent overwriting same path', {
164
+ inputPath: inputPath,
165
+ inputSearch: inputSearch,
166
+ sanitizedPath: sanitizedPath,
167
+ currentLocationSearch: location.search,
168
+ currentLocationHash: location.hash,
169
+ });
170
+ return;
171
+ }
172
+ console.warn('Set path', {
173
+ inputPath: inputPath,
174
+ inputSearch: inputSearch,
175
+ sanitizedPath: sanitizedPath,
176
+ currentLocationSearch: location.search,
177
+ currentLocationHash: location.hash,
178
+ });
179
+ return history.pushState(
180
+ stateStorage,
181
+ title,
182
+ `${sanitizedPath}${inputSearch ? `?${inputSearch}` : ''}${location.hash ?? ''}`,
183
+ );
152
184
  };
153
185
 
154
186
  /**
@@ -425,6 +457,19 @@ const getLang = () =>
425
457
  .slice(0, 2)
426
458
  .toLowerCase();
427
459
 
460
+ function hexToRgbA(hex) {
461
+ let c;
462
+ if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
463
+ c = hex.substring(1).split('');
464
+ if (c.length == 3) {
465
+ c = [c[0], c[0], c[1], c[1], c[2], c[2]];
466
+ }
467
+ c = '0x' + c.join('');
468
+ return [(c >> 16) & 255, (c >> 8) & 255, c & 255];
469
+ }
470
+ throw new Error('Invalid Hex');
471
+ }
472
+
428
473
  export {
429
474
  s,
430
475
  htmls,
@@ -454,4 +499,5 @@ export {
454
499
  isDevInstance,
455
500
  getDataFromInputFile,
456
501
  getLang,
502
+ hexToRgbA,
457
503
  };
@@ -35,7 +35,7 @@ const MenuDefault = {
35
35
  const id = getId(this.Data, 'menu-');
36
36
  this.Data[id] = {};
37
37
  const RouterInstance = RouterDefault();
38
- const { NameApp } = RouterInstance;
38
+ const { BannerAppTemplate } = RouterInstance;
39
39
  const { barConfig } = await Themes[Css.currentTheme]();
40
40
  const heightTopBar = 50;
41
41
  const heightBottomBar = 50;
@@ -162,7 +162,7 @@ const MenuDefault = {
162
162
  </div>
163
163
  `,
164
164
  barConfig: newInstance(barConfig),
165
- title: NameApp,
165
+ title: BannerAppTemplate,
166
166
  // titleClass: 'hide',
167
167
  titleRender: () => {
168
168
  ThemeEvents['titleRender'] = () => {
@@ -4,7 +4,7 @@ import { getProxyPath, s } from '../core/VanillaJs.js';
4
4
 
5
5
  const logger = loggerFactory(import.meta);
6
6
 
7
- const NameApp = html`<strong class="inl" style="font-family: system-ui">PWA</strong>`;
7
+ const BannerAppTemplate = html`<strong class="inl" style="font-family: system-ui">PWA</strong>`;
8
8
 
9
9
  // Router
10
10
  const RoutesDefault = () => {
@@ -39,7 +39,7 @@ const RoutesDefault = () => {
39
39
  window.Routes = RoutesDefault;
40
40
 
41
41
  const RouterDefault = () => {
42
- return { Routes: RoutesDefault, NameApp };
42
+ return { Routes: RoutesDefault, BannerAppTemplate };
43
43
  };
44
44
 
45
- export { RoutesDefault, RouterDefault, NameApp };
45
+ export { RoutesDefault, RouterDefault, BannerAppTemplate };
package/src/index.js CHANGED
@@ -35,7 +35,7 @@ class Underpost {
35
35
  * @type {String}
36
36
  * @memberof Underpost
37
37
  */
38
- static version = 'v2.8.85';
38
+ static version = 'v2.8.86';
39
39
  /**
40
40
  * Repository cli API
41
41
  * @static
@@ -48,6 +48,43 @@ const MailerProvider = {
48
48
  ...options,
49
49
  transporter,
50
50
  templates: await EmailRender.getTemplates(options),
51
+ translateTemplates: {
52
+ confirmEmail: {
53
+ H1: {
54
+ en: 'Confirm Your Email',
55
+ es: 'Confirma tu correo electrónico',
56
+ },
57
+ P1: {
58
+ en: `Email confirmed! Thanks.
59
+ <br />
60
+ <span style="font-size: 12px; color: gray">
61
+ If it is not automatically verified,
62
+ please allow the image to be seen, thank you.
63
+ </span>
64
+ `,
65
+ es: `Correo electrónico confirmado! Gracias.
66
+ <br />
67
+ <span style="font-size: 12px; color: gray">
68
+ Si no se verifica automáticamente, por favor permita que se vea la imagen, gracias.
69
+ </span>
70
+ `,
71
+ },
72
+ },
73
+ recoverEmail: {
74
+ H1: {
75
+ en: 'Recover your account',
76
+ es: 'Recupera tu cuenta',
77
+ },
78
+ P1: {
79
+ en: 'To recover your account, please click the button below:',
80
+ es: 'Para recuperar tu cuenta, haz click en el botón de abajo:',
81
+ },
82
+ BTN_LABEL: {
83
+ en: 'Recover Password',
84
+ es: 'Recuperar Contraseña',
85
+ },
86
+ },
87
+ },
51
88
  };
52
89
 
53
90
  return this.instance[id];
@@ -59,7 +59,7 @@ const clientLiveBuild = async () => {
59
59
  const updates = JSON.parse(fs.readFileSync(`./tmp/client.build.json`, 'utf8'));
60
60
  const liveClientBuildPaths = [];
61
61
  for (let srcPath of updates) {
62
- srcPath = srcPath.replaceAll('/', `\\`); // linux case
62
+ srcPath = srcPath.replaceAll('/', `\\`);
63
63
 
64
64
  const srcBuildPath = `./src${srcPath.split('src')[1].replace(/\\/g, '/')}`;
65
65
  if (
@@ -7,7 +7,7 @@ const logger = loggerFactory(import.meta);
7
7
 
8
8
  const createClientDevServer = () => {
9
9
  // process.argv.slice(2).join(' ')
10
- shellExec(`env-cmd -f .env.development node bin/deploy build-full-client ${process.argv.slice(2).join(' ')} l`);
10
+ shellExec(`env-cmd -f .env.development node bin/deploy build-full-client ${process.argv.slice(2).join(' ')}`);
11
11
  shellExec(
12
12
  `env-cmd -f .env.development node src/api ${process.argv[2]}${process.argv[5] ? ` ${process.argv[5]}` : ''}${
13
13
  process.argv.includes('static') ? ' static' : ''