underpost 2.8.87 → 2.8.88

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 (99) hide show
  1. package/.env.development +35 -3
  2. package/.env.production +39 -4
  3. package/.env.test +35 -3
  4. package/.github/workflows/ghpkg.ci.yml +1 -1
  5. package/.github/workflows/npmpkg.ci.yml +1 -1
  6. package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -5
  7. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  8. package/.github/workflows/release.cd.yml +3 -3
  9. package/README.md +56 -2
  10. package/bin/build.js +4 -0
  11. package/bin/deploy.js +62 -8
  12. package/bin/file.js +3 -2
  13. package/cli.md +8 -2
  14. package/conf.js +30 -4
  15. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  16. package/manifests/deployment/dd-test-development/deployment.yaml +174 -0
  17. package/manifests/deployment/dd-test-development/proxy.yaml +51 -0
  18. package/package.json +6 -5
  19. package/src/api/core/core.router.js +2 -1
  20. package/src/api/default/default.controller.js +6 -1
  21. package/src/api/default/default.router.js +6 -2
  22. package/src/api/default/default.service.js +10 -1
  23. package/src/api/document/document.controller.js +66 -0
  24. package/src/api/document/document.model.js +51 -0
  25. package/src/api/document/document.router.js +24 -0
  26. package/src/api/document/document.service.js +125 -0
  27. package/src/api/file/file.controller.js +15 -1
  28. package/src/api/file/file.router.js +2 -1
  29. package/src/api/test/test.router.js +1 -1
  30. package/src/api/user/postman_collection.json +216 -0
  31. package/src/api/user/user.controller.js +25 -60
  32. package/src/api/user/user.model.js +29 -7
  33. package/src/api/user/user.router.js +9 -3
  34. package/src/api/user/user.service.js +84 -32
  35. package/src/cli/baremetal.js +33 -3
  36. package/src/cli/cloud-init.js +11 -0
  37. package/src/cli/cron.js +0 -1
  38. package/src/cli/deploy.js +46 -23
  39. package/src/cli/index.js +5 -0
  40. package/src/cli/lxd.js +7 -0
  41. package/src/cli/repository.js +42 -6
  42. package/src/cli/run.js +45 -13
  43. package/src/cli/ssh.js +20 -6
  44. package/src/client/Default.index.js +42 -1
  45. package/src/client/components/core/Account.js +10 -2
  46. package/src/client/components/core/AgGrid.js +30 -8
  47. package/src/client/components/core/Auth.js +99 -56
  48. package/src/client/components/core/BtnIcon.js +3 -2
  49. package/src/client/components/core/CalendarCore.js +2 -3
  50. package/src/client/components/core/CommonJs.js +1 -2
  51. package/src/client/components/core/Content.js +15 -12
  52. package/src/client/components/core/Css.js +2 -1
  53. package/src/client/components/core/CssCore.js +6 -1
  54. package/src/client/components/core/Docs.js +5 -5
  55. package/src/client/components/core/FileExplorer.js +3 -3
  56. package/src/client/components/core/Input.js +22 -17
  57. package/src/client/components/core/JoyStick.js +2 -2
  58. package/src/client/components/core/LoadingAnimation.js +2 -2
  59. package/src/client/components/core/LogIn.js +16 -23
  60. package/src/client/components/core/LogOut.js +5 -1
  61. package/src/client/components/core/Logger.js +4 -1
  62. package/src/client/components/core/Modal.js +82 -53
  63. package/src/client/components/core/ObjectLayerEngineModal.js +2 -1
  64. package/src/client/components/core/Pagination.js +207 -0
  65. package/src/client/components/core/Panel.js +10 -10
  66. package/src/client/components/core/PanelForm.js +130 -33
  67. package/src/client/components/core/Recover.js +2 -2
  68. package/src/client/components/core/Router.js +210 -34
  69. package/src/client/components/core/SignUp.js +1 -2
  70. package/src/client/components/core/Stream.js +1 -1
  71. package/src/client/components/core/VanillaJs.js +3 -84
  72. package/src/client/components/core/Worker.js +2 -2
  73. package/src/client/components/default/LogInDefault.js +0 -6
  74. package/src/client/components/default/LogOutDefault.js +0 -16
  75. package/src/client/components/default/MenuDefault.js +97 -44
  76. package/src/client/components/default/RoutesDefault.js +5 -2
  77. package/src/client/services/core/core.service.js +8 -20
  78. package/src/client/services/default/default.management.js +115 -18
  79. package/src/client/services/default/default.service.js +13 -4
  80. package/src/client/services/document/document.service.js +97 -0
  81. package/src/client/services/file/file.service.js +2 -0
  82. package/src/client/services/test/test.service.js +3 -0
  83. package/src/client/services/user/user.management.js +6 -0
  84. package/src/client/services/user/user.service.js +15 -4
  85. package/src/client/ssr/Render.js +1 -1
  86. package/src/client/ssr/head/DefaultScripts.js +3 -0
  87. package/src/client/ssr/head/Seo.js +1 -0
  88. package/src/index.js +24 -2
  89. package/src/runtime/lampp/Lampp.js +89 -2
  90. package/src/runtime/xampp/Xampp.js +48 -1
  91. package/src/server/auth.js +519 -155
  92. package/src/server/backup.js +2 -2
  93. package/src/server/conf.js +66 -8
  94. package/src/server/process.js +2 -1
  95. package/src/server/runtime.js +135 -286
  96. package/src/server/ssl.js +1 -2
  97. package/src/server/ssr.js +85 -0
  98. package/src/server/start.js +2 -2
  99. package/src/server/valkey.js +2 -1
package/src/cli/run.js CHANGED
@@ -7,6 +7,8 @@ import fs from 'fs-extra';
7
7
  import { range, setPad, timer } from '../client/components/core/CommonJs.js';
8
8
  import UnderpostDeploy from './deploy.js';
9
9
  import UnderpostRootEnv from './env.js';
10
+ import UnderpostRepository from './repository.js';
11
+ import os from 'os';
10
12
 
11
13
  const logger = loggerFactory(import.meta);
12
14
 
@@ -19,6 +21,8 @@ class UnderpostRun {
19
21
  imageName: '',
20
22
  containerName: '',
21
23
  namespace: '',
24
+ build: false,
25
+ replicas: 1,
22
26
  };
23
27
  static RUNNERS = {
24
28
  'spark-template': (path, options = UnderpostRun.DEFAULT_OPTION) => {
@@ -26,9 +30,9 @@ class UnderpostRun {
26
30
  shellExec(`sudo rm -rf ${dir}`);
27
31
  shellCd('/home/dd');
28
32
 
29
- // pbcopy(`cd /home/dd && sbt new underpostnet/spark-template.g8`);
33
+ // pbcopy(`cd /home/dd && sbt new ${process.env.GITHUB_USERNAME}/spark-template.g8`);
30
34
  // await read({ prompt: 'Command copy to clipboard, press enter to continue.\n' });
31
- shellExec(`cd /home/dd && sbt new underpostnet/spark-template.g8 '--name=spark-template'`);
35
+ shellExec(`cd /home/dd && sbt new ${process.env.GITHUB_USERNAME}/spark-template.g8 '--name=spark-template'`);
32
36
 
33
37
  shellCd(dir);
34
38
 
@@ -103,10 +107,12 @@ class UnderpostRun {
103
107
  },
104
108
  'template-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
105
109
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
110
+ shellExec(`${baseCommand} run clean`);
111
+ shellExec(`${baseCommand} push ./engine-private ${process.env.GITHUB_USERNAME}/engine-private`);
106
112
  shellCd('/home/dd/engine');
107
113
  shellExec(`git reset`);
108
114
  shellExec(`${baseCommand} cmt . --empty ci package-pwa-microservices-template`);
109
- shellExec(`${baseCommand} push . underpostnet/engine`);
115
+ shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
110
116
  },
111
117
  clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
112
118
  shellCd(path ?? `/home/dd/engine`);
@@ -115,8 +121,8 @@ class UnderpostRun {
115
121
  pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
116
122
  shellCd(`/home/dd/engine`);
117
123
  shellExec(`node bin/deploy clean-core-repo`);
118
- shellExec(`underpost pull . underpostnet/engine`);
119
- shellExec(`underpost pull engine-private underpostnet/engine-private`, { silent: true });
124
+ shellExec(`underpost pull . ${process.env.GITHUB_USERNAME}/engine`);
125
+ shellExec(`underpost pull ./engine-private ${process.env.GITHUB_USERNAME}/engine-private`);
120
126
  },
121
127
  'release-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
122
128
  actionInitLog();
@@ -128,11 +134,12 @@ class UnderpostRun {
128
134
  }
129
135
  },
130
136
  'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
137
+ actionInitLog();
131
138
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
132
139
  shellCd('/home/dd/engine');
133
140
  shellExec(`git reset`);
134
141
  shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
135
- shellExec(`${baseCommand} push . underpostnet/engine`);
142
+ shellExec(`${baseCommand} push . ${process.env.GITHUB_USERNAME}/engine`);
136
143
  },
137
144
  ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
138
145
  const { underpostRoot } = options;
@@ -148,13 +155,28 @@ class UnderpostRun {
148
155
  let [deployId, subConf] = path.split(',');
149
156
  shellExec(`npm run dev-api ${deployId} ${subConf}`);
150
157
  },
151
- 'router-sync': (path, options = UnderpostRun.DEFAULT_OPTION) => {
158
+ sync: (path, options = UnderpostRun.DEFAULT_OPTION) => {
159
+ const env = options.dev ? 'development' : 'production';
152
160
  const baseCommand = options.dev || true ? 'node bin' : 'underpost';
153
- const defaultPaht = ['dd', 'kind-control-plane'];
154
- let [deployId, node] = path ? path.split(',') : defaultPaht;
155
- deployId = deployId ?? defaultPaht[0];
156
- node = node ?? defaultPaht[1];
157
- shellExec(`${baseCommand} deploy --sync --node ${node} --build-manifest --info-router ${deployId} production`);
161
+ shellExec(`${baseCommand} run clean`);
162
+ const defaultPath = ['dd-default', 1, ``, ``, 'kind-control-plane'];
163
+ let [deployId, replicas, versions, image, node] = path ? path.split(',') : defaultPath;
164
+ deployId = deployId ?? defaultPath[0];
165
+ replicas = replicas ?? defaultPath[1];
166
+ versions = versions ?? defaultPath[2];
167
+ image = image ?? defaultPath[3];
168
+ node = node ?? defaultPath[4];
169
+ shellExec(
170
+ `${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${
171
+ replicas ?? 1
172
+ } --node ${node}${image ? ` --image ${image}` : ''}${
173
+ versions ? ` --versions ${versions.replaceAll('+', ',')}` : ''
174
+ } dd ${env}`,
175
+ );
176
+ if (!options.build) shellExec(`${baseCommand} deploy --kubeadm ${deployId} ${env}`);
177
+ },
178
+ 'ls-deployments': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
179
+ console.table(await UnderpostDeploy.API.get(path, 'deployments'));
158
180
  },
159
181
  monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
160
182
  const pid = getTerminalPid();
@@ -290,11 +312,21 @@ class UnderpostRun {
290
312
  },
291
313
  deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
292
314
  const deployId = path;
315
+ const { validVersion, deployVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
316
+ if (!validVersion) throw new Error('Version mismatch');
293
317
  const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
294
318
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
295
319
  const env = 'production';
296
320
  const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`).map((p) => p.NAME);
297
- shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
321
+
322
+ if (options.build === true) {
323
+ // deployId, replicas, versions, image, node
324
+ shellExec(
325
+ `node bin run sync ${deployId},${options.replicas ?? 1},${targetTraffic},${
326
+ options.imageName ?? `localhost/rockylinux9-underpost:${deployVersion}`
327
+ },${os.hostname()}`,
328
+ );
329
+ } else shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic}`);
298
330
 
299
331
  let checkStatusIteration = 0;
300
332
  const checkStatusIterationMsDelay = 1000;
package/src/cli/ssh.js CHANGED
@@ -1,23 +1,37 @@
1
- import { getNpmRootPath } from '../server/conf.js';
1
+ /**
2
+ * SSH module for managing SSH key generation and connection setup.
3
+ * @module src/cli/ssh.js
4
+ * @namespace UnderpostSSH
5
+ */
6
+
2
7
  import { shellExec } from '../server/process.js';
3
8
 
9
+ /**
10
+ * @class UnderpostSSH
11
+ * @description Manages SSH key generation and connection setup.
12
+ * @memberof UnderpostSSH
13
+ */
4
14
  class UnderpostSSH {
5
15
  static API = {
6
16
  /**
7
17
  * @method callback
8
- * @param {object} options
9
- * @param {boolean} options.generate - Generates new ssh credential and stores it in current private keys file storage.
10
- * @description Import and start ssh server and client based on current default deployment ID.
18
+ * @description Manages SSH key generation and connection setup based on the default deployment ID.
19
+ * This function will either generate a new SSH key pair or import an existing one,
20
+ * then initiate the SSH connection process.
21
+ * @param {object} [options={ generate: false }] - Options for the SSH callback.
22
+ * @param {boolean} [options.generate=false] - If true, generates a new SSH key pair. Otherwise, it imports the existing one.
23
+ * @memberof UnderpostSSH
24
+ * @returns {Promise<void>}
11
25
  */
12
26
  callback: async (
13
27
  options = {
14
28
  generate: false,
15
29
  },
16
30
  ) => {
17
- // only import + start
31
+ // Example usage for importing an existing key:
18
32
  // node bin/deploy ssh root@<host> <password> import
19
33
 
20
- // generate + import + start
34
+ // Example usage for generating a new key:
21
35
  // node bin/deploy ssh root@<host> <password>
22
36
 
23
37
  shellExec(
@@ -16,9 +16,49 @@ import { SocketIo } from './components/core/SocketIo.js';
16
16
  import { SocketIoDefault } from './components/default/SocketIoDefault.js';
17
17
  import { ElementsDefault } from './components/default/ElementsDefault.js';
18
18
  import { CssDefaultDark, CssDefaultLight } from './components/default/CssDefault.js';
19
+ import { EventsUI } from './components/core/EventsUI.js';
20
+ import { Modal } from './components/core/Modal.js';
19
21
 
20
22
  const htmlMainBody = async () => {
21
- return html`<span>Hello World!!</span>`;
23
+ setTimeout(() => {
24
+ EventsUI.onClick('.get-started-button', (e) => {
25
+ e.preventDefault();
26
+ location.href = `https://www.nexodev.org/docs/?cid=src`;
27
+ });
28
+ });
29
+ return html`
30
+ <div class="landing-container">
31
+ <div class="content-wrapper">
32
+ <h1 class="animated-text">
33
+ <span class="greeting">Hello, World!</span>
34
+ <span class="subtitle">Welcome to Our Platform</span>
35
+ </h1>
36
+
37
+ <div class="features">
38
+ <div class="feature-card">
39
+ <i class="icon">🚀</i>
40
+ <h3>Fast & Reliable</h3>
41
+ <p>Lightning-fast performance with 99.9% uptime</p>
42
+ </div>
43
+ <div class="feature-card">
44
+ <i class="icon">🎨</i>
45
+ <h3>Beautiful UI</h3>
46
+ <p>Modern and intuitive user interface</p>
47
+ </div>
48
+ <div class="feature-card">
49
+ <i class="icon">⚡</i>
50
+ <h3>Powerful Features</h3>
51
+ <p>Everything you need in one place</p>
52
+ </div>
53
+ </div>
54
+
55
+ <button class="cta-button get-started-button">
56
+ Get Started
57
+ <span class="button-icon">→</span>
58
+ </button>
59
+ </div>
60
+ </div>
61
+ `;
22
62
  };
23
63
 
24
64
  window.onload = () =>
@@ -39,5 +79,6 @@ window.onload = () =>
39
79
  await LogOutDefault();
40
80
  await SignUpDefault();
41
81
  await Keyboard.Init({ callBackTime: DefaultParams.EVENT_CALLBACK_TIME });
82
+ await Modal.RenderSeoSanitizer();
42
83
  },
43
84
  });
@@ -10,7 +10,8 @@ import { Modal } from './Modal.js';
10
10
  import { NotificationManager } from './NotificationManager.js';
11
11
  import { Translate } from './Translate.js';
12
12
  import { Validator } from './Validator.js';
13
- import { append, getProxyPath, htmls, s } from './VanillaJs.js';
13
+ import { append, htmls, s } from './VanillaJs.js';
14
+ import { getProxyPath } from './Router.js';
14
15
 
15
16
  const Account = {
16
17
  UpdateEvent: {},
@@ -194,7 +195,14 @@ const Account = {
194
195
  await this.instanceModalUiEvents({ user });
195
196
  });
196
197
  return html`
197
- <input type="file" accept="${profileFileAccept.join(', ')}" class="account-profile-image-input hide" />
198
+ <label for="account-profile-image-input-label"
199
+ ><span class="hide">Profile Image</span>
200
+ <input
201
+ type="file"
202
+ accept="${profileFileAccept.join(', ')}"
203
+ class="account-profile-image-input hide"
204
+ id="account-profile-image-input-label"
205
+ /></label>
198
206
  ${renderWave({ id: waveAnimationId })}
199
207
 
200
208
  <form class="in">
@@ -2,8 +2,9 @@
2
2
  // https://www.ag-grid.com/javascript-data-grid/themes/
3
3
 
4
4
  import { ThemeEvents, darkTheme } from './Css.js';
5
- import { append, getProxyPath, htmls, s } from './VanillaJs.js';
6
- import * as agGrid from 'ag-grid-community';
5
+ import { append, htmls, s } from './VanillaJs.js';
6
+ import { getProxyPath } from './Router.js';
7
+ import './Pagination.js';
7
8
 
8
9
  const AgGrid = {
9
10
  grids: {},
@@ -14,6 +15,9 @@ const AgGrid = {
14
15
  // Grid Options: Contains all of the grid configurations
15
16
  const gridOptions = {
16
17
  // Row Data: The data to be displayed.
18
+ pagination: false, // Disabled by default, will be handled by the management view
19
+ // paginationPageSize: 100,
20
+ // suppressPaginationPanel: true, // We are using our own custom pagination component
17
21
  // rowHeight: 60,
18
22
  enableCellChangeFlash: true,
19
23
  defaultColDef: {
@@ -23,6 +27,12 @@ const AgGrid = {
23
27
  filter: true,
24
28
  autoHeight: true,
25
29
  },
30
+ rowClassRules: {
31
+ 'row-new-highlight': (params) => {
32
+ // a temporary flag we can set on new rows to highlight them
33
+ return params.data && params.data._new;
34
+ },
35
+ },
26
36
  // domLayout: 'autoHeight', || 'normal'
27
37
  // Column Definitions: Defines & controls grid columns.
28
38
  columnDefs: options?.gridOptions?.rowData?.[0]
@@ -48,13 +58,17 @@ const AgGrid = {
48
58
  }
49
59
  };
50
60
  });
61
+ const usePagination = options?.usePagination;
51
62
  return html`
52
- <div
53
- class="${id} ${this.theme}${options?.darkTheme ? `-dark` : ''}"
54
- style="${options?.style
55
- ? Object.keys(options.style).map((styleKey) => `${styleKey}: ${options.style[styleKey]}; `)
56
- : 'height: 500px'}"
57
- ></div>
63
+ <div>
64
+ <div
65
+ class="${id} ${this.theme}${options?.darkTheme ? `-dark` : ''}"
66
+ style="${options?.style
67
+ ? Object.keys(options.style).map((styleKey) => `${styleKey}: ${options.style[styleKey]}; `)
68
+ : 'height: 500px'}"
69
+ ></div>
70
+ ${usePagination ? `<ag-pagination id="ag-pagination-${id}"></ag-pagination>` : ''}
71
+ </div>
58
72
  `;
59
73
  },
60
74
  RenderStyle: async function (
@@ -136,6 +150,10 @@ const AgGrid = {
136
150
  ${darkTheme
137
151
  ? html`
138
152
  <style>
153
+ .ag-row.row-new-highlight {
154
+ background-color: #6d68ff !important;
155
+ transition: background-color 1s ease-out;
156
+ }
139
157
  .ag-cell-data-changed,
140
158
  .ag-cell-data-changed-animation {
141
159
  background-color: #6d68ff !important;
@@ -145,6 +163,10 @@ const AgGrid = {
145
163
  </style>
146
164
  `
147
165
  : html`<style>
166
+ .ag-row.row-new-highlight {
167
+ background-color: #d0eaf8 !important;
168
+ transition: background-color 1s ease-out;
169
+ }
148
170
  .ag-cell-data-changed,
149
171
  .ag-cell-data-changed-animation {
150
172
  background-color: #d1d1d1 !important;
@@ -4,16 +4,17 @@ import { loggerFactory } from './Logger.js';
4
4
  import { LogIn } from './LogIn.js';
5
5
  import { LogOut } from './LogOut.js';
6
6
  import { NotificationManager } from './NotificationManager.js';
7
- import { SignUp } from './SignUp.js';
8
7
  import { Translate } from './Translate.js';
9
8
  import { s } from './VanillaJs.js';
10
9
 
11
- const logger = loggerFactory(import.meta);
10
+ const logger = loggerFactory(import.meta, { trace: true });
12
11
 
13
12
  const token = Symbol('token');
14
13
 
15
14
  const guestToken = Symbol('guestToken');
16
15
 
16
+ const refreshTimeout = Symbol('refreshTimeout');
17
+
17
18
  const Auth = {
18
19
  [token]: '',
19
20
  [guestToken]: '',
@@ -35,50 +36,65 @@ const Auth = {
35
36
  getGuestToken: function () {
36
37
  return this[guestToken];
37
38
  },
38
- // jwt
39
39
  getJWT: function () {
40
- return `Bearer ${this.getToken() ? this.getToken() : this.getGuestToken()}`;
40
+ if (Auth.getToken()) return `Bearer ${Auth.getToken()}`;
41
+ if (Auth.getGuestToken()) return `Bearer ${Auth.getGuestToken()}`;
42
+ return '';
41
43
  },
42
- signUpToken: async function (
43
- result = {
44
- data: {
45
- token: '',
46
- user: null,
47
- },
48
- },
49
- ) {
44
+ decodeJwt: function (token) {
50
45
  try {
51
- localStorage.setItem('jwt', result.data.token);
52
- await SignUp.Trigger(result.data);
53
- await Auth.sessionIn();
54
- } catch (error) {
55
- logger.error(error);
56
- localStorage.removeItem('jwt');
46
+ return JSON.parse(atob(token.split('.')[1]));
47
+ } catch (e) {
48
+ return null;
57
49
  }
58
50
  },
51
+ scheduleTokenRefresh: function () {
52
+ if (this[refreshTimeout]) {
53
+ clearTimeout(this[refreshTimeout]);
54
+ }
55
+
56
+ const currentToken = Auth.getToken();
57
+ if (!currentToken) return;
58
+
59
+ const payload = Auth.decodeJwt(currentToken);
60
+ if (!payload || !payload.refreshExpiresAt) return;
61
+
62
+ const expiresIn = payload.refreshExpiresAt - Date.now();
63
+ const refreshBuffer = 2 * 60 * 1000; // 2 minutes
64
+ const refreshIn = expiresIn - refreshBuffer;
65
+
66
+ logger.info(`Token refresh in ${refreshIn / (1000 * 60)} minutes`);
67
+
68
+ if (refreshIn <= 0) return; // Already expired or close to it
69
+
70
+ this[refreshTimeout] = setTimeout(async () => {
71
+ const { data, status } = await UserService.get({ id: 'auth' });
72
+ if (status === 'success') {
73
+ logger.info('Refreshed access token.');
74
+ Auth.setToken(data.token);
75
+ localStorage.setItem('jwt', data.token);
76
+ Auth.scheduleTokenRefresh();
77
+ } else Auth.sessionOut();
78
+ }, refreshIn);
79
+ },
59
80
  sessionIn: async function (userServicePayload) {
60
81
  try {
61
82
  const token = userServicePayload?.data?.token ? userServicePayload.data.token : localStorage.getItem('jwt');
62
-
63
83
  if (token) {
64
- this.setToken(token);
84
+ Auth.setToken(token);
85
+
65
86
  const result = userServicePayload
66
- ? userServicePayload
67
- : await (async () => {
68
- const _result = await UserService.get({ id: 'auth' });
69
- return {
70
- status: _result.status,
71
- message: _result.message,
72
- data: {
73
- user: _result.data,
74
- },
75
- };
76
- })();
87
+ ? userServicePayload // From login/signup
88
+ : await UserService.get({ id: 'auth' });
89
+
77
90
  const { status, data, message } = result;
78
91
  if (status === 'success') {
92
+ Auth.setToken(data.token);
79
93
  localStorage.setItem('jwt', token);
94
+ Auth.renderSessionUI();
80
95
  await LogIn.Trigger({ user: data.user });
81
96
  await Account.updateForm(data.user);
97
+ Auth.scheduleTokenRefresh();
82
98
  return { user: data.user };
83
99
  }
84
100
  if (message && message.match('expired'))
@@ -89,41 +105,68 @@ const Auth = {
89
105
  status: 'warning',
90
106
  });
91
107
  });
92
- return await Auth.sessionOut();
93
108
  }
94
-
95
- // anon guest session
96
- this.deleteToken();
109
+ // Important delete session token if guest token already exists
110
+ Auth.deleteToken();
97
111
  localStorage.removeItem('jwt');
98
- let guestToken = localStorage.getItem('jwt.g');
99
112
 
100
- if (!guestToken) {
101
- const result = await UserService.post({ id: 'guest' });
102
- localStorage.setItem('jwt.g', result.data.token);
103
- guestToken = result.data.token;
104
- }
105
-
106
- this.setGuestToken(guestToken);
107
- let { data, status, message } = await UserService.get({ id: 'auth' });
108
- if (status === 'error') {
109
- if (message && message.match('expired')) {
110
- localStorage.removeItem('jwt.g');
111
- return await Auth.sessionOut();
112
- } else throw new Error(message);
113
+ // Anon guest session
114
+ let guestToken = localStorage.getItem('jwt.g');
115
+ if (guestToken) {
116
+ Auth.setGuestToken(guestToken);
117
+ let { data, status, message } = await UserService.get({ id: 'auth' });
118
+ if (status === 'success') {
119
+ await LogIn.Trigger(data);
120
+ await Account.updateForm(data.user);
121
+ return data;
122
+ } else logger.error(message);
113
123
  }
114
- await Account.updateForm(data);
115
- return { user: data };
124
+ return await Auth.sessionOut();
116
125
  } catch (error) {
117
126
  logger.error(error);
118
127
  return { user: UserMock.default };
119
128
  }
120
129
  },
121
130
  sessionOut: async function () {
122
- this.deleteToken();
123
- localStorage.removeItem('jwt');
124
- const result = await this.sessionIn();
125
- await LogOut.Trigger(result);
126
- return result;
131
+ {
132
+ const result = await UserService.delete({ id: 'logout' });
133
+ localStorage.removeItem('jwt');
134
+ Auth.deleteToken();
135
+ if (this[refreshTimeout]) {
136
+ clearTimeout(this[refreshTimeout]);
137
+ }
138
+ Auth.renderGuestUi();
139
+ LogIn.Scope.user.main.model.user = {};
140
+ await LogOut.Trigger(result);
141
+ }
142
+ {
143
+ localStorage.removeItem('jwt.g');
144
+ Auth.deleteGuestToken();
145
+ const result = await UserService.post({ id: 'guest' });
146
+ localStorage.setItem('jwt.g', result.data.token);
147
+ Auth.setGuestToken(result.data.token);
148
+ return await Auth.sessionIn();
149
+ }
150
+ },
151
+ renderSessionUI: function () {
152
+ s(`.main-btn-log-in`).style.display = 'none';
153
+ s(`.main-btn-sign-up`).style.display = 'none';
154
+ s(`.main-btn-log-out`).style.display = null;
155
+ s(`.main-btn-account`).style.display = null;
156
+ setTimeout(() => {
157
+ if (s(`.modal-log-in`)) s(`.btn-close-modal-log-in`).click();
158
+ if (s(`.modal-sign-up`)) s(`.btn-close-modal-sign-up`).click();
159
+ });
160
+ },
161
+ renderGuestUi: function () {
162
+ s(`.main-btn-log-in`).style.display = null;
163
+ s(`.main-btn-sign-up`).style.display = null;
164
+ s(`.main-btn-log-out`).style.display = 'none';
165
+ s(`.main-btn-account`).style.display = 'none';
166
+ setTimeout(() => {
167
+ if (s(`.modal-log-out`)) s(`.btn-close-modal-log-out`).click();
168
+ if (s(`.modal-account`)) s(`.btn-close-modal-account`).click();
169
+ });
127
170
  },
128
171
  };
129
172
 
@@ -1,7 +1,7 @@
1
1
  import { getId, s4 } from './CommonJs.js';
2
2
  import { renderCssAttr } from './Css.js';
3
3
  import { ToolTip } from './ToolTip.js';
4
- import { getAllChildNodes, s } from './VanillaJs.js';
4
+ import { getAllChildNodes, htmlStrSanitize, s } from './VanillaJs.js';
5
5
 
6
6
  const BtnIcon = {
7
7
  Tokens: {},
@@ -32,7 +32,7 @@ const BtnIcon = {
32
32
  : ''}`;
33
33
  let render = html`<button
34
34
  ${options?.class ? `class="${options.class} ${tokenId}"` : ''}
35
- ${options?.type ? `type="${options.type}"` : ''}
35
+ ${options?.type ? `type="${options.type}"` : `type="button"`}
36
36
  ${options?.style ? `style="${options.style}"` : ''}
37
37
  ${options?.attrs ? `${options.attrs}` : ''}
38
38
  >
@@ -49,6 +49,7 @@ const BtnIcon = {
49
49
  >`
50
50
  : label}
51
51
  </div>
52
+ <label class="hide">${htmlStrSanitize(options.label) ? htmlStrSanitize(options.label) : tokenId}</label>
52
53
  </button>`;
53
54
  if (options.tooltipHtml)
54
55
  setTimeout(() => {
@@ -7,9 +7,9 @@ import { Modal } from './Modal.js';
7
7
  import { NotificationManager } from './NotificationManager.js';
8
8
  import { Panel } from './Panel.js';
9
9
  import { Responsive } from './Responsive.js';
10
- import { listenQueryPathInstance, RouterEvents, setQueryPath } from './Router.js';
10
+ import { listenQueryPathInstance, RouterEvents, setQueryPath, getQueryParams } from './Router.js';
11
11
  import { Translate } from './Translate.js';
12
- import { append, getQueryParams, getTimeZone, htmls, s, sa } from './VanillaJs.js';
12
+ import { append, getTimeZone, htmls, s, sa } from './VanillaJs.js';
13
13
 
14
14
  // https://fullcalendar.io/docs/event-object
15
15
 
@@ -363,7 +363,6 @@ const CalendarCore = {
363
363
  const cid = getQueryParams().cid ? getQueryParams().cid : '';
364
364
  if (lastCid === cid) return;
365
365
  lastCid = cid;
366
- if (options.route === 'home') Modal.homeCid = newInstance(cid);
367
366
  if (s(`.main-body-calendar-${options.idModal}`)) {
368
367
  // if (Auth.getToken())
369
368
  // else getSrrData();
@@ -577,8 +577,7 @@ const getCurrentTrace = () => {
577
577
  try {
578
578
  _stack;
579
579
  } catch (error) {
580
- console.error(error);
581
- return error;
580
+ return error.stack;
582
581
  }
583
582
  };
584
583
 
@@ -1,13 +1,14 @@
1
1
  import { marked } from 'marked';
2
2
  import { FileService } from '../../services/file/file.service.js';
3
- import { append, getBlobFromUint8ArrayFile, getQueryParams, getRawContentFile, htmls, s } from './VanillaJs.js';
4
- import { titleFormatted } from './CommonJs.js';
3
+ import { append, getBlobFromUint8ArrayFile, getRawContentFile, htmls, s } from './VanillaJs.js';
4
+ import { s4 } from './CommonJs.js';
5
5
  import { Translate } from './Translate.js';
6
6
  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
10
  import { imageShimmer, renderChessPattern, renderCssAttr, styleFactory } from './Css.js';
11
+ import { getQueryParams } from './Router.js';
11
12
 
12
13
  const logger = loggerFactory(import.meta);
13
14
 
@@ -136,12 +137,9 @@ const Content = {
136
137
  case 'svg':
137
138
  case 'gif':
138
139
  case 'png': {
139
- const url = options.url
140
- ? options.url
141
- : file._id
142
- ? getApiBaseUrl({ id: file._id, endpoint: 'file/blob' })
143
- : URL.createObjectURL(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
140
+ const url = Content.urlFactory(options);
144
141
  const imgRender = html`<img
142
+ alt="${file.name ? file.name : `file ${s4()}`}"
145
143
  class="in ${options.class}"
146
144
  ${styleFactory(options.style, `${renderChessPattern(50)}`)}
147
145
  src="${url}"
@@ -150,11 +148,7 @@ const Content = {
150
148
  break;
151
149
  }
152
150
  case 'pdf': {
153
- const url = options.url
154
- ? options.url
155
- : file._id
156
- ? getApiBaseUrl({ id: file._id, endpoint: 'file/blob' })
157
- : URL.createObjectURL(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
151
+ const url = Content.urlFactory(options);
158
152
  render += html`<iframe
159
153
  class="in ${options.class} iframe-${options.idModal}"
160
154
  ${styleFactory(options.style)}
@@ -188,6 +182,15 @@ const Content = {
188
182
  if (options.raw) return render;
189
183
  append(container, render);
190
184
  },
185
+ urlFactory: function (options) {
186
+ return options.url
187
+ ? options.url
188
+ : options.file?.data?.data
189
+ ? URL.createObjectURL(getBlobFromUint8ArrayFile(options.file.data.data, options.file.mimetype))
190
+ : options.file._id
191
+ ? getApiBaseUrl({ id: options.file._id, endpoint: 'file/blob' })
192
+ : null;
193
+ },
191
194
  };
192
195
 
193
196
  export { Content };