underpost 2.8.848 → 2.8.852

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/README.md CHANGED
@@ -42,6 +42,10 @@ template
42
42
 
43
43
 
44
44
 
45
+
46
+
47
+
48
+
45
49
 
46
50
 
47
51
 
@@ -50,7 +54,7 @@ template
50
54
  <!-- badges -->
51
55
 
52
56
 
53
- [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/2.8.848)](https://socket.dev/npm/package/underpost/overview/2.8.848) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
57
+ [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/2.8.852)](https://socket.dev/npm/package/underpost/overview/2.8.852) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
54
58
 
55
59
 
56
60
  <!-- end-badges -->
@@ -75,6 +79,10 @@ template
75
79
 
76
80
 
77
81
 
82
+
83
+
84
+
85
+
78
86
 
79
87
 
80
88
 
@@ -124,7 +132,7 @@ Run dev client server
124
132
  npm run dev
125
133
  ```
126
134
  <!-- -->
127
- ## underpost ci/cd cli v2.8.848
135
+ ## underpost ci/cd cli v2.8.852
128
136
 
129
137
  ### Usage: `underpost [options] [command]`
130
138
  ```
package/cli.md CHANGED
@@ -1,4 +1,4 @@
1
- ## underpost ci/cd cli v2.8.848
1
+ ## underpost ci/cd cli v2.8.852
2
2
 
3
3
  ### Usage: `underpost [options] [command]`
4
4
  ```
@@ -58,7 +58,7 @@ services:
58
58
  cpus: '0.25'
59
59
  memory: 20M
60
60
  labels: # labels in Compose file instead of Dockerfile
61
- engine.version: '2.8.848'
61
+ engine.version: '2.8.852'
62
62
  networks:
63
63
  - load-balancer
64
64
 
@@ -17,7 +17,7 @@ spec:
17
17
  spec:
18
18
  containers:
19
19
  - name: dd-template-development-blue
20
- image: localhost/rockylinux9-underpost:v2.8.848
20
+ image: localhost/rockylinux9-underpost:v2.8.852
21
21
  # resources:
22
22
  # requests:
23
23
  # memory: "124Ki"
@@ -100,7 +100,7 @@ spec:
100
100
  spec:
101
101
  containers:
102
102
  - name: dd-template-development-green
103
- image: localhost/rockylinux9-underpost:v2.8.848
103
+ image: localhost/rockylinux9-underpost:v2.8.852
104
104
  # resources:
105
105
  # requests:
106
106
  # memory: "124Ki"
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "2.8.848",
5
+ "version": "2.8.852",
6
6
  "description": "pwa api rest template",
7
7
  "scripts": {
8
8
  "start": "env-cmd -f .env.production node --max-old-space-size=8192 src/server",
@@ -31,20 +31,10 @@ const UserService = {
31
31
  const token = hashJWT({ email: req.body.email }, '15m');
32
32
  const payloadToken = hashJWT({ email: req.body.email }, '15m');
33
33
  const id = `${options.host}${options.path}`;
34
- const translate = {
35
- H1: {
36
- en: 'Recover your account',
37
- es: 'Recupera tu cuenta',
38
- },
39
- P1: {
40
- en: 'To recover your account, please click the button below:',
41
- es: 'Para recuperar tu cuenta, haz click en el botón de abajo:',
42
- },
43
- BTN_LABEL: {
44
- en: 'Recover Password',
45
- es: 'Recuperar Contraseña',
46
- },
47
- };
34
+ const translate = MailerProvider.instance[id].translateTemplates.recoverEmail;
35
+ const recoverUrl = `${process.env.NODE_ENV === 'development' ? 'http://' : 'https://'}${req.body.hostname}${
36
+ req.body.proxyPath
37
+ }recover?payload=${payloadToken}`;
48
38
  const sendResult = await MailerProvider.send({
49
39
  id,
50
40
  sendOptions: {
@@ -55,13 +45,8 @@ const UserService = {
55
45
  .replace('{{H1}}', translate.H1[req.lang])
56
46
  .replace('{{P1}}', translate.P1[req.lang])
57
47
  .replace('{{TOKEN}}', token)
58
- .replace(`{{COMPANY}}`, options.host) // html body
59
- .replace(
60
- '{{RECOVER_WEB_URL}}',
61
- `${process.env === 'development' ? 'http://' : 'https://'}${options.host}${options.path}${
62
- options.path === '/' ? 'recover' : `/recover`
63
- }?payload=${payloadToken}`,
64
- )
48
+ .replace(`{{COMPANY}}`, req.body.hostname) // html body
49
+ .replace('{{RECOVER_WEB_URL}}', recoverUrl)
65
50
  .replace('{{RECOVER_BTN_LABEL}}', translate.BTN_LABEL[req.lang]),
66
51
 
67
52
  attachments: [
@@ -98,18 +83,7 @@ const UserService = {
98
83
  },
99
84
  );
100
85
  }
101
- const translate = {
102
- H1: {
103
- en: 'Confirm Your Email',
104
- zh: '请确认您的电子邮箱',
105
- es: 'Confirma tu correo electrónico',
106
- },
107
- P1: {
108
- en: 'Email confirmed! Thanks.',
109
- zh: '电子邮箱已确认!感谢。',
110
- es: 'Correo electrónico confirmado! Gracias.',
111
- },
112
- };
86
+ const translate = MailerProvider.instance[id].translateTemplates.confirmEmail;
113
87
  const sendResult = await MailerProvider.send({
114
88
  id,
115
89
  sendOptions: {
@@ -120,7 +94,7 @@ const UserService = {
120
94
  .replace('{{H1}}', translate.H1[req.lang])
121
95
  .replace('{{P1}}', translate.P1[req.lang])
122
96
  .replace('{{TOKEN}}', token)
123
- .replace(`{{COMPANY}}`, options.host), // html body
97
+ .replace(`{{COMPANY}}`, req.body.hostname), // html body
124
98
  attachments: [
125
99
  // {
126
100
  // filename: 'logo.png',
@@ -103,7 +103,11 @@ const Account = {
103
103
  e.preventDefault();
104
104
  const result = await UserService.post({
105
105
  id: 'mailer/verify-email',
106
- body: { email: s(`.account-email`).value },
106
+ body: {
107
+ email: s(`.account-email`).value,
108
+ hostname: `${location.hostname}`,
109
+ proxyPath: getProxyPath(),
110
+ },
107
111
  });
108
112
  NotificationManager.Push({
109
113
  html: result.status === 'error' ? result.message : Translate.Render(`email send`),
@@ -7,7 +7,7 @@ import { Modal, renderViewTitle } from './Modal.js';
7
7
  import { DocumentService } from '../../services/document/document.service.js';
8
8
  import { CoreService, getApiBaseUrl } from '../../services/core/core.service.js';
9
9
  import { loggerFactory } from './Logger.js';
10
- import { imageShimmer, renderCssAttr } from './Css.js';
10
+ import { imageShimmer, renderChessPattern, renderCssAttr, styleFactory } from './Css.js';
11
11
 
12
12
  const logger = loggerFactory(import.meta);
13
13
 
@@ -114,7 +114,6 @@ const Content = {
114
114
  width: '100%',
115
115
  border: 'none',
116
116
  };
117
- options.style = `style="${renderCssAttr(options)}"`;
118
117
  if (!options.class) options.class = ``;
119
118
  const { container, file } = options;
120
119
  const ext = file.name.split('.')[file.name.split('.').length - 1];
@@ -126,7 +125,7 @@ const Content = {
126
125
  const content = options.url
127
126
  ? await CoreService.getRaw({ url: options.url })
128
127
  : await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
129
- render += html`<div class="${options.class}" ${options.style}>${marked.parse(content)}</div>`;
128
+ render += html`<div class="${options.class}" ${styleFactory(options.style)}>${marked.parse(content)}</div>`;
130
129
  }
131
130
 
132
131
  break;
@@ -135,13 +134,18 @@ const Content = {
135
134
  case 'jpeg':
136
135
  case 'webp':
137
136
  case 'svg':
137
+ case 'gif':
138
138
  case 'png': {
139
139
  const url = options.url
140
140
  ? options.url
141
141
  : file._id
142
142
  ? getApiBaseUrl({ id: file._id, endpoint: 'file/blob' })
143
143
  : URL.createObjectURL(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
144
- const imgRender = html`<img class="in ${options.class}" ${options.style} src="${url}" />`;
144
+ const imgRender = html`<img
145
+ class="in ${options.class}"
146
+ ${styleFactory(options.style, `${renderChessPattern(50)}`)}
147
+ src="${url}"
148
+ />`;
145
149
  render += imgRender;
146
150
  break;
147
151
  }
@@ -153,14 +157,14 @@ const Content = {
153
157
  : URL.createObjectURL(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
154
158
  render += html`<iframe
155
159
  class="in ${options.class} iframe-${options.idModal}"
156
- ${options.style}
160
+ ${styleFactory(options.style)}
157
161
  src="${url}"
158
162
  ></iframe>`;
159
163
  break;
160
164
  }
161
165
 
162
166
  case 'json':
163
- render += html`<pre class="in ${options.class}" ${options.style}>
167
+ render += html`<pre class="in ${options.class}" ${styleFactory(options.style)}>
164
168
  ${JSON.stringify(
165
169
  JSON.parse(
166
170
  options.url
@@ -174,7 +178,7 @@ const Content = {
174
178
  break;
175
179
 
176
180
  default:
177
- render += html`<div class="in ${options.class}" ${options.style}>
181
+ render += html`<div class="in ${options.class}" ${styleFactory(options.style)}>
178
182
  ${options.url
179
183
  ? await CoreService.getRaw({ url: options.url })
180
184
  : await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype))}
@@ -1026,7 +1026,8 @@ const imageShimmer = () => html`<div
1026
1026
  </div>
1027
1027
  </div>`;
1028
1028
 
1029
- const renderChessPattern = () => `background: repeating-conic-gradient(#808080 0 25%, #0000 0 50%) 50% / 20px 20px`;
1029
+ const renderChessPattern = (patternSize = 20) =>
1030
+ `background: repeating-conic-gradient(#808080 0 25%, #0000 0 50%) 50% / ${patternSize}px ${patternSize}px`;
1030
1031
 
1031
1032
  const extractBackgroundImageUrl = (element) => {
1032
1033
  const style = window.getComputedStyle(element);
@@ -1044,6 +1045,8 @@ const simpleIconsRender = (selector) => {
1044
1045
  });
1045
1046
  };
1046
1047
 
1048
+ const styleFactory = (payload, plain = '') => `style="${renderCssAttr({ style: payload })} ${plain}"`;
1049
+
1047
1050
  export {
1048
1051
  Css,
1049
1052
  Themes,
@@ -1083,4 +1086,5 @@ export {
1083
1086
  lightenHex,
1084
1087
  darkenHex,
1085
1088
  adjustHex,
1089
+ styleFactory,
1086
1090
  };
@@ -359,4 +359,9 @@ const InputFile = {
359
359
  },
360
360
  };
361
361
 
362
- export { Input, InputFile, fileFormDataFactory, getSrcFromFileData, getFileFromFileData };
362
+ function isTextInputFocused() {
363
+ const active = document.activeElement;
364
+ return active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA');
365
+ }
366
+
367
+ export { Input, InputFile, fileFormDataFactory, getSrcFromFileData, getFileFromFileData, isTextInputFocused };
@@ -189,6 +189,9 @@ const LogIn = {
189
189
  </form>
190
190
  `;
191
191
  },
192
+ cleanMainUser: () => {
193
+ LogIn.Scope.user.main.model.user = {};
194
+ },
192
195
  };
193
196
 
194
197
  export { LogIn };
@@ -8,7 +8,7 @@ import { Webhook } from './Webhook.js';
8
8
  const LogOut = {
9
9
  Event: {},
10
10
  Trigger: async function (options) {
11
- LogIn.Scope.user.main.model.user = {};
11
+ LogIn.cleanMainUser();
12
12
  await Webhook.unregister();
13
13
  for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
14
14
  if (s(`.session`))
@@ -28,7 +28,7 @@ import { setDocTitle, closeModalRouteChangeEvent, handleModalViewRoute } from '.
28
28
  import { NotificationManager } from './NotificationManager.js';
29
29
  import { EventsUI } from './EventsUI.js';
30
30
  import { Translate } from './Translate.js';
31
- import { Input } from './Input.js';
31
+ import { Input, isTextInputFocused } from './Input.js';
32
32
  import { Validator } from './Validator.js';
33
33
  import { DropDown } from './DropDown.js';
34
34
  import { Keyboard } from './Keyboard.js';
@@ -186,7 +186,7 @@ const Modal = {
186
186
  `.default-slide-menu-top-bar-fix-title-container`,
187
187
  html`
188
188
  <div class="inl default-slide-menu-top-bar-fix-title-container-text">
189
- ${options.RouterInstance.NameApp}
189
+ ${options.RouterInstance.BannerAppTemplate}
190
190
  </div>
191
191
  `,
192
192
  );
@@ -608,6 +608,7 @@ const Modal = {
608
608
  }),
609
609
  );
610
610
  s(`.search-result-btn-${result.routerId}`).onclick = () => {
611
+ if (!s(`.html-${searchBoxHistoryId}`) || !s(`.html-${searchBoxHistoryId}`).hasChildNodes()) return;
611
612
  s(`.html-${searchBoxHistoryId}`).childNodes[currentKeyBoardSearchBoxIndex].classList.remove(
612
613
  `main-btn-menu-active`,
613
614
  );
@@ -621,6 +622,7 @@ const Modal = {
621
622
  };
622
623
 
623
624
  const getResultSearchBox = (validatorData) => {
625
+ if (!s(`.html-${searchBoxHistoryId}`) || !s(`.html-${searchBoxHistoryId}`).hasChildNodes()) return;
624
626
  const { model, id } = validatorData;
625
627
  switch (model) {
626
628
  case 'search-box':
@@ -998,6 +1000,7 @@ const Modal = {
998
1000
  ['Alt', 'k'],
999
1001
  ],
1000
1002
  eventCallBack: () => {
1003
+ if (isTextInputFocused()) return;
1001
1004
  if (s(`.top-bar-search-box`)) {
1002
1005
  if (s(`.main-body-btn-ui-close`).classList.contains('hide')) {
1003
1006
  s(`.main-body-btn-ui-open`).click();
@@ -7,7 +7,7 @@ import { LogIn } from './LogIn.js';
7
7
  import { NotificationManager } from './NotificationManager.js';
8
8
  import { Translate } from './Translate.js';
9
9
  import { Validator } from './Validator.js';
10
- import { getQueryParams, s } from './VanillaJs.js';
10
+ import { getProxyPath, getQueryParams, s } from './VanillaJs.js';
11
11
 
12
12
  const Recover = {
13
13
  Event: {},
@@ -80,6 +80,8 @@ const Recover = {
80
80
  }
81
81
  switch (mode) {
82
82
  case 'recover-verify-email': {
83
+ body.proxyPath = getProxyPath();
84
+ body.hostname = `${location.hostname}`;
83
85
  const result = await UserService.post({ id: 'recover-verify-email', body });
84
86
  NotificationManager.Push({
85
87
  html:
@@ -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
  /**
@@ -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 = () => {
@@ -12,7 +12,6 @@ const RoutesDefault = () => {
12
12
  '/': {
13
13
  title: 'Home',
14
14
  render: () => Modal.onHomeRouterEvent(),
15
- upperCase: false,
16
15
  },
17
16
  '/home': { title: 'home', render: () => Modal.onHomeRouterEvent() },
18
17
  '/settings': { title: 'settings', render: () => s(`.main-btn-settings`).click() },
@@ -40,7 +39,7 @@ const RoutesDefault = () => {
40
39
  window.Routes = RoutesDefault;
41
40
 
42
41
  const RouterDefault = () => {
43
- return { Routes: RoutesDefault, NameApp };
42
+ return { Routes: RoutesDefault, BannerAppTemplate };
44
43
  };
45
44
 
46
- 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.848';
38
+ static version = 'v2.8.852';
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];