underpost 3.1.2 → 3.2.0

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 (98) hide show
  1. package/.env.example +0 -2
  2. package/.github/workflows/ghpkg.ci.yml +4 -4
  3. package/.github/workflows/npmpkg.ci.yml +38 -7
  4. package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -4
  5. package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
  6. package/.github/workflows/release.cd.yml +4 -4
  7. package/CHANGELOG.md +365 -1
  8. package/CLI-HELP.md +55 -3
  9. package/README.md +7 -3
  10. package/bin/build.js +18 -12
  11. package/bin/deploy.js +205 -225
  12. package/bin/file.js +3 -0
  13. package/conf.js +4 -10
  14. package/jsdoc.json +1 -1
  15. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  16. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  17. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  18. package/manifests/deployment/dd-test-development/deployment.yaml +72 -50
  19. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  20. package/manifests/deployment/playwright/deployment.yaml +1 -1
  21. package/nodemon.json +1 -1
  22. package/package.json +21 -14
  23. package/scripts/ports-ls.sh +2 -0
  24. package/scripts/rhel-grpc-setup.sh +56 -0
  25. package/src/api/file/file.ref.json +18 -0
  26. package/src/api/user/user.service.js +8 -7
  27. package/src/cli/cluster.js +7 -7
  28. package/src/cli/db.js +76 -242
  29. package/src/cli/deploy.js +104 -65
  30. package/src/cli/env.js +1 -0
  31. package/src/cli/fs.js +2 -1
  32. package/src/cli/index.js +50 -1
  33. package/src/cli/kubectl.js +211 -0
  34. package/src/cli/release.js +284 -0
  35. package/src/cli/repository.js +328 -112
  36. package/src/cli/run.js +283 -69
  37. package/src/cli/test.js +3 -3
  38. package/src/client/Default.index.js +3 -4
  39. package/src/client/components/core/Alert.js +2 -2
  40. package/src/client/components/core/AppStore.js +69 -0
  41. package/src/client/components/core/CalendarCore.js +2 -2
  42. package/src/client/components/core/Docs.js +9 -2
  43. package/src/client/components/core/DropDown.js +129 -17
  44. package/src/client/components/core/Keyboard.js +2 -2
  45. package/src/client/components/core/LogIn.js +2 -2
  46. package/src/client/components/core/LogOut.js +2 -2
  47. package/src/client/components/core/Modal.js +0 -1
  48. package/src/client/components/core/Panel.js +0 -1
  49. package/src/client/components/core/PanelForm.js +19 -19
  50. package/src/client/components/core/RichText.js +1 -2
  51. package/src/client/components/core/SocketIo.js +82 -29
  52. package/src/client/components/core/SocketIoHandler.js +75 -0
  53. package/src/client/components/core/Stream.js +143 -95
  54. package/src/client/components/core/Webhook.js +40 -7
  55. package/src/client/components/default/AppStoreDefault.js +5 -0
  56. package/src/client/components/default/LogInDefault.js +3 -3
  57. package/src/client/components/default/LogOutDefault.js +2 -2
  58. package/src/client/components/default/MenuDefault.js +5 -5
  59. package/src/client/components/default/SocketIoDefault.js +3 -51
  60. package/src/client/services/core/core.service.js +20 -8
  61. package/src/client/services/user/user.management.js +2 -2
  62. package/src/client/ssr/body/404.js +15 -11
  63. package/src/client/ssr/body/500.js +15 -11
  64. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  65. package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
  66. package/src/client/ssr/pages/Test.js +11 -10
  67. package/src/index.js +24 -1
  68. package/src/runtime/express/Express.js +26 -9
  69. package/src/runtime/lampp/Dockerfile +9 -2
  70. package/src/runtime/lampp/Lampp.js +4 -3
  71. package/src/runtime/wp/Dockerfile +64 -0
  72. package/src/runtime/wp/Wp.js +497 -0
  73. package/src/server/auth.js +30 -6
  74. package/src/server/backup.js +19 -1
  75. package/src/server/client-build-docs.js +51 -110
  76. package/src/server/client-build.js +55 -64
  77. package/src/server/client-formatted.js +109 -57
  78. package/src/server/conf.js +19 -15
  79. package/src/server/ipfs-client.js +24 -1
  80. package/src/server/peer.js +8 -0
  81. package/src/server/runtime.js +25 -1
  82. package/src/server/start.js +21 -8
  83. package/src/ws/IoInterface.js +1 -10
  84. package/src/ws/IoServer.js +14 -33
  85. package/src/ws/core/channels/core.ws.chat.js +65 -20
  86. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  87. package/src/ws/core/channels/core.ws.stream.js +90 -31
  88. package/src/ws/core/core.ws.connection.js +12 -33
  89. package/src/ws/core/core.ws.emit.js +10 -26
  90. package/src/ws/core/core.ws.server.js +25 -58
  91. package/src/ws/default/channels/default.ws.main.js +53 -12
  92. package/src/ws/default/default.ws.connection.js +26 -13
  93. package/src/ws/default/default.ws.server.js +30 -12
  94. package/src/client/components/default/ElementsDefault.js +0 -38
  95. package/src/ws/core/management/core.ws.chat.js +0 -8
  96. package/src/ws/core/management/core.ws.mailer.js +0 -16
  97. package/src/ws/core/management/core.ws.stream.js +0 -8
  98. package/src/ws/default/management/default.ws.main.js +0 -8
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Shared factory for app-specific WebSocket event handlers.
3
+ * Eliminates duplication across per-app SocketIo*.js modules by providing
4
+ * common channel event handling (chat, email-confirmed, etc.).
5
+ *
6
+ * @module client/core/SocketIoHandler
7
+ * @namespace SocketIoHandlerProvider
8
+ */
9
+ import { Account } from './Account.js';
10
+ import { Chat } from './Chat.js';
11
+ import { s4 } from './CommonJs.js';
12
+ import { loggerFactory } from './Logger.js';
13
+ import { SocketIo } from './SocketIo.js';
14
+ import { s } from './VanillaJs.js';
15
+
16
+ const logger = loggerFactory(import.meta);
17
+
18
+ /**
19
+ * @class SocketIoHandlerProvider
20
+ * @classdesc Provides a static factory method to create app-specific SocketIo event handlers
21
+ * from an {@link AppStore} instance. Handles common channel events (chat, email-confirmed)
22
+ * and wires connect/disconnect lifecycle.
23
+ * @memberof SocketIoHandlerProvider
24
+ */
25
+ class SocketIoHandlerProvider {
26
+ /**
27
+ * Creates a standard SocketIo event initialization object for an app module.
28
+ *
29
+ * @static
30
+ * @param {import('./AppStore.js').AppStore} appStore - The app-specific AppStore instance.
31
+ * @returns {{ Init: function(): Promise<void> }} An object with an `Init` method for SocketIo event registration.
32
+ */
33
+ static create(appStore) {
34
+ return {
35
+ Init() {
36
+ return new Promise((resolve) => {
37
+ for (const type of Object.keys(appStore.Data)) {
38
+ SocketIo.Event[type][s4()] = async (args) => {
39
+ args = JSON.parse(args[0]);
40
+ switch (type) {
41
+ case 'chat':
42
+ {
43
+ const idModal = 'modal-chat';
44
+ if (s(`.${idModal}-chat-box`)) Chat.appendChatBox({ idModal, ...args });
45
+ }
46
+ break;
47
+
48
+ default:
49
+ break;
50
+ }
51
+ const { status } = args;
52
+
53
+ switch (status) {
54
+ case 'email-confirmed': {
55
+ const newUser = { ...appStore.Data.user.main.model.user, emailConfirmed: true };
56
+ Account.renderVerifyEmailStatus(newUser);
57
+ Account.triggerUpdateEvent({ user: newUser });
58
+ break;
59
+ }
60
+
61
+ default:
62
+ break;
63
+ }
64
+ };
65
+ }
66
+ SocketIo.Event.connect[s4()] = async (reason) => {};
67
+ SocketIo.Event.disconnect[s4()] = async (reason) => {};
68
+ return resolve();
69
+ });
70
+ },
71
+ };
72
+ }
73
+ }
74
+
75
+ export { SocketIoHandlerProvider };
@@ -1,113 +1,161 @@
1
+ /**
2
+ * Client-side WebRTC stream manager backed by PeerJS.
3
+ * Handles peer creation, media capture, and room-based A/V streaming.
4
+ *
5
+ * @module client/core/Stream
6
+ * @see https://peerjs.com/docs/
7
+ */
8
+
1
9
  import { loggerFactory } from './Logger.js';
2
10
  import { getProxyPath } from './Router.js';
3
11
 
4
- // https://peerjs.com/docs/
5
-
6
12
  const logger = loggerFactory(import.meta);
7
13
 
8
- const Stream = {
9
- Data: {},
10
- createPeerServer: function ({ id }) {
11
- const peerOptions = {
12
- host: location.hostname, // '/'
14
+ /**
15
+ * @class Stream
16
+ * @classdesc Static manager for PeerJS connections and MediaStream lifecycle.
17
+ * Supports multiple concurrent peer sessions keyed by an arbitrary `id`.
18
+ */
19
+ class Stream {
20
+ /** @type {Object.<string, { peer: Peer, options: Object }>} Active peer sessions keyed by session id. */
21
+ static #sessions = {};
22
+
23
+ /**
24
+ * Builds PeerJS connection options from current page location.
25
+ * @returns {{ host: string, port: number, path: string, secure: boolean }}
26
+ */
27
+ static #buildPeerOptions() {
28
+ return {
29
+ host: location.hostname,
13
30
  port: location.protocol === 'https:' ? 443 : location.port ? parseInt(location.port) + 1 : 80,
14
31
  path: `${getProxyPath()}peer`,
15
32
  secure: location.protocol === 'https:',
16
33
  };
17
- logger.info('peerOptions', peerOptions);
18
- this.Data[id] = {
19
- peer: {
20
- peerOptions,
21
- server: new Peer(undefined, peerOptions),
22
- },
23
- };
24
- return this.Data[id].peer;
25
- },
26
- connectToNewUser: function (
27
- mediaType,
28
- id,
29
- userId,
30
- stream,
31
- onConnectStream = (mediaElement) => null,
32
- onDisconnectStream = (mediaElement) => null,
33
- ) {
34
- // This runs when someone joins our room
35
- const call = this.Data[id].peer.server.call(userId, stream); // Call the user who just joined
36
- const mediaElement = this.createMediaElement(mediaType);
34
+ }
37
35
 
38
- call.on('stream', (userMediaStream) => {
39
- onConnectStream(this.renderElementStream(mediaElement, userMediaStream));
40
- });
41
- // If they leave, remove their video
42
- call.on('close', () => {
43
- onDisconnectStream(mediaElement);
44
- });
45
- return { call, mediaElement };
46
- },
47
- handlePeerDisconnect({ id }) {
48
- // manually close the peer connections
49
- for (let conns in this.Data[id].peer.server.connections) {
50
- this.Data[id].peer.server.connections[conns].forEach((conn, index, array) => {
51
- console.log(`closing ${conn.connectionId} peerConnection (${index + 1}/${array.length})`, conn.peerConnection);
52
- if (conn.peerConnection && conn.peerConnection.close) conn.peerConnection.close();
36
+ /**
37
+ * Creates a PeerJS client for the given session id.
38
+ * @param {string} id - Unique session identifier.
39
+ * @returns {{ peer: Peer, options: Object }} The Peer instance and its options.
40
+ */
41
+ static createPeer(id) {
42
+ this.destroyPeer(id);
43
+ const options = this.#buildPeerOptions();
44
+ logger.info('peerOptions', options);
45
+ const peer = new Peer(undefined, options);
46
+ this.#sessions[id] = { peer, options };
47
+ return this.#sessions[id];
48
+ }
49
+
50
+ /**
51
+ * Returns an existing session or `undefined`.
52
+ * @param {string} id
53
+ * @returns {{ peer: Peer, options: Object }|undefined}
54
+ */
55
+ static getSession(id) {
56
+ return this.#sessions[id];
57
+ }
53
58
 
54
- // close it using peerjs methods
55
- if (conn.close) conn.close();
56
- });
59
+ /**
60
+ * Destroys a peer session — closes all connections and the peer itself.
61
+ * @param {string} id - Session identifier.
62
+ */
63
+ static destroyPeer(id) {
64
+ const session = this.#sessions[id];
65
+ if (!session) return;
66
+ const { peer } = session;
67
+ if (peer) {
68
+ for (const key in peer.connections) {
69
+ for (const conn of peer.connections[key]) {
70
+ if (conn.peerConnection?.close) conn.peerConnection.close();
71
+ if (conn.close) conn.close();
72
+ }
73
+ }
74
+ if (!peer.destroyed) peer.destroy();
57
75
  }
58
- },
59
- renderElementStream: function (mediaElement, stream) {
60
- mediaElement.srcObject = stream;
61
- mediaElement.addEventListener('loadedmetadata', () => {
62
- // Play the video as it loads
63
- mediaElement.play();
64
- });
65
- return mediaElement;
66
- // use example:
67
- // videoGrid.append(mediaElement); // Append video element to videoGrid
68
- },
69
- // Access the user's video and audio
70
- getMediaStream: function (
71
- options = {
72
- video: true,
73
- audio: true,
74
- },
75
- ) {
76
- return new Promise((resolve) => {
77
- navigator.mediaDevices
78
- .getUserMedia(options)
79
- .then((stream) => resolve(stream))
80
- .catch((error) => {
81
- logger.error(error);
82
- resolve(undefined);
83
- });
84
- });
85
- },
86
- removeMediaStream: function (stream) {
87
- // later you can do below
88
- // stop both video and audio
89
- stream.getTracks().forEach((track) => {
90
- track.stop();
76
+ delete this.#sessions[id];
77
+ }
78
+
79
+ /**
80
+ * Calls a remote peer and streams local media to them.
81
+ * @param {string} id - Session identifier.
82
+ * @param {string} remotePeerId - The remote peer's ID.
83
+ * @param {MediaStream} localStream - Local media stream to send.
84
+ * @param {Object} [callbacks]
85
+ * @param {function(HTMLMediaElement): void} [callbacks.onStream] - Called when remote stream arrives.
86
+ * @param {function(HTMLMediaElement): void} [callbacks.onClose] - Called when the call closes.
87
+ * @returns {{ call: import('peerjs').MediaConnection, element: HTMLVideoElement }}
88
+ */
89
+ static callPeer(id, remotePeerId, localStream, { onStream, onClose } = {}) {
90
+ const session = this.#sessions[id];
91
+ if (!session) throw new Error(`No peer session "${id}"`);
92
+ const call = session.peer.call(remotePeerId, localStream);
93
+ const element = this.createVideoElement();
94
+ call.on('stream', (remoteStream) => {
95
+ this.attachStream(element, remoteStream);
96
+ onStream?.(element);
91
97
  });
92
- // stop only audio
93
- // stream.getAudioTracks()[0].stop();
94
- // stop only video
95
- // stream.getVideoTracks()[0].stop();
96
- },
97
- createMediaElement: function (mediaType) {
98
- let mediaElement;
99
- switch (mediaType) {
100
- case 'audio-video':
101
- mediaElement = document.createElement('video'); // Create a new audio/video tag to show our audio/video
102
- // mediaElement.muted = true; // Mute ourselves on our end so there is no feedback loop
98
+ call.on('close', () => onClose?.(element));
99
+ return { call, element };
100
+ }
101
+
102
+ // ── Media helpers ────────────────────────────────────────────────
103
103
 
104
- break;
104
+ /**
105
+ * Requests user media (camera / microphone) with graceful fallback.
106
+ * Falls back to video-only if audio is unavailable (NotReadableError).
107
+ * @param {{ video?: boolean|MediaTrackConstraints, audio?: boolean|MediaTrackConstraints }} [constraints]
108
+ * @returns {Promise<MediaStream|undefined>}
109
+ */
110
+ static async getMediaStream(constraints = { video: true, audio: true }) {
111
+ const fallbacks = [
112
+ constraints,
113
+ constraints.audio ? { video: constraints.video, audio: false } : null,
114
+ constraints.video ? { video: false, audio: constraints.audio } : null,
115
+ ].filter(Boolean);
105
116
 
106
- default:
107
- break;
117
+ for (const c of fallbacks) {
118
+ try {
119
+ return await navigator.mediaDevices.getUserMedia(c);
120
+ } catch (err) {
121
+ logger.warn(`getUserMedia failed (${err.name}): ${err.message}`, c);
122
+ }
108
123
  }
109
- return mediaElement;
110
- },
111
- };
124
+ logger.error('All media capture attempts failed');
125
+ return undefined;
126
+ }
127
+
128
+ /**
129
+ * Stops all tracks of a MediaStream.
130
+ * @param {MediaStream} stream
131
+ */
132
+ static stopStream(stream) {
133
+ if (!stream) return;
134
+ stream.getTracks().forEach((track) => track.stop());
135
+ }
136
+
137
+ /**
138
+ * Creates a `<video>` element pre-configured for inline autoplay.
139
+ * @returns {HTMLVideoElement}
140
+ */
141
+ static createVideoElement() {
142
+ const video = document.createElement('video');
143
+ video.playsInline = true;
144
+ video.autoplay = true;
145
+ return video;
146
+ }
147
+
148
+ /**
149
+ * Attaches a MediaStream to a media element and starts playback.
150
+ * @param {HTMLMediaElement} element
151
+ * @param {MediaStream} stream
152
+ * @returns {HTMLMediaElement}
153
+ */
154
+ static attachStream(element, stream) {
155
+ element.srcObject = stream;
156
+ element.addEventListener('loadedmetadata', () => element.play());
157
+ return element;
158
+ }
159
+ }
112
160
 
113
161
  export { Stream };
@@ -1,7 +1,31 @@
1
+ /**
2
+ * WebSocket session registration bridge.
3
+ * Registers and unregisters the authenticated user's identity with the server-side
4
+ * WebSocket management layer, enabling targeted real-time pushes (e.g. email confirmation).
5
+ *
6
+ * @module client/core/Webhook
7
+ * @namespace WebhookProvider
8
+ */
1
9
  import { SocketIo } from './SocketIo.js';
2
10
 
3
- const Webhook = {
4
- register: async function (options = { user: {} }) {
11
+ /**
12
+ * @class WebhookProvider
13
+ * @classdesc Provides static methods to register/unregister the authenticated user
14
+ * with the server-side WebSocket session management channels.
15
+ * @memberof WebhookProvider
16
+ */
17
+ class WebhookProvider {
18
+ /**
19
+ * Registers the authenticated user with the server-side WebSocket channels.
20
+ * Creates a user↔socket mapping on the server, enabling targeted events.
21
+ *
22
+ * @static
23
+ * @async
24
+ * @param {Object} options - Registration options.
25
+ * @param {Object} options.user - The authenticated user object to register.
26
+ * @returns {Promise<void>}
27
+ */
28
+ static async register(options = { user: {} }) {
5
29
  const { user } = options;
6
30
  SocketIo.Emit('mailer', {
7
31
  status: 'register-user',
@@ -11,15 +35,24 @@ const Webhook = {
11
35
  status: 'register-user',
12
36
  user,
13
37
  });
14
- },
15
- unregister: async function () {
38
+ }
39
+
40
+ /**
41
+ * Unregisters the current user from server-side WebSocket channels.
42
+ * Cleans up the user↔socket mapping on the server.
43
+ *
44
+ * @static
45
+ * @async
46
+ * @returns {Promise<void>}
47
+ */
48
+ static async unregister() {
16
49
  SocketIo.Emit('mailer', {
17
50
  status: 'unregister-user',
18
51
  });
19
52
  SocketIo.Emit('user', {
20
53
  status: 'unregister-user',
21
54
  });
22
- },
23
- };
55
+ }
56
+ }
24
57
 
25
- export { Webhook };
58
+ export { WebhookProvider };
@@ -0,0 +1,5 @@
1
+ import { AppStore } from '../core/AppStore.js';
2
+
3
+ const AppStoreDefault = AppStore.create();
4
+
5
+ export { AppStoreDefault };
@@ -1,14 +1,14 @@
1
1
  import { Auth } from '../core/Auth.js';
2
2
  import { LogIn } from '../core/LogIn.js';
3
- import { ElementsDefault } from './ElementsDefault.js';
3
+ import { AppStoreDefault } from './AppStoreDefault.js';
4
4
 
5
5
  const LogInDefault = async function () {
6
6
  LogIn.Event['LogInDefault'] = async (options) => {
7
7
  const { token, user } = options;
8
- ElementsDefault.Data.user.main.model.user = user;
8
+ AppStoreDefault.Data.user.main.model.user = user;
9
9
  };
10
10
  const { user } = await Auth.sessionIn();
11
- ElementsDefault.Data.user.main.model.user = user;
11
+ AppStoreDefault.Data.user.main.model.user = user;
12
12
  };
13
13
 
14
14
  export { LogInDefault };
@@ -1,9 +1,9 @@
1
1
  import { LogOut } from '../core/LogOut.js';
2
- import { ElementsDefault } from './ElementsDefault.js';
2
+ import { AppStoreDefault } from './AppStoreDefault.js';
3
3
 
4
4
  const LogOutDefault = async function () {
5
5
  LogOut.Event['LogOutDefault'] = async (result = { user: { _id: '' } }) => {
6
- ElementsDefault.Data.user.main.model.user = result.user;
6
+ AppStoreDefault.Data.user.main.model.user = result.user;
7
7
  };
8
8
  };
9
9
 
@@ -10,7 +10,7 @@ import { SignUp } from '../core/SignUp.js';
10
10
  import { Translate } from '../core/Translate.js';
11
11
  import { htmls, s } from '../core/VanillaJs.js';
12
12
  import { extractUsernameFromPath, getProxyPath, getQueryParams } from '../core/Router.js';
13
- import { ElementsDefault } from './ElementsDefault.js';
13
+ import { AppStoreDefault } from './AppStoreDefault.js';
14
14
  import Sortable from 'sortablejs';
15
15
  import { RouterDefault, BannerAppTemplate } from './RoutesDefault.js';
16
16
  import { SettingsDefault } from './SettingsDefault.js';
@@ -551,7 +551,7 @@ const MenuDefault = {
551
551
  html: async () =>
552
552
  await Account.Render({
553
553
  idModal: 'modal-account',
554
- user: ElementsDefault.Data.user.main.model.user,
554
+ user: AppStoreDefault.Data.user.main.model.user,
555
555
  disabled: [],
556
556
  }),
557
557
  handleType: 'bar',
@@ -565,7 +565,7 @@ const MenuDefault = {
565
565
  EventsUI.onClick(`.main-btn-public-profile`, async () => {
566
566
  const { barConfig } = await Themes[Css.currentTheme]();
567
567
  const idModal = 'modal-public-profile';
568
- const user = ElementsDefault.Data.user.main.model.user;
568
+ const user = AppStoreDefault.Data.user.main.model.user;
569
569
 
570
570
  // Check if modal already exists
571
571
  const existingModal = s(`.${idModal}`);
@@ -635,7 +635,7 @@ const MenuDefault = {
635
635
  text: Translate.Render('recover'),
636
636
  }),
637
637
  html: async () =>
638
- await Recover.Render({ idModal: 'modal-recover', user: ElementsDefault.Data.user.main.model.user }),
638
+ await Recover.Render({ idModal: 'modal-recover', user: AppStoreDefault.Data.user.main.model.user }),
639
639
  handleType: 'bar',
640
640
  maximize: true,
641
641
  mode: 'view',
@@ -723,7 +723,7 @@ const MenuDefault = {
723
723
  await PanelForm.instance({
724
724
  idPanel: 'default-blog',
725
725
  defaultUrlImage: `${getProxyPath()}android-chrome-96x96.png`,
726
- Elements: ElementsDefault,
726
+ appStore: AppStoreDefault,
727
727
  parentIdModal: idModal,
728
728
  scrollClassContainer: `html-${idModal}`,
729
729
  route: routeModal,
@@ -1,54 +1,6 @@
1
- import { Account } from '../core/Account.js';
2
- import { Chat } from '../core/Chat.js';
3
- import { s4 } from '../core/CommonJs.js';
4
- import { loggerFactory } from '../core/Logger.js';
5
- import { SocketIo } from '../core/SocketIo.js';
6
- import { s } from '../core/VanillaJs.js';
7
- import { ElementsDefault } from './ElementsDefault.js';
1
+ import { SocketIoHandlerProvider } from '../core/SocketIoHandler.js';
2
+ import { AppStoreDefault } from './AppStoreDefault.js';
8
3
 
9
- const logger = loggerFactory(import.meta);
10
-
11
- const SocketIoDefault = {
12
- Init: function () {
13
- return new Promise((resolve) => {
14
- for (const type of Object.keys(ElementsDefault.Data)) {
15
- SocketIo.Event[type][s4()] = async (args) => {
16
- args = JSON.parse(args[0]);
17
- switch (type) {
18
- case 'chat':
19
- {
20
- const idModal = 'modal-chat';
21
- if (s(`.${idModal}-chat-box`)) Chat.appendChatBox({ idModal, ...args });
22
- }
23
- break;
24
-
25
- default:
26
- break;
27
- }
28
- const { status } = args;
29
-
30
- switch (status) {
31
- case 'email-confirmed': {
32
- const newUser = { ...ElementsDefault.Data.user.main.model.user, emailConfirmed: true };
33
- Account.renderVerifyEmailStatus(newUser);
34
- Account.triggerUpdateEvent({ user: newUser });
35
- break;
36
- }
37
-
38
- default:
39
- break;
40
- }
41
- };
42
- }
43
- SocketIo.Event.connect[s4()] = async (reason) => {
44
- // ElementsDefault.Init({ type, id, element });
45
- };
46
- SocketIo.Event.disconnect[s4()] = async (reason) => {
47
- // ElementsDefault.removeAll();
48
- };
49
- return resolve();
50
- });
51
- },
52
- };
4
+ const SocketIoDefault = SocketIoHandlerProvider.create(AppStoreDefault);
53
5
 
54
6
  export { SocketIoDefault };
@@ -17,6 +17,19 @@ const endpoint = 'core';
17
17
 
18
18
  // https://developer.mozilla.org/en-US/docs/Web/API/AbortController
19
19
 
20
+ /**
21
+ * Returns the normalized proxy path from renderPayload (always trailing `/`).
22
+ * Falls back to `null` when no apiBaseProxyPath is set.
23
+ * @memberof CoreServiceClient
24
+ * @return {string|null}
25
+ */
26
+ const getApiBaseProxyPath = () =>
27
+ window.renderPayload?.apiBaseProxyPath
28
+ ? window.renderPayload.apiBaseProxyPath === '/'
29
+ ? window.renderPayload.apiBaseProxyPath
30
+ : `${window.renderPayload.apiBaseProxyPath}/`
31
+ : null;
32
+
20
33
  /**
21
34
  * Gets the base host for API requests.
22
35
  * Uses the apiBaseHost from renderPayload if available, otherwise falls back to location.host.
@@ -35,13 +48,7 @@ const getBaseHost = () => (window.renderPayload?.apiBaseHost ? window.renderPayl
35
48
  */
36
49
  const getApiBasePath = (options) =>
37
50
  `${
38
- options?.proxyPath
39
- ? `/${options.proxyPath}/`
40
- : window.renderPayload?.apiBaseProxyPath
41
- ? window.renderPayload.apiBaseProxyPath == '/'
42
- ? window.renderPayload.apiBaseProxyPath
43
- : `${window.renderPayload.apiBaseProxyPath}/`
44
- : getProxyPath()
51
+ options?.proxyPath ? `/${options.proxyPath}/` : getApiBaseProxyPath() || getProxyPath()
45
52
  }${window.renderPayload?.apiBasePath ? window.renderPayload.apiBasePath : 'api'}/`;
46
53
 
47
54
  /**
@@ -65,7 +72,11 @@ const getApiBaseUrl = (options = { id: '', endpoint: '', proxyPath: '' }) =>
65
72
  * @memberof CoreServiceClient
66
73
  * @return {string} The WebSocket base path.
67
74
  */
68
- const getWsBasePath = () => (getProxyPath() !== '/' ? `${getProxyPath()}socket.io/` : '/socket.io/');
75
+ const getWsBasePath = () => {
76
+ const proxyPath = getApiBaseProxyPath();
77
+ if (proxyPath) return proxyPath !== '/' ? `${proxyPath}socket.io/` : '/socket.io/';
78
+ return getProxyPath() !== '/' ? `${getProxyPath()}socket.io/` : '/socket.io/';
79
+ };
69
80
 
70
81
  /**
71
82
  * Constructs the full WebSocket base URL for connections.
@@ -321,6 +332,7 @@ export {
321
332
  payloadFactory,
322
333
  buildQueryUrl,
323
334
  getBaseHost,
335
+ getApiBaseProxyPath,
324
336
  getApiBasePath,
325
337
  getApiBaseUrl,
326
338
  getWsBasePath,
@@ -2,8 +2,8 @@ import { DefaultManagement } from '../default/default.management.js';
2
2
  import { UserService } from './user.service.js';
3
3
 
4
4
  const UserManagement = {
5
- RenderTable: async ({ Elements }) => {
6
- const user = Elements.Data.user.main.model.user;
5
+ RenderTable: async ({ appStore }) => {
6
+ const user = appStore.Data.user.main.model.user;
7
7
  const { role } = user;
8
8
  return await DefaultManagement.RenderTable({
9
9
  idModal: 'modal-user-management',
@@ -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>`;