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.
- package/.env.example +0 -2
- package/.github/workflows/ghpkg.ci.yml +4 -4
- package/.github/workflows/npmpkg.ci.yml +38 -7
- package/.github/workflows/pwa-microservices-template-page.cd.yml +3 -4
- package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
- package/.github/workflows/release.cd.yml +4 -4
- package/CHANGELOG.md +365 -1
- package/CLI-HELP.md +55 -3
- package/README.md +7 -3
- package/bin/build.js +18 -12
- package/bin/deploy.js +205 -225
- package/bin/file.js +3 -0
- package/conf.js +4 -10
- package/jsdoc.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +72 -50
- package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
- package/manifests/deployment/playwright/deployment.yaml +1 -1
- package/nodemon.json +1 -1
- package/package.json +21 -14
- package/scripts/ports-ls.sh +2 -0
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/src/api/file/file.ref.json +18 -0
- package/src/api/user/user.service.js +8 -7
- package/src/cli/cluster.js +7 -7
- package/src/cli/db.js +76 -242
- package/src/cli/deploy.js +104 -65
- package/src/cli/env.js +1 -0
- package/src/cli/fs.js +2 -1
- package/src/cli/index.js +50 -1
- package/src/cli/kubectl.js +211 -0
- package/src/cli/release.js +284 -0
- package/src/cli/repository.js +328 -112
- package/src/cli/run.js +283 -69
- package/src/cli/test.js +3 -3
- package/src/client/Default.index.js +3 -4
- package/src/client/components/core/Alert.js +2 -2
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/CalendarCore.js +2 -2
- package/src/client/components/core/Docs.js +9 -2
- package/src/client/components/core/DropDown.js +129 -17
- package/src/client/components/core/Keyboard.js +2 -2
- package/src/client/components/core/LogIn.js +2 -2
- package/src/client/components/core/LogOut.js +2 -2
- package/src/client/components/core/Modal.js +0 -1
- package/src/client/components/core/Panel.js +0 -1
- package/src/client/components/core/PanelForm.js +19 -19
- package/src/client/components/core/RichText.js +1 -2
- package/src/client/components/core/SocketIo.js +82 -29
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +143 -95
- package/src/client/components/core/Webhook.js +40 -7
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/LogInDefault.js +3 -3
- package/src/client/components/default/LogOutDefault.js +2 -2
- package/src/client/components/default/MenuDefault.js +5 -5
- package/src/client/components/default/SocketIoDefault.js +3 -51
- package/src/client/services/core/core.service.js +20 -8
- package/src/client/services/user/user.management.js +2 -2
- package/src/client/ssr/body/404.js +15 -11
- package/src/client/ssr/body/500.js +15 -11
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
- package/src/client/ssr/pages/Test.js +11 -10
- package/src/index.js +24 -1
- package/src/runtime/express/Express.js +26 -9
- package/src/runtime/lampp/Dockerfile +9 -2
- package/src/runtime/lampp/Lampp.js +4 -3
- package/src/runtime/wp/Dockerfile +64 -0
- package/src/runtime/wp/Wp.js +497 -0
- package/src/server/auth.js +30 -6
- package/src/server/backup.js +19 -1
- package/src/server/client-build-docs.js +51 -110
- package/src/server/client-build.js +55 -64
- package/src/server/client-formatted.js +109 -57
- package/src/server/conf.js +19 -15
- package/src/server/ipfs-client.js +24 -1
- package/src/server/peer.js +8 -0
- package/src/server/runtime.js +25 -1
- package/src/server/start.js +21 -8
- package/src/ws/IoInterface.js +1 -10
- package/src/ws/IoServer.js +14 -33
- package/src/ws/core/channels/core.ws.chat.js +65 -20
- package/src/ws/core/channels/core.ws.mailer.js +113 -32
- package/src/ws/core/channels/core.ws.stream.js +90 -31
- package/src/ws/core/core.ws.connection.js +12 -33
- package/src/ws/core/core.ws.emit.js +10 -26
- package/src/ws/core/core.ws.server.js +25 -58
- package/src/ws/default/channels/default.ws.main.js +53 -12
- package/src/ws/default/default.ws.connection.js +26 -13
- package/src/ws/default/default.ws.server.js +30 -12
- package/src/client/components/default/ElementsDefault.js +0 -38
- package/src/ws/core/management/core.ws.chat.js +0 -8
- package/src/ws/core/management/core.ws.mailer.js +0 -16
- package/src/ws/core/management/core.ws.stream.js +0 -8
- package/src/ws/default/management/default.ws.main.js +0 -8
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core per-app state store for WebSocket channel data.
|
|
3
|
+
*
|
|
4
|
+
* @module client/core/AppStore
|
|
5
|
+
* @namespace AppStore
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @class AppStore
|
|
10
|
+
* @classdesc Per-app singleton state store for WebSocket channel data and authenticated user state.
|
|
11
|
+
*
|
|
12
|
+
* Usage: `AppStoreX.Data.user.main.model.user` — the authenticated user object.
|
|
13
|
+
* `AppStoreX.Data` keys (`chat`, `mailer`, `stream`, etc.) — channel definitions for `SocketIo.Init`.
|
|
14
|
+
* @memberof AppStore
|
|
15
|
+
*/
|
|
16
|
+
class AppStore {
|
|
17
|
+
/**
|
|
18
|
+
* Channel data map, keyed by channel name (e.g. `user`, `chat`, `mailer`).
|
|
19
|
+
* The `user` channel always contains `{ main: { model: { user: { _id: '' } } } }`.
|
|
20
|
+
*
|
|
21
|
+
* @type {Object.<string, Object>}
|
|
22
|
+
*/
|
|
23
|
+
Data;
|
|
24
|
+
|
|
25
|
+
/** @private @type {function(): Object} */
|
|
26
|
+
#initialStateFactory;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new AppStore instance.
|
|
30
|
+
*
|
|
31
|
+
* @param {function(): Object} initialStateFactory - Factory function returning the initial data shape.
|
|
32
|
+
* Must return at least `{ user: { main: { model: { user: { _id: '' } } } } }`.
|
|
33
|
+
*/
|
|
34
|
+
constructor(initialStateFactory) {
|
|
35
|
+
this.#initialStateFactory = initialStateFactory;
|
|
36
|
+
this.Data = initialStateFactory();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resets `Data` to its initial state.
|
|
41
|
+
*
|
|
42
|
+
* @returns {void}
|
|
43
|
+
*/
|
|
44
|
+
reset() {
|
|
45
|
+
this.Data = this.#initialStateFactory();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates an AppStore with the standard channel layout.
|
|
50
|
+
* Always includes `user`, `chat`, and `mailer` channels.
|
|
51
|
+
*
|
|
52
|
+
* @static
|
|
53
|
+
* @param {...string} extraChannels - Additional channel names (e.g. `'stream'`).
|
|
54
|
+
* @returns {AppStore}
|
|
55
|
+
*/
|
|
56
|
+
static create(...extraChannels) {
|
|
57
|
+
return new AppStore(() => {
|
|
58
|
+
const state = {
|
|
59
|
+
user: { main: { model: { user: { _id: '' } } } },
|
|
60
|
+
chat: {},
|
|
61
|
+
mailer: {},
|
|
62
|
+
};
|
|
63
|
+
for (const ch of extraChannels) state[ch] = {};
|
|
64
|
+
return state;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { AppStore };
|
|
@@ -25,7 +25,7 @@ const eventDateFactory = (event) =>
|
|
|
25
25
|
const CalendarCore = {
|
|
26
26
|
RenderStyle: async function () {},
|
|
27
27
|
Data: {},
|
|
28
|
-
Render: async function (options = { idModal: '',
|
|
28
|
+
Render: async function (options = { idModal: '', appStore: {}, hiddenDates: [] }) {
|
|
29
29
|
this.Data[options.idModal] = {
|
|
30
30
|
data: [],
|
|
31
31
|
originData: [],
|
|
@@ -49,7 +49,7 @@ const CalendarCore = {
|
|
|
49
49
|
this.Data[options.idModal].filesData = [];
|
|
50
50
|
this.Data[options.idModal].originData = newInstance(resultData);
|
|
51
51
|
this.Data[options.idModal].data = resultData.map((o) => {
|
|
52
|
-
if (o.creatorUserId && options.
|
|
52
|
+
if (o.creatorUserId && options.appStore.Data.user.main.model.user._id === o.creatorUserId) o.tools = true;
|
|
53
53
|
o.id = o._id;
|
|
54
54
|
|
|
55
55
|
this.Data[options.idModal].filesData.push({});
|
|
@@ -24,11 +24,18 @@ const Docs = {
|
|
|
24
24
|
html: async () => {
|
|
25
25
|
if (docData.renderHtml) return await docData.renderHtml();
|
|
26
26
|
return html`
|
|
27
|
+
<style>
|
|
28
|
+
.iframe-${ModalId} {
|
|
29
|
+
width: 100%;
|
|
30
|
+
border: none;
|
|
31
|
+
background: white;
|
|
32
|
+
display: block;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
27
35
|
<iframe
|
|
28
36
|
class="in iframe-${ModalId}"
|
|
29
|
-
style="width: 100%; border: none; background: white; display: block"
|
|
30
37
|
src="${docData.url()}"
|
|
31
|
-
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-popups-to-escape-sandbox"
|
|
38
|
+
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-popups-to-escape-sandbox allow-top-navigation"
|
|
32
39
|
>
|
|
33
40
|
</iframe>
|
|
34
41
|
`;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Badge } from './Badge.js';
|
|
1
2
|
import { getId, newInstance } from './CommonJs.js';
|
|
3
|
+
import { darkTheme, ThemeEvents } from './Css.js';
|
|
2
4
|
import { Input } from './Input.js';
|
|
3
5
|
import { ToggleSwitch } from './ToggleSwitch.js';
|
|
4
6
|
import { Translate } from './Translate.js';
|
|
@@ -15,17 +17,75 @@ const DropDown = {
|
|
|
15
17
|
originData: options.data ? newInstance(options.data) : [],
|
|
16
18
|
};
|
|
17
19
|
|
|
20
|
+
const _renderSelectedBadges = async () => {
|
|
21
|
+
if (options.type !== 'checkbox') return;
|
|
22
|
+
const container = s(`.dropdown-current-${id}`);
|
|
23
|
+
if (!container) return;
|
|
24
|
+
const selected = Object.entries(DropDown.Tokens[id].oncheckvalues);
|
|
25
|
+
if (selected.length === 0) {
|
|
26
|
+
htmls(`.dropdown-current-${id}`, '');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let badgesHtml = '';
|
|
30
|
+
for (const [key, val] of selected) {
|
|
31
|
+
badgesHtml += html`<span class="inl" style="display:inline-flex;align-items:center;margin:2px;">
|
|
32
|
+
${await Badge.Render({
|
|
33
|
+
text: html`<i class="fa-solid fa-tag" style="margin-right:3px;font-size:9px;"></i>${val.display}`,
|
|
34
|
+
style: {
|
|
35
|
+
background: darkTheme ? '#335' : '#cde',
|
|
36
|
+
color: darkTheme ? '#adf' : '#246',
|
|
37
|
+
'border-radius': '4px',
|
|
38
|
+
'font-size': '11px',
|
|
39
|
+
height: 'auto',
|
|
40
|
+
'min-width': 'auto',
|
|
41
|
+
},
|
|
42
|
+
})}
|
|
43
|
+
<span
|
|
44
|
+
class="dd-badge-del-${id}"
|
|
45
|
+
data-key="${key}"
|
|
46
|
+
style="cursor:pointer;padding:0 4px;font-size:14px;color:${darkTheme ? '#f88' : '#a00'};line-height:1;"
|
|
47
|
+
>×</span
|
|
48
|
+
>
|
|
49
|
+
</span>`;
|
|
50
|
+
}
|
|
51
|
+
htmls(`.dropdown-current-${id}`, badgesHtml);
|
|
52
|
+
container.querySelectorAll(`.dd-badge-del-${id}`).forEach((btn) => {
|
|
53
|
+
btn.onclick = async (e) => {
|
|
54
|
+
e.stopPropagation();
|
|
55
|
+
const key = btn.dataset.key;
|
|
56
|
+
delete DropDown.Tokens[id].oncheckvalues[key];
|
|
57
|
+
const dataItem = options.data.find((d) => d.value.trim().replaceAll(' ', '-') === key);
|
|
58
|
+
if (dataItem) dataItem.checked = false;
|
|
59
|
+
if (ToggleSwitch.Tokens[`checkbox-role-${key}`]) {
|
|
60
|
+
const checkbox = s(`.checkbox-role-${key}-checkbox`);
|
|
61
|
+
if (checkbox && checkbox.checked) ToggleSwitch.Tokens[`checkbox-role-${key}`].click();
|
|
62
|
+
}
|
|
63
|
+
DropDown.Tokens[id].value = Object.values(DropDown.Tokens[id].oncheckvalues).map((v) => v.data);
|
|
64
|
+
s(`.${id}`).value = DropDown.Tokens[id].value;
|
|
65
|
+
await _renderSelectedBadges();
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
DropDown.Tokens[id]._renderSelectedBadges = _renderSelectedBadges;
|
|
70
|
+
|
|
18
71
|
options.data.push({
|
|
19
72
|
value: 'reset',
|
|
20
73
|
display: html`<i class="fa-solid fa-broom"></i> ${Translate.Render('clear')}`,
|
|
21
74
|
onClick: () => {
|
|
22
75
|
console.log('DropDown onClick', this.value);
|
|
23
76
|
if (options && options.resetOnClick) options.resetOnClick();
|
|
24
|
-
if (options && options.type === 'checkbox')
|
|
25
|
-
|
|
26
|
-
|
|
77
|
+
if (options && options.type === 'checkbox') {
|
|
78
|
+
if (options.serviceProvider) {
|
|
79
|
+
DropDown.Tokens[id].oncheckvalues = {};
|
|
80
|
+
DropDown.Tokens[id].value = [];
|
|
81
|
+
htmls(`.dropdown-current-${id}`, '');
|
|
82
|
+
htmls(`.${id}-render-container`, '');
|
|
83
|
+
} else {
|
|
84
|
+
for (const opt of DropDown.Tokens[id].value) {
|
|
85
|
+
s(`.dropdown-option-${id}-${opt}`).click();
|
|
86
|
+
}
|
|
27
87
|
}
|
|
28
|
-
else this.Tokens[id].value = undefined;
|
|
88
|
+
} else this.Tokens[id].value = undefined;
|
|
29
89
|
},
|
|
30
90
|
});
|
|
31
91
|
|
|
@@ -52,7 +112,7 @@ const DropDown = {
|
|
|
52
112
|
const i = index;
|
|
53
113
|
const valueDisplay = optionData.value.trim().replaceAll(' ', '-');
|
|
54
114
|
setTimeout(() => {
|
|
55
|
-
const onclick = (e) => {
|
|
115
|
+
const onclick = async (e) => {
|
|
56
116
|
if (options && options.lastSelectClass && s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`)) {
|
|
57
117
|
s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`).classList.remove(options.lastSelectClass);
|
|
58
118
|
}
|
|
@@ -72,22 +132,18 @@ const DropDown = {
|
|
|
72
132
|
if (optionData.value !== 'close') {
|
|
73
133
|
if (optionData.value !== 'reset') {
|
|
74
134
|
if (options.type === 'checkbox') {
|
|
75
|
-
|
|
76
|
-
// .filter((d) => d.checked)
|
|
77
|
-
// .map((v, i, a) => `${v.display}${i < a.length - 1 ? ',' : ''}`)
|
|
78
|
-
// .join('');
|
|
79
|
-
const value = Object.keys(DropDown.Tokens[id].oncheckvalues);
|
|
80
|
-
htmls(
|
|
81
|
-
`.dropdown-current-${id}`,
|
|
82
|
-
value.map((v) => DropDown.Tokens[id].originData.find((_v) => _v.value === v).display),
|
|
83
|
-
);
|
|
135
|
+
_renderSelectedBadges();
|
|
84
136
|
} else {
|
|
85
137
|
htmls(`.dropdown-current-${id}`, optionData.display);
|
|
86
138
|
}
|
|
87
139
|
} else htmls(`.dropdown-current-${id}`, '');
|
|
88
140
|
|
|
89
141
|
this.Tokens[id].value =
|
|
90
|
-
options.type === 'checkbox'
|
|
142
|
+
options.type === 'checkbox'
|
|
143
|
+
? options.serviceProvider
|
|
144
|
+
? Object.values(DropDown.Tokens[id].oncheckvalues).map((v) => v.data)
|
|
145
|
+
: data.filter((d) => d.checked).map((d) => d.data)
|
|
146
|
+
: optionData.data;
|
|
91
147
|
|
|
92
148
|
console.warn('current value dropdown id:' + id, this.Tokens[id].value);
|
|
93
149
|
|
|
@@ -126,7 +182,11 @@ const DropDown = {
|
|
|
126
182
|
},
|
|
127
183
|
checked: () => {
|
|
128
184
|
optionData.checked = true;
|
|
129
|
-
DropDown.Tokens[id].oncheckvalues[valueDisplay] = {
|
|
185
|
+
DropDown.Tokens[id].oncheckvalues[valueDisplay] = {
|
|
186
|
+
data: optionData.data,
|
|
187
|
+
display: optionData.display,
|
|
188
|
+
value: optionData.value,
|
|
189
|
+
};
|
|
130
190
|
},
|
|
131
191
|
},
|
|
132
192
|
})}
|
|
@@ -153,6 +213,10 @@ const DropDown = {
|
|
|
153
213
|
s(`.dropdown-current-${id}`).onclick = switchOptionsPanel;
|
|
154
214
|
if (options && options.open) switchOptionsPanel();
|
|
155
215
|
|
|
216
|
+
if (options.type === 'checkbox') {
|
|
217
|
+
ThemeEvents[`dropdown-badge-${id}`] = () => _renderSelectedBadges();
|
|
218
|
+
}
|
|
219
|
+
|
|
156
220
|
const dropDownSearchHandle = async () => {
|
|
157
221
|
const _data = [];
|
|
158
222
|
if (!s(`.search-box-${id}`)) return;
|
|
@@ -160,6 +224,12 @@ const DropDown = {
|
|
|
160
224
|
let _value = s(`.search-box-${id}`).value.toLowerCase();
|
|
161
225
|
|
|
162
226
|
for (const objData of options.data) {
|
|
227
|
+
if (
|
|
228
|
+
options.excludeSelected &&
|
|
229
|
+
options.type === 'checkbox' &&
|
|
230
|
+
DropDown.Tokens[id].oncheckvalues[objData.value.trim().replaceAll(' ', '-')]
|
|
231
|
+
)
|
|
232
|
+
continue;
|
|
163
233
|
const objValue = objData.value.toLowerCase();
|
|
164
234
|
if (
|
|
165
235
|
objValue.match(_value) ||
|
|
@@ -186,7 +256,49 @@ const DropDown = {
|
|
|
186
256
|
}
|
|
187
257
|
};
|
|
188
258
|
|
|
189
|
-
|
|
259
|
+
if (options.serviceProvider) {
|
|
260
|
+
let serviceSearchTimeout = null;
|
|
261
|
+
s(`.search-box-${id}`).oninput = () => {
|
|
262
|
+
clearTimeout(serviceSearchTimeout);
|
|
263
|
+
const q = s(`.search-box-${id}`).value.trim();
|
|
264
|
+
if (!q) {
|
|
265
|
+
htmls(`.${id}-render-container`, '');
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
serviceSearchTimeout = setTimeout(async () => {
|
|
269
|
+
try {
|
|
270
|
+
let results = await options.serviceProvider(q);
|
|
271
|
+
if (options.type === 'checkbox') {
|
|
272
|
+
if (options.excludeSelected) {
|
|
273
|
+
const selectedKeys = Object.keys(DropDown.Tokens[id].oncheckvalues);
|
|
274
|
+
results = results.filter((item) => !selectedKeys.includes(item.value.trim().replaceAll(' ', '-')));
|
|
275
|
+
}
|
|
276
|
+
results = results.map((item) => {
|
|
277
|
+
const vd = item.value.trim().replaceAll(' ', '-');
|
|
278
|
+
return { ...item, checked: !!DropDown.Tokens[id].oncheckvalues[vd] };
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
const controlItems = options.data.filter((d) => d.value === 'reset' || d.value === 'close');
|
|
282
|
+
const allData = [...results, ...controlItems];
|
|
283
|
+
if (allData.length > controlItems.length) {
|
|
284
|
+
const { render } = await _render(allData);
|
|
285
|
+
htmls(`.${id}-render-container`, render);
|
|
286
|
+
} else {
|
|
287
|
+
htmls(
|
|
288
|
+
`.${id}-render-container`,
|
|
289
|
+
html` <div class="inl" style="padding: 10px; color: red">
|
|
290
|
+
<i class="fas fa-exclamation-circle"></i> ${Translate.Render('no-result-found')}
|
|
291
|
+
</div>`,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
console.error('DropDown serviceProvider error:', e);
|
|
296
|
+
}
|
|
297
|
+
}, 200);
|
|
298
|
+
};
|
|
299
|
+
} else {
|
|
300
|
+
s(`.search-box-${id}`).oninput = dropDownSearchHandle;
|
|
301
|
+
}
|
|
190
302
|
|
|
191
303
|
// Not use onblur generate bug on input toggle
|
|
192
304
|
// s(`.search-box-${id}`).onblur = dropDownSearchHandle;
|
|
@@ -3,8 +3,8 @@ import { cap, getId } from './CommonJs.js';
|
|
|
3
3
|
const Keyboard = {
|
|
4
4
|
ActiveKey: {},
|
|
5
5
|
Event: {},
|
|
6
|
-
Init: async function (
|
|
7
|
-
const
|
|
6
|
+
Init: async function () {
|
|
7
|
+
const callBackTime = 45;
|
|
8
8
|
window.onkeydown = (e = new KeyboardEvent()) => {
|
|
9
9
|
this.ActiveKey[e.key] = true;
|
|
10
10
|
// e.composedPath()
|
|
@@ -10,7 +10,7 @@ import { NotificationManager } from './NotificationManager.js';
|
|
|
10
10
|
import { Translate } from './Translate.js';
|
|
11
11
|
import { Validator } from './Validator.js';
|
|
12
12
|
import { htmls, s } from './VanillaJs.js';
|
|
13
|
-
import {
|
|
13
|
+
import { WebhookProvider } from './Webhook.js';
|
|
14
14
|
|
|
15
15
|
const logger = loggerFactory(import.meta);
|
|
16
16
|
|
|
@@ -31,7 +31,7 @@ const LogIn = {
|
|
|
31
31
|
|
|
32
32
|
for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
|
|
33
33
|
if (!user || user.role === 'guest') return;
|
|
34
|
-
await
|
|
34
|
+
await WebhookProvider.register({ user });
|
|
35
35
|
if (s(`.session`))
|
|
36
36
|
htmls(
|
|
37
37
|
`.session`,
|
|
@@ -3,13 +3,13 @@ import { BtnIcon } from './BtnIcon.js';
|
|
|
3
3
|
import { LogIn } from './LogIn.js';
|
|
4
4
|
import { Translate } from './Translate.js';
|
|
5
5
|
import { htmls, s } from './VanillaJs.js';
|
|
6
|
-
import {
|
|
6
|
+
import { WebhookProvider } from './Webhook.js';
|
|
7
7
|
import { NotificationManager } from './NotificationManager.js';
|
|
8
8
|
|
|
9
9
|
const LogOut = {
|
|
10
10
|
Event: {},
|
|
11
11
|
Trigger: async function (options) {
|
|
12
|
-
await
|
|
12
|
+
await WebhookProvider.unregister();
|
|
13
13
|
for (const eventKey of Object.keys(this.Event)) await this.Event[eventKey](options);
|
|
14
14
|
if (s(`.session`))
|
|
15
15
|
htmls(
|
|
@@ -85,7 +85,7 @@ const PanelForm = {
|
|
|
85
85
|
options = {
|
|
86
86
|
idPanel: '',
|
|
87
87
|
defaultUrlImage: '',
|
|
88
|
-
|
|
88
|
+
appStore: {},
|
|
89
89
|
parentIdModal: undefined,
|
|
90
90
|
route: 'home',
|
|
91
91
|
htmlFormHeader: async () => '',
|
|
@@ -97,7 +97,7 @@ const PanelForm = {
|
|
|
97
97
|
showCreatorProfile: false,
|
|
98
98
|
},
|
|
99
99
|
) {
|
|
100
|
-
const { idPanel, defaultUrlImage,
|
|
100
|
+
const { idPanel, defaultUrlImage, appStore } = options;
|
|
101
101
|
|
|
102
102
|
// Authenticated users don't need 'public' tag - they see all their own posts
|
|
103
103
|
// Only include 'public' for unauthenticated users (handled by backend)
|
|
@@ -458,10 +458,10 @@ const PanelForm = {
|
|
|
458
458
|
baseNewDoc.mdFileId = hasMdContent
|
|
459
459
|
? `<div class="markdown-content">${marked.parse(data.mdFileId)}</div>`
|
|
460
460
|
: null;
|
|
461
|
-
baseNewDoc.userId =
|
|
461
|
+
baseNewDoc.userId = appStore.Data.user?.main?.model?.user?._id;
|
|
462
462
|
|
|
463
463
|
// Ensure profileImageId is properly formatted as object with _id property
|
|
464
|
-
const profileImageIdValue =
|
|
464
|
+
const profileImageIdValue = appStore.Data.user?.main?.model?.user?.profileImageId;
|
|
465
465
|
const formattedProfileImageId = profileImageIdValue
|
|
466
466
|
? typeof profileImageIdValue === 'string'
|
|
467
467
|
? { _id: profileImageIdValue }
|
|
@@ -469,9 +469,9 @@ const PanelForm = {
|
|
|
469
469
|
: null;
|
|
470
470
|
|
|
471
471
|
baseNewDoc.userInfo = {
|
|
472
|
-
username:
|
|
473
|
-
email:
|
|
474
|
-
_id:
|
|
472
|
+
username: appStore.Data.user?.main?.model?.user?.username,
|
|
473
|
+
email: appStore.Data.user?.main?.model?.user?.email,
|
|
474
|
+
_id: appStore.Data.user?.main?.model?.user?._id,
|
|
475
475
|
profileImageId: formattedProfileImageId,
|
|
476
476
|
};
|
|
477
477
|
baseNewDoc.tools = true;
|
|
@@ -737,8 +737,8 @@ const PanelForm = {
|
|
|
737
737
|
tools:
|
|
738
738
|
documentObject.userId &&
|
|
739
739
|
typeof documentObject.userId === 'object' &&
|
|
740
|
-
|
|
741
|
-
documentObject.userId._id ===
|
|
740
|
+
appStore.Data.user?.main?.model?.user?._id &&
|
|
741
|
+
documentObject.userId._id === appStore.Data.user.main.model.user._id,
|
|
742
742
|
_id: documentObject._id,
|
|
743
743
|
totalCopyShareLinkCount: documentObject.totalCopyShareLinkCount || 0,
|
|
744
744
|
isPublic: documentObject.isPublic || false,
|
|
@@ -772,8 +772,8 @@ const PanelForm = {
|
|
|
772
772
|
tools:
|
|
773
773
|
documentObject.userId &&
|
|
774
774
|
typeof documentObject.userId === 'object' &&
|
|
775
|
-
|
|
776
|
-
documentObject.userId._id ===
|
|
775
|
+
appStore.Data.user?.main?.model?.user?._id &&
|
|
776
|
+
documentObject.userId._id === appStore.Data.user.main.model.user._id,
|
|
777
777
|
_id: documentObject._id,
|
|
778
778
|
totalCopyShareLinkCount: documentObject.totalCopyShareLinkCount || 0,
|
|
779
779
|
isPublic: documentObject.isPublic || false,
|
|
@@ -867,10 +867,10 @@ const PanelForm = {
|
|
|
867
867
|
try {
|
|
868
868
|
const cid = getQueryParams().cid ? getQueryParams().cid : '';
|
|
869
869
|
const forceUpdate =
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
lastUserId !==
|
|
870
|
+
appStore.Data.user.main.model &&
|
|
871
|
+
appStore.Data.user.main.model.user &&
|
|
872
|
+
appStore.Data.user.main.model.user._id &&
|
|
873
|
+
lastUserId !== appStore.Data.user.main.model.user._id;
|
|
874
874
|
|
|
875
875
|
logger.warn(
|
|
876
876
|
{
|
|
@@ -878,8 +878,8 @@ const PanelForm = {
|
|
|
878
878
|
cid,
|
|
879
879
|
forceUpdate,
|
|
880
880
|
},
|
|
881
|
-
|
|
882
|
-
? JSON.stringify(
|
|
881
|
+
appStore.Data.user?.main?.model?.user
|
|
882
|
+
? JSON.stringify(appStore.Data.user.main.model.user, null, 4)
|
|
883
883
|
: 'No user data',
|
|
884
884
|
);
|
|
885
885
|
|
|
@@ -889,8 +889,8 @@ const PanelForm = {
|
|
|
889
889
|
|
|
890
890
|
if (loadingGetData || (normalizedLastCid === normalizedCid && !forceUpdate)) return;
|
|
891
891
|
loadingGetData = true;
|
|
892
|
-
lastUserId =
|
|
893
|
-
? newInstance(
|
|
892
|
+
lastUserId = appStore.Data.user?.main?.model?.user?._id
|
|
893
|
+
? newInstance(appStore.Data.user.main.model.user._id)
|
|
894
894
|
: null;
|
|
895
895
|
lastCid = cid;
|
|
896
896
|
|
|
@@ -30,10 +30,9 @@ const RichText = {
|
|
|
30
30
|
return html` <style>
|
|
31
31
|
.md-container {
|
|
32
32
|
background: white;
|
|
33
|
-
color: black;
|
|
34
33
|
}
|
|
35
34
|
.md-container button {
|
|
36
|
-
color: black
|
|
35
|
+
color: black;
|
|
37
36
|
}
|
|
38
37
|
</style>
|
|
39
38
|
<div class="in md-container"><textarea class="${id}"></textarea></div>`;
|
|
@@ -1,25 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side WebSocket provider using Socket.IO.
|
|
3
|
+
* Manages a singleton socket connection, channel registration, and event dispatching.
|
|
4
|
+
*
|
|
5
|
+
* @module client/core/SocketIo
|
|
6
|
+
* @namespace SocketIoProvider
|
|
7
|
+
*/
|
|
1
8
|
import { io } from 'socket.io/client-dist/socket.io.esm.min.js';
|
|
2
9
|
import { loggerFactory } from './Logger.js';
|
|
3
10
|
import { getWsBasePath, getWsBaseUrl } from '../../services/core/core.service.js';
|
|
4
11
|
|
|
5
12
|
const logger = loggerFactory(import.meta);
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @class SocketIoProvider
|
|
16
|
+
* @classdesc Provides static methods for managing a singleton Socket.IO client connection,
|
|
17
|
+
* channel event dispatching, and message emission.
|
|
18
|
+
* @memberof SocketIoProvider
|
|
19
|
+
*/
|
|
20
|
+
class SocketIoProvider {
|
|
21
|
+
/**
|
|
22
|
+
* Event callback registry, keyed by channel name → unique callback ID → handler function.
|
|
23
|
+
* Built-in channels: `connect`, `connect_error`, `disconnect`.
|
|
24
|
+
*
|
|
25
|
+
* @static
|
|
26
|
+
* @type {Object.<string, Object.<string, function>>}
|
|
27
|
+
*/
|
|
28
|
+
static Event = {
|
|
9
29
|
connect: {},
|
|
10
30
|
connect_error: {},
|
|
11
31
|
disconnect: {},
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The active Socket.IO client socket instance.
|
|
36
|
+
*
|
|
37
|
+
* @static
|
|
38
|
+
* @type {import('socket.io-client').Socket|null}
|
|
39
|
+
*/
|
|
40
|
+
static socket = null;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The current WebSocket host URL.
|
|
44
|
+
*
|
|
45
|
+
* @static
|
|
46
|
+
* @type {string|undefined}
|
|
47
|
+
*/
|
|
48
|
+
static host;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Emits a JSON-serialized payload to the server on the specified channel.
|
|
52
|
+
*
|
|
53
|
+
* @static
|
|
54
|
+
* @param {string} [channel=''] - The channel/event name to emit on.
|
|
55
|
+
* @param {Object} [payload={}] - The data to send.
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
static Emit(channel = '', payload = {}) {
|
|
16
59
|
try {
|
|
17
60
|
this.socket.emit(channel, JSON.stringify(payload));
|
|
18
61
|
} catch (error) {
|
|
19
62
|
logger.error(error);
|
|
20
63
|
}
|
|
21
|
-
}
|
|
22
|
-
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Initializes (or re-initializes) the Socket.IO connection.
|
|
68
|
+
* Disconnects any existing socket, creates a new connection, and registers
|
|
69
|
+
* built-in event listeners and custom channels.
|
|
70
|
+
*
|
|
71
|
+
* @static
|
|
72
|
+
* @async
|
|
73
|
+
* @param {Object} options - Connection options.
|
|
74
|
+
* @param {string} [options.host] - Override WebSocket host URL.
|
|
75
|
+
* @param {Object.<string, Object>} [options.channels] - Channel definitions to register listeners for.
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
*/
|
|
78
|
+
static async Init(options) {
|
|
23
79
|
if (this.socket) this.socket.disconnect();
|
|
24
80
|
const path = getWsBasePath();
|
|
25
81
|
this.host = options.host ? options.host : getWsBaseUrl({ wsBasePath: '' });
|
|
@@ -29,24 +85,10 @@ const SocketIo = {
|
|
|
29
85
|
});
|
|
30
86
|
const connectOptions = {
|
|
31
87
|
path: path === '/' ? undefined : path,
|
|
32
|
-
// auth: {
|
|
33
|
-
// token: '',
|
|
34
|
-
// },
|
|
35
|
-
// query: {
|
|
36
|
-
// 'my-key': 'my-value',
|
|
37
|
-
// },
|
|
38
|
-
// forceNew: true,
|
|
39
|
-
// reconnectionAttempts: 'Infinity',
|
|
40
|
-
// timeout: 10000,
|
|
41
|
-
// autoConnect: 5000,
|
|
42
|
-
// Custom auth socket io credentials:
|
|
43
88
|
withCredentials: true,
|
|
44
|
-
extraHeaders: {
|
|
45
|
-
// "my-custom-header": "abcd"
|
|
46
|
-
},
|
|
89
|
+
extraHeaders: {},
|
|
47
90
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
48
91
|
};
|
|
49
|
-
// logger.error(`connect options:`, JSON.stringify(connectOptions, null, 4));
|
|
50
92
|
this.socket = io(this.host, connectOptions);
|
|
51
93
|
|
|
52
94
|
this.socket.on('connect', () => {
|
|
@@ -65,17 +107,28 @@ const SocketIo = {
|
|
|
65
107
|
});
|
|
66
108
|
|
|
67
109
|
if (options && 'channels' in options) this.setChannels(options.channels);
|
|
68
|
-
}
|
|
69
|
-
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Registers socket listeners for each channel key, routing incoming messages
|
|
114
|
+
* to all registered callbacks in `Event[channel]`.
|
|
115
|
+
*
|
|
116
|
+
* @static
|
|
117
|
+
* @param {Object.<string, Object>} channels - Channel definitions keyed by channel name.
|
|
118
|
+
* @returns {void}
|
|
119
|
+
*/
|
|
120
|
+
static setChannels(channels) {
|
|
70
121
|
Object.keys(channels).map((type) => {
|
|
71
122
|
logger.info(`load chanel`, type);
|
|
72
123
|
this.Event[type] = {};
|
|
73
124
|
this.socket.on(type, (...args) => {
|
|
74
|
-
// logger.info(`event: ${type} | ${JSON.stringify(args, null, 4)}`);
|
|
75
125
|
Object.keys(this.Event[type]).map((keyEvent) => this.Event[type][keyEvent](args));
|
|
76
126
|
});
|
|
77
127
|
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** @type {SocketIoProvider} Backward compatibility alias. */
|
|
132
|
+
const SocketIo = SocketIoProvider;
|
|
80
133
|
|
|
81
|
-
export { SocketIo };
|
|
134
|
+
export { SocketIoProvider, SocketIo };
|