@ozdao/martyrs 0.2.468 → 0.2.470
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/dist/{Media-DwVcRIAX.mjs → Media-C4Ges_Sd.mjs} +1 -1
- package/dist/{Media-DCGbUujy.js → Media-CR0V1zvB.js} +1 -1
- package/dist/chats.server.js +14 -13
- package/dist/chats.server.mjs +14 -13
- package/dist/globals.server.js +20 -12
- package/dist/globals.server.mjs +20 -12
- package/dist/{main-DEHjtgLs.mjs → main-CTcal9qN.mjs} +900 -884
- package/dist/{main-UVdexuMN.js → main-CsZAG5Wz.js} +5 -5
- package/dist/martyrs/src/components/Block/Block.vue.cjs +1 -1
- package/dist/martyrs/src/components/Block/Block.vue.js +1 -1
- package/dist/martyrs/src/components/SelectMulti/{SelectMulti.vue2.cjs → SelectMulti.vue.cjs} +2 -2
- package/dist/martyrs/src/components/SelectMulti/{SelectMulti.vue2.js.map → SelectMulti.vue.cjs.map} +1 -1
- package/dist/martyrs/src/components/SelectMulti/{SelectMulti.vue2.js → SelectMulti.vue.js} +2 -2
- package/dist/martyrs/src/components/SelectMulti/SelectMulti.vue.js.map +1 -0
- package/dist/martyrs/src/components/Tooltip/{Tooltip.vue.cjs → Tooltip.vue2.cjs} +2 -2
- package/dist/martyrs/src/components/Tooltip/Tooltip.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/Tooltip/{Tooltip.vue.js → Tooltip.vue2.js} +2 -2
- package/dist/martyrs/src/components/Tooltip/{Tooltip.vue.cjs.map → Tooltip.vue2.js.map} +1 -1
- package/dist/martyrs/src/modules/backoffice/components/partials/Sidebar.vue.cjs +1 -1
- package/dist/martyrs/src/modules/backoffice/components/partials/Sidebar.vue.js +1 -1
- package/dist/martyrs/src/modules/chats/store/chat.store.cjs +47 -16
- package/dist/martyrs/src/modules/chats/store/chat.store.cjs.map +1 -1
- package/dist/martyrs/src/modules/chats/store/chat.store.js +47 -16
- package/dist/martyrs/src/modules/chats/store/chat.store.js.map +1 -1
- package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.cjs +1 -1
- package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.js +1 -1
- package/dist/martyrs/src/modules/community/components/blocks/CardBlogpost.vue.js.map +1 -1
- package/dist/martyrs/src/modules/community/components/pages/Blog.vue.cjs +1 -1
- package/dist/martyrs/src/modules/community/components/pages/Blog.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/community/components/pages/Blog.vue.js +1 -1
- package/dist/martyrs/src/modules/community/components/pages/Blog.vue.js.map +1 -1
- package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.cjs +4 -1
- package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.js +4 -1
- package/dist/martyrs/src/modules/community/components/pages/BlogPost.vue.js.map +1 -1
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs +3 -3
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js +3 -3
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js.map +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/VideoPlayer.vue.cjs +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/VideoPlayer.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/VideoPlayer.vue.js +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/VideoPlayer.vue.js.map +1 -1
- package/dist/martyrs/src/modules/constructor/components/sections/Constructor.vue.cjs +9 -9
- package/dist/martyrs/src/modules/constructor/components/sections/Constructor.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/constructor/components/sections/Constructor.vue.js +9 -9
- package/dist/martyrs/src/modules/constructor/components/sections/Constructor.vue.js.map +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.cjs +2 -2
- package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.js +2 -2
- package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.js.map +1 -1
- package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.cjs +1 -1
- package/dist/martyrs/src/modules/gallery/components/sections/BackofficeGallery.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/globals.client.cjs +0 -9
- package/dist/martyrs/src/modules/globals/globals.client.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/globals.client.js +0 -9
- package/dist/martyrs/src/modules/globals/globals.client.js.map +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.websocket.cjs +40 -97
- package/dist/martyrs/src/modules/globals/views/classes/globals.websocket.cjs.map +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.websocket.js +40 -97
- package/dist/martyrs/src/modules/globals/views/classes/globals.websocket.js.map +1 -1
- package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.js +1 -1
- package/dist/martyrs/src/modules/icons/skeletons/SkeletonBlogpost.vue.cjs +1 -1
- package/dist/martyrs/src/modules/icons/skeletons/SkeletonBlogpost.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/icons/skeletons/SkeletonBlogpost.vue.js +1 -1
- package/dist/martyrs/src/modules/icons/skeletons/SkeletonBlogpost.vue.js.map +1 -1
- package/dist/martyrs/src/modules/notifications/notifications.client.cjs +35 -9
- package/dist/martyrs/src/modules/notifications/notifications.client.cjs.map +1 -1
- package/dist/martyrs/src/modules/notifications/notifications.client.js +35 -9
- package/dist/martyrs/src/modules/notifications/notifications.client.js.map +1 -1
- package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.cjs +7 -7
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.js +8 -8
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreate.vue.js.map +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/blocks/CardPosition.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/blocks/CardPosition.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/EditLeftover.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/EditLeftover.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.cjs +6 -5
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.js +6 -5
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.js.map +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/RentsEdit.vue.js +1 -1
- package/dist/martyrs.cjs.js +1 -1
- package/dist/martyrs.css +1 -1
- package/dist/martyrs.es.js +1 -1
- package/dist/notifications.server.js +69 -13
- package/dist/notifications.server.mjs +69 -13
- package/dist/orders.server.js +1 -0
- package/dist/orders.server.mjs +1 -0
- package/package.json +1 -1
- package/src/components/Shader/Shader.vue +44 -25
- package/src/modules/chats/routes/chats.routes.js +21 -17
- package/src/modules/chats/store/chat.store.js +55 -28
- package/src/modules/community/components/blocks/CardBlogpost.vue +1 -1
- package/src/modules/community/components/pages/Blog.vue +1 -1
- package/src/modules/community/components/pages/BlogPost.vue +1 -0
- package/src/modules/community/components/pages/CreateBlogPost.vue +3 -3
- package/src/modules/constructor/components/elements/VideoPlayer.vue +1 -1
- package/src/modules/constructor/components/sections/Constructor.vue +9 -9
- package/src/modules/events/components/pages/EditEvent.vue +1 -1
- package/src/modules/globals/controllers/classes/globals.websocket.js +29 -21
- package/src/modules/globals/globals.client.js +0 -12
- package/src/modules/globals/views/classes/globals.websocket.js +46 -146
- package/src/modules/icons/skeletons/SkeletonBlogpost.vue +2 -2
- package/src/modules/notifications/controllers/notifications.controller.js +2 -2
- package/src/modules/notifications/notifications.client.js +57 -19
- package/src/modules/notifications/notifications.server.js +3 -1
- package/src/modules/notifications/routes/notifications.routes.js +2 -2
- package/src/modules/notifications/services/notification.service.js +34 -15
- package/src/modules/notifications/services/web-push.service.js +7 -6
- package/src/modules/orders/components/pages/OrderCreate.vue +3 -4
- package/src/modules/orders/controllers/orders.controller.js +2 -0
- package/src/modules/products/components/pages/Product.vue +7 -6
- package/dist/martyrs/src/components/SelectMulti/SelectMulti.vue2.cjs.map +0 -1
- package/dist/martyrs/src/components/Tooltip/Tooltip.vue.js.map +0 -1
- package/src/modules/notifications/notifications2.client.js +0 -256
- package/src/modules/notifications/services/websocket.service.js +0 -100
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global WebSocket handler for centralized WebSocket connections
|
|
3
|
-
* @martyrs/src/modules/globals/views/classes/globals.websocket.js
|
|
4
|
-
*/
|
|
5
1
|
class GlobalWebSocket {
|
|
6
2
|
constructor() {
|
|
7
3
|
this.socket = null;
|
|
@@ -11,16 +7,13 @@ class GlobalWebSocket {
|
|
|
11
7
|
this.reconnectDelay = 3000;
|
|
12
8
|
this.baseUrl = null;
|
|
13
9
|
this.pingInterval = null;
|
|
14
|
-
this.pingIntervalTime = 30000;
|
|
10
|
+
this.pingIntervalTime = 30000;
|
|
15
11
|
this.listeners = {};
|
|
16
12
|
this.userId = null;
|
|
17
13
|
this.connectPromise = null;
|
|
14
|
+
this.subscribedModules = new Set(); // <--- ключевое изменение
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
/**
|
|
21
|
-
* Initialize WebSocket with configuration options
|
|
22
|
-
* @param {Object} options - Configuration options
|
|
23
|
-
*/
|
|
24
17
|
initialize(options = {}) {
|
|
25
18
|
this.maxReconnectAttempts = options.maxReconnectAttempts || this.maxReconnectAttempts;
|
|
26
19
|
this.reconnectDelay = options.reconnectDelay || this.reconnectDelay;
|
|
@@ -28,67 +21,39 @@ class GlobalWebSocket {
|
|
|
28
21
|
this.pingIntervalTime = options.pingInterval || this.pingIntervalTime;
|
|
29
22
|
}
|
|
30
23
|
|
|
31
|
-
/**
|
|
32
|
-
* Get default WebSocket URL based on current HTTP URL
|
|
33
|
-
* @returns {String} WebSocket URL
|
|
34
|
-
*/
|
|
35
24
|
_getDefaultWsUrl() {
|
|
36
|
-
|
|
37
|
-
if (typeof window === 'undefined') {
|
|
38
|
-
return '/api/ws'; // Default fallback for SSR
|
|
39
|
-
}
|
|
40
|
-
|
|
25
|
+
if (typeof window === 'undefined') return '/api/ws';
|
|
41
26
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
42
27
|
const host = window.location.host;
|
|
43
28
|
return `${protocol}//${host}/api/ws`;
|
|
44
29
|
}
|
|
45
30
|
|
|
46
|
-
/**
|
|
47
|
-
* Connect to WebSocket server
|
|
48
|
-
* @param {String} userId - User ID for authentication
|
|
49
|
-
* @returns {Promise} Promise that resolves when connection is established
|
|
50
|
-
*/
|
|
51
31
|
connect(userId) {
|
|
52
|
-
|
|
53
|
-
if (typeof window === 'undefined') {
|
|
54
|
-
return Promise.resolve(false);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Always update userId before any connection logic
|
|
32
|
+
if (typeof window === 'undefined') return Promise.resolve(false);
|
|
58
33
|
this.userId = userId;
|
|
59
34
|
|
|
60
|
-
|
|
61
|
-
if (this.isConnected && this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
35
|
+
if (this.isConnected && this.socket?.readyState === WebSocket.OPEN) {
|
|
62
36
|
return Promise.resolve(this.socket);
|
|
63
37
|
}
|
|
64
38
|
|
|
65
|
-
// Clear any existing connection promise to avoid conflicts
|
|
66
|
-
this.connectPromise = null;
|
|
67
|
-
|
|
68
|
-
// Create new connection promise
|
|
69
39
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
70
|
-
// Close existing connection if any
|
|
71
40
|
this.disconnect();
|
|
72
41
|
|
|
73
|
-
// Create new WebSocket connection with user ID
|
|
74
42
|
const wsUrl = userId ? `${this.baseUrl}?userId=${encodeURIComponent(userId)}` : this.baseUrl;
|
|
75
43
|
this.socket = new WebSocket(wsUrl);
|
|
76
44
|
|
|
77
|
-
// Setup event handlers
|
|
78
45
|
this.socket.onopen = () => {
|
|
79
46
|
this._handleOpen();
|
|
80
47
|
resolve(this.socket);
|
|
81
48
|
};
|
|
82
|
-
|
|
49
|
+
|
|
83
50
|
this.socket.onmessage = this._handleMessage.bind(this);
|
|
84
|
-
this.socket.onerror = (
|
|
85
|
-
this._handleError(
|
|
86
|
-
reject(
|
|
51
|
+
this.socket.onerror = (err) => {
|
|
52
|
+
this._handleError(err);
|
|
53
|
+
reject(err);
|
|
87
54
|
};
|
|
88
|
-
|
|
89
55
|
this.socket.onclose = this._handleClose.bind(this);
|
|
90
56
|
|
|
91
|
-
// Timeout for connection
|
|
92
57
|
setTimeout(() => {
|
|
93
58
|
if (!this.isConnected) {
|
|
94
59
|
reject(new Error('WebSocket connection timeout'));
|
|
@@ -101,32 +66,20 @@ class GlobalWebSocket {
|
|
|
101
66
|
return this.connectPromise;
|
|
102
67
|
}
|
|
103
68
|
|
|
104
|
-
/**
|
|
105
|
-
* Disconnect from WebSocket server
|
|
106
|
-
*/
|
|
107
69
|
disconnect() {
|
|
108
|
-
|
|
109
|
-
if (typeof window === 'undefined') {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
70
|
+
if (typeof window === 'undefined') return;
|
|
112
71
|
|
|
113
72
|
if (this.socket) {
|
|
114
|
-
// Remove event listeners
|
|
115
73
|
this.socket.onopen = null;
|
|
116
74
|
this.socket.onmessage = null;
|
|
117
75
|
this.socket.onerror = null;
|
|
118
76
|
this.socket.onclose = null;
|
|
119
|
-
|
|
120
|
-
// Close connection
|
|
121
|
-
if (this.socket.readyState === WebSocket.OPEN ||
|
|
122
|
-
this.socket.readyState === WebSocket.CONNECTING) {
|
|
77
|
+
if (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING) {
|
|
123
78
|
this.socket.close();
|
|
124
79
|
}
|
|
125
|
-
|
|
126
80
|
this.socket = null;
|
|
127
81
|
}
|
|
128
82
|
|
|
129
|
-
// Clear ping interval
|
|
130
83
|
if (this.pingInterval) {
|
|
131
84
|
clearInterval(this.pingInterval);
|
|
132
85
|
this.pingInterval = null;
|
|
@@ -136,11 +89,6 @@ class GlobalWebSocket {
|
|
|
136
89
|
this.userId = null;
|
|
137
90
|
}
|
|
138
91
|
|
|
139
|
-
/**
|
|
140
|
-
* Send data through WebSocket
|
|
141
|
-
* @param {Object|String} data - Data to send
|
|
142
|
-
* @returns {Boolean} Success status
|
|
143
|
-
*/
|
|
144
92
|
async send(data) {
|
|
145
93
|
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
146
94
|
console.error('Cannot send message: WebSocket is not connected');
|
|
@@ -148,51 +96,29 @@ class GlobalWebSocket {
|
|
|
148
96
|
}
|
|
149
97
|
|
|
150
98
|
try {
|
|
151
|
-
const
|
|
152
|
-
|
|
99
|
+
const msg = typeof data === 'string' ? data : JSON.stringify(data);
|
|
100
|
+
this.socket.send(msg);
|
|
153
101
|
return true;
|
|
154
|
-
} catch (
|
|
155
|
-
console.error('Error sending
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error('Error sending message:', err);
|
|
156
104
|
return false;
|
|
157
105
|
}
|
|
158
106
|
}
|
|
159
107
|
|
|
160
|
-
/**
|
|
161
|
-
* Register an event listener
|
|
162
|
-
* @param {String} eventType - Event type to listen for
|
|
163
|
-
* @param {Function} callback - Callback function
|
|
164
|
-
* @param {Object} options - Additional options like module name for namespacing
|
|
165
|
-
* @returns {String} Listener ID for unregistering
|
|
166
|
-
*/
|
|
167
108
|
addEventListener(eventType, callback, options = {}) {
|
|
168
109
|
const listenerId = `${options.module || 'global'}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
169
|
-
|
|
170
|
-
if (!this.listeners[eventType]) {
|
|
171
|
-
this.listeners[eventType] = {};
|
|
172
|
-
}
|
|
173
|
-
|
|
110
|
+
if (!this.listeners[eventType]) this.listeners[eventType] = {};
|
|
174
111
|
this.listeners[eventType][listenerId] = callback;
|
|
175
112
|
return listenerId;
|
|
176
113
|
}
|
|
177
114
|
|
|
178
|
-
/**
|
|
179
|
-
* Remove an event listener
|
|
180
|
-
* @param {String} eventType - Event type
|
|
181
|
-
* @param {String} listenerId - Listener ID returned from addEventListener
|
|
182
|
-
*/
|
|
183
115
|
removeEventListener(eventType, listenerId) {
|
|
184
|
-
if (this.listeners[eventType]
|
|
116
|
+
if (this.listeners[eventType]?.[listenerId]) {
|
|
185
117
|
delete this.listeners[eventType][listenerId];
|
|
186
118
|
}
|
|
187
119
|
}
|
|
188
120
|
|
|
189
|
-
/**
|
|
190
|
-
* Remove all listeners for a module
|
|
191
|
-
* @param {String} moduleName - Module name to remove listeners for
|
|
192
|
-
*/
|
|
193
121
|
removeModuleListeners(moduleName) {
|
|
194
|
-
if (!moduleName) return;
|
|
195
|
-
|
|
196
122
|
Object.keys(this.listeners).forEach(eventType => {
|
|
197
123
|
Object.keys(this.listeners[eventType]).forEach(listenerId => {
|
|
198
124
|
if (listenerId.startsWith(`${moduleName}_`)) {
|
|
@@ -202,111 +128,85 @@ class GlobalWebSocket {
|
|
|
202
128
|
});
|
|
203
129
|
}
|
|
204
130
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
131
|
+
async subscribeModule(moduleName) {
|
|
132
|
+
if (!moduleName || this.subscribedModules.has(moduleName)) return;
|
|
133
|
+
const success = await this.send({ type: 'subscribe', module: moduleName });
|
|
134
|
+
if (success) {
|
|
135
|
+
this.subscribedModules.add(moduleName);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
_resubscribeAllModules() {
|
|
140
|
+
for (const moduleName of this.subscribedModules) {
|
|
141
|
+
this.send({ type: 'subscribe', module: moduleName });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
208
145
|
_handleOpen() {
|
|
209
146
|
this.isConnected = true;
|
|
210
147
|
this.reconnectAttempts = 0;
|
|
211
|
-
|
|
212
|
-
// Setup ping interval to keep connection alive
|
|
148
|
+
|
|
213
149
|
this.pingInterval = setInterval(() => {
|
|
214
|
-
if (this.socket
|
|
150
|
+
if (this.socket?.readyState === WebSocket.OPEN) {
|
|
215
151
|
this.socket.send(JSON.stringify({ type: 'ping' }));
|
|
216
152
|
}
|
|
217
153
|
}, this.pingIntervalTime);
|
|
218
154
|
|
|
219
|
-
//
|
|
155
|
+
this._resubscribeAllModules(); // <--- ВАЖНО
|
|
220
156
|
this._notifyListeners('open', { isConnected: true });
|
|
221
157
|
}
|
|
222
158
|
|
|
223
|
-
/**
|
|
224
|
-
* Handle WebSocket message event
|
|
225
|
-
* @param {Event} event - WebSocket message event
|
|
226
|
-
*/
|
|
227
159
|
_handleMessage(event) {
|
|
228
160
|
try {
|
|
229
161
|
const data = JSON.parse(event.data);
|
|
230
|
-
|
|
231
|
-
// Generic event notifications
|
|
232
162
|
this._notifyListeners('message', data);
|
|
233
|
-
|
|
234
|
-
// Specific event type notifications
|
|
235
163
|
if (data.type) {
|
|
236
164
|
this._notifyListeners(data.type, data);
|
|
237
165
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
console.error('Error processing WebSocket message:', error);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
console.error('WebSocket message error:', err);
|
|
241
168
|
}
|
|
242
169
|
}
|
|
243
170
|
|
|
244
|
-
/**
|
|
245
|
-
* Handle WebSocket error
|
|
246
|
-
* @param {Error} error - WebSocket error
|
|
247
|
-
*/
|
|
248
171
|
_handleError(error) {
|
|
249
172
|
console.error('WebSocket error:', error);
|
|
250
173
|
this._notifyListeners('error', { error });
|
|
251
174
|
}
|
|
252
175
|
|
|
253
|
-
/**
|
|
254
|
-
* Handle WebSocket close event
|
|
255
|
-
* @param {Event} event - WebSocket close event
|
|
256
|
-
*/
|
|
257
176
|
_handleClose(event) {
|
|
258
177
|
this.isConnected = false;
|
|
259
|
-
|
|
260
|
-
// Clear ping interval
|
|
261
178
|
if (this.pingInterval) {
|
|
262
179
|
clearInterval(this.pingInterval);
|
|
263
180
|
this.pingInterval = null;
|
|
264
181
|
}
|
|
265
|
-
|
|
182
|
+
|
|
266
183
|
this._notifyListeners('close', { code: event.code, reason: event.reason });
|
|
267
|
-
|
|
268
|
-
// Attempt to reconnect if not a normal closure and we have a userId
|
|
184
|
+
|
|
269
185
|
if (event.code !== 1000 && this.userId && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
270
186
|
this.reconnectAttempts++;
|
|
271
187
|
const delay = this.reconnectDelay * this.reconnectAttempts;
|
|
272
|
-
|
|
273
188
|
setTimeout(() => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
});
|
|
278
|
-
}
|
|
189
|
+
this.connect(this.userId).catch(err => {
|
|
190
|
+
console.error('Reconnection failed:', err);
|
|
191
|
+
});
|
|
279
192
|
}, delay);
|
|
280
193
|
}
|
|
281
194
|
}
|
|
282
195
|
|
|
283
|
-
/**
|
|
284
|
-
* Notify event listeners
|
|
285
|
-
* @param {String} eventType - Event type
|
|
286
|
-
* @param {Object} data - Event data
|
|
287
|
-
*/
|
|
288
196
|
_notifyListeners(eventType, data) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
Object.values(this.listeners[eventType]).forEach(callback => {
|
|
197
|
+
Object.values(this.listeners[eventType] || {}).forEach(fn => {
|
|
292
198
|
try {
|
|
293
|
-
|
|
294
|
-
} catch (
|
|
295
|
-
console.error(`
|
|
199
|
+
fn(data);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.error(`Listener for ${eventType} failed:`, err);
|
|
296
202
|
}
|
|
297
203
|
});
|
|
298
204
|
}
|
|
299
205
|
|
|
300
|
-
/**
|
|
301
|
-
* Get connection status
|
|
302
|
-
* @returns {Boolean} Connection status
|
|
303
|
-
*/
|
|
304
206
|
isSocketConnected() {
|
|
305
207
|
return this.isConnected && this.socket?.readyState === WebSocket.OPEN;
|
|
306
208
|
}
|
|
307
209
|
}
|
|
308
210
|
|
|
309
|
-
// Create singleton instance
|
|
310
211
|
const globalWebSocket = new GlobalWebSocket();
|
|
311
|
-
|
|
312
|
-
export default globalWebSocket;
|
|
212
|
+
export default globalWebSocket;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<svg width="100%" height="20rem" xmlns="http://www.w3.org/2000/svg">
|
|
3
3
|
<!-- Фоновый прямоугольник для всей карточки -->
|
|
4
|
-
<rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" fill="rgb(var(--
|
|
4
|
+
<rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" fill="rgb(var(--light))"/>
|
|
5
5
|
|
|
6
6
|
<!-- Имитация шапки карточки -->
|
|
7
7
|
<rect x="15" y="15" rx="4" ry="4" width="40%" height="20" fill="rgba(var(--black),0.05)"></rect>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<rect x="15" y="130" rx="3" ry="3" width="70%" height="6" fill="rgba(var(--black),0.05)"></rect>
|
|
15
15
|
|
|
16
16
|
<!-- Блик с анимацией движения -->
|
|
17
|
-
<rect x="-25%" y="0" width="25%" height="100%" fill="rgb(var(--
|
|
17
|
+
<rect x="-25%" y="0" width="25%" height="100%" fill="rgb(var(--light))" mask="url(#myMask)" opacity="0.3">
|
|
18
18
|
<animate attributeName="x" from="-25%" to="100%" dur="2s" repeatCount="indefinite"></animate>
|
|
19
19
|
</rect>
|
|
20
20
|
</svg>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// controllers/notifications.controller.js
|
|
2
|
-
const NotificationsController = (db) => {
|
|
2
|
+
const NotificationsController = (db, wss) => {
|
|
3
3
|
// Create a new notification
|
|
4
4
|
const create = async (req, res) => {
|
|
5
5
|
try {
|
|
6
6
|
const notification = await db.notification.create(req.body);
|
|
7
7
|
|
|
8
8
|
// Trigger notification sending process
|
|
9
|
-
const notificationService = require('../services/notification.service')(db);
|
|
9
|
+
const notificationService = require('../services/notification.service')(db, wss);
|
|
10
10
|
notificationService.processNotification(notification);
|
|
11
11
|
|
|
12
12
|
return res.status(201).json(notification);
|
|
@@ -178,6 +178,7 @@ class CapacitorPushHandler {
|
|
|
178
178
|
/**
|
|
179
179
|
* Notification Manager for coordinating WebSocket and Push notifications
|
|
180
180
|
*/
|
|
181
|
+
|
|
181
182
|
class NotificationManager {
|
|
182
183
|
constructor(store, options = {}) {
|
|
183
184
|
this.store = store;
|
|
@@ -187,45 +188,82 @@ class NotificationManager {
|
|
|
187
188
|
this.isServer = typeof window === 'undefined';
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
async registerWebPush(store) {
|
|
192
|
+
if (!('Notification' in window) || !('serviceWorker' in navigator) || !('PushManager' in window)) {
|
|
193
|
+
console.warn('Web Push не поддерживается в браузере');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const permission = await Notification.requestPermission();
|
|
198
|
+
if (permission !== 'granted') {
|
|
199
|
+
console.warn('Разрешение на уведомления не получено');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const registration = await navigator.serviceWorker.register('/sw.js');
|
|
204
|
+
const subscription = await registration.pushManager.subscribe({
|
|
205
|
+
userVisibleOnly: true,
|
|
206
|
+
applicationServerKey: 'BJtNnRrx05VQS0abnkHC-8gHJWpnmoqC_iQveENCmZOZIs-adWzqAiqFCdGVVd7CbiaLW-Q5iuIBDRgM9G-VnKg'
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
console.log('New subscription:', JSON.stringify(subscription));
|
|
210
|
+
|
|
211
|
+
// Отправь подписку на сервер
|
|
212
|
+
await store.notifications.actions.registerDevice({
|
|
213
|
+
deviceToken: JSON.stringify(subscription),
|
|
214
|
+
deviceType: 'web'
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
193
218
|
async initialize() {
|
|
194
219
|
if (this.initialized || this.isServer) return;
|
|
195
|
-
|
|
196
|
-
// Get current user ID from auth store instead of localStorage
|
|
220
|
+
|
|
197
221
|
const userId = this.store.auth.state.user?._id;
|
|
198
222
|
if (!userId) {
|
|
199
223
|
console.warn('Cannot initialize notifications: No user ID found in auth store');
|
|
200
224
|
return;
|
|
201
225
|
}
|
|
202
|
-
|
|
203
|
-
|
|
226
|
+
|
|
227
|
+
console.log('Connecting to websockets via notifications')
|
|
204
228
|
await globalWebSocket.connect(userId);
|
|
205
|
-
|
|
206
|
-
|
|
229
|
+
|
|
230
|
+
globalWebSocket.removeModuleListeners('notification');
|
|
231
|
+
|
|
232
|
+
await globalWebSocket.subscribeModule('notification');
|
|
233
|
+
|
|
234
|
+
globalWebSocket.addEventListener(
|
|
235
|
+
'notification',
|
|
236
|
+
(data) => {
|
|
237
|
+
this.store.notifications.actions.addLocalNotification(data.data);
|
|
238
|
+
},
|
|
239
|
+
{ module: 'notification' }
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
// 🎯 Опционально включаем push
|
|
207
245
|
if (this.options.enablePush !== false) {
|
|
208
246
|
await this.pushHandler.requestPermissions();
|
|
247
|
+
await this.registerWebPush(this.store)
|
|
209
248
|
}
|
|
210
|
-
|
|
249
|
+
|
|
211
250
|
this.initialized = true;
|
|
212
|
-
|
|
213
|
-
//
|
|
251
|
+
|
|
252
|
+
// ✅ Загружаем список уведомлений из API
|
|
214
253
|
await this.store.notifications.actions.getNotifications(userId);
|
|
215
254
|
}
|
|
216
255
|
|
|
217
|
-
/**
|
|
218
|
-
* Disconnect and clean up
|
|
219
|
-
*/
|
|
220
256
|
disconnect() {
|
|
221
257
|
if (this.isServer) return;
|
|
222
|
-
|
|
223
|
-
globalWebSocket.removeModuleListeners('
|
|
258
|
+
|
|
259
|
+
globalWebSocket.removeModuleListeners('notification');
|
|
224
260
|
this.pushHandler.removeListeners();
|
|
225
261
|
this.initialized = false;
|
|
226
262
|
}
|
|
227
263
|
}
|
|
228
264
|
|
|
265
|
+
|
|
266
|
+
|
|
229
267
|
/**
|
|
230
268
|
* Server-side utility for pre-fetching notification data
|
|
231
269
|
*/
|
|
@@ -283,7 +321,7 @@ function initializeNotifications(app, store, router, options = {}) {
|
|
|
283
321
|
|
|
284
322
|
if (autoInit) {
|
|
285
323
|
// Initialize after auth is confirmed
|
|
286
|
-
const isAuthenticated = store.auth.state.
|
|
324
|
+
const isAuthenticated = store.auth.state.access.status;
|
|
287
325
|
const userId = store.auth.state.user?._id;
|
|
288
326
|
|
|
289
327
|
if (isAuthenticated && userId) {
|
|
@@ -291,7 +329,7 @@ function initializeNotifications(app, store, router, options = {}) {
|
|
|
291
329
|
}
|
|
292
330
|
|
|
293
331
|
// Watch for user login/logout using auth store
|
|
294
|
-
watch(() => store.auth.state.
|
|
332
|
+
watch(() => store.auth.state.access.status, (isAuthenticated) => {
|
|
295
333
|
if (isAuthenticated) {
|
|
296
334
|
notificationManager.initialize();
|
|
297
335
|
} else {
|
|
@@ -20,6 +20,7 @@ const NotificationService = require('./services/notification.service.js');
|
|
|
20
20
|
const { getInstance } = require('@martyrs/src/modules/globals/controllers/classes/globals.abac.js');
|
|
21
21
|
|
|
22
22
|
function initializeNotifications(app, db, wss, origins, publicPath) {
|
|
23
|
+
console.log('wss init is', wss)
|
|
23
24
|
// Set up models in the database object
|
|
24
25
|
db.notification = NotificationModel(db);
|
|
25
26
|
db.userDevice = UserDeviceModel(db);
|
|
@@ -31,11 +32,12 @@ function initializeNotifications(app, db, wss, origins, publicPath) {
|
|
|
31
32
|
|
|
32
33
|
// Set up routes if app is provided
|
|
33
34
|
if (app) {
|
|
34
|
-
notificationsRoutes(app, db, origins, publicPath);
|
|
35
|
+
notificationsRoutes(app, db, wss, origins, publicPath);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
// Initialize notification service and related background tasks
|
|
38
39
|
const notificationService = NotificationService(db, wss);
|
|
40
|
+
console.log('[DEBUG] WSS in notification init:', wss);
|
|
39
41
|
|
|
40
42
|
// Set up a scheduler to process pending notifications
|
|
41
43
|
setInterval(() => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// routes/notifications.routes.js
|
|
2
|
-
const notificationsRoutes = (app, db, origins, publicPath) => {
|
|
3
|
-
const controller = require('../controllers/notifications.controller')(db);
|
|
2
|
+
const notificationsRoutes = (app, db, wss, origins, publicPath) => {
|
|
3
|
+
const controller = require('../controllers/notifications.controller')(db, wss);
|
|
4
4
|
|
|
5
5
|
// Get notifications for a user
|
|
6
6
|
app.get('/api/notifications/user/:userId', controller.getUserNotifications);
|
|
@@ -1,18 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const ObjectId = require('mongoose').Types.ObjectId;
|
|
2
|
+
|
|
3
|
+
module.exports = function(db, wss) {
|
|
4
|
+
wss.registerModule('notification', async (ws, msg) => {
|
|
5
|
+
console.log('[WebSocket][notification] msg:', msg);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
|
|
3
9
|
// Process and route notification to appropriate channels
|
|
4
10
|
const processNotification = async (notification) => {
|
|
5
11
|
try {
|
|
6
|
-
const userId = notification.userId;
|
|
12
|
+
const userId = notification.userId.toString();
|
|
7
13
|
|
|
8
14
|
// Get user preferences
|
|
9
15
|
const preferences = await db.notificationPreference.find({
|
|
10
|
-
userId,
|
|
11
|
-
notificationType: notification.type
|
|
16
|
+
userId: userId,
|
|
17
|
+
// notificationType: notification.type
|
|
12
18
|
});
|
|
13
19
|
|
|
20
|
+
console.log('userId', userId)
|
|
21
|
+
|
|
22
|
+
console.log('preferences', preferences)
|
|
23
|
+
|
|
24
|
+
console.log('notificationType', notification.type)
|
|
25
|
+
|
|
26
|
+
console.log('notification', notification)
|
|
27
|
+
|
|
14
28
|
// Get user data
|
|
15
|
-
const user = await db.mongoose.model('User').findById(userId);
|
|
29
|
+
const user = await db.mongoose.model('User').findById(new ObjectId(userId));
|
|
30
|
+
|
|
16
31
|
if (!user) {
|
|
17
32
|
throw new Error(`User not found: ${userId}`);
|
|
18
33
|
}
|
|
@@ -53,6 +68,9 @@ const NotificationService = (db, wss) => {
|
|
|
53
68
|
.map(pref => pref.channelType);
|
|
54
69
|
}
|
|
55
70
|
|
|
71
|
+
|
|
72
|
+
console.log('channels', channels)
|
|
73
|
+
|
|
56
74
|
// Send to each enabled channel
|
|
57
75
|
for (const channel of channels) {
|
|
58
76
|
const sendFunc = channelRouters[channel];
|
|
@@ -107,20 +125,23 @@ const NotificationService = (db, wss) => {
|
|
|
107
125
|
}
|
|
108
126
|
};
|
|
109
127
|
|
|
110
|
-
|
|
111
|
-
// const webSocketService = require('./websocket.service')(wss);
|
|
112
|
-
|
|
128
|
+
// Send websocket notifications
|
|
113
129
|
const sendWebNotification = async (notification, user) => {
|
|
114
|
-
|
|
130
|
+
console.log(`[WebSocket][notification] sending to user ${user._id}`);
|
|
131
|
+
const result = await wss.sendToUserInModule('notification', user._id, {
|
|
115
132
|
type: 'notification',
|
|
116
133
|
data: notification
|
|
117
134
|
});
|
|
135
|
+
if (!result) {
|
|
136
|
+
console.log(`Failed to send web notification to user ${user._id}`);
|
|
137
|
+
}
|
|
118
138
|
};
|
|
119
139
|
|
|
120
140
|
// Send push notification to mobile device
|
|
121
141
|
const sendPushNotification = async (notification, user, device) => {
|
|
122
|
-
|
|
142
|
+
|
|
123
143
|
const pushService = getPushServiceByDeviceType(device.deviceType);
|
|
144
|
+
|
|
124
145
|
return pushService.send({
|
|
125
146
|
token: device.deviceToken,
|
|
126
147
|
title: notification.title,
|
|
@@ -136,7 +157,7 @@ const NotificationService = (db, wss) => {
|
|
|
136
157
|
} else if (deviceType === 'android') {
|
|
137
158
|
return require('./fcm.service');
|
|
138
159
|
} else {
|
|
139
|
-
|
|
160
|
+
return require('./web-push.service');
|
|
140
161
|
}
|
|
141
162
|
};
|
|
142
163
|
|
|
@@ -198,6 +219,4 @@ const NotificationService = (db, wss) => {
|
|
|
198
219
|
processNotification,
|
|
199
220
|
processPendingNotifications
|
|
200
221
|
};
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
module.exports = NotificationService;
|
|
222
|
+
};
|
|
@@ -13,21 +13,22 @@ const WebPushService = {
|
|
|
13
13
|
try {
|
|
14
14
|
// Parse the subscription object from the token
|
|
15
15
|
const subscription = JSON.parse(token);
|
|
16
|
-
|
|
16
|
+
console.log('title is', title)
|
|
17
|
+
console.log('body is', body)
|
|
18
|
+
console.log('data is', data)
|
|
17
19
|
// Prepare the notification payload
|
|
18
20
|
const payload = JSON.stringify({
|
|
19
|
-
notification: {
|
|
20
21
|
title: title,
|
|
21
22
|
body: body,
|
|
22
23
|
icon: process.env.NOTIFICATION_ICON_URL || '/favicon.ico',
|
|
23
|
-
badge: process.env.NOTIFICATION_BADGE_URL,
|
|
24
|
+
badge: process.env.NOTIFICATION_BADGE_URL || '/favicon.ico',
|
|
24
25
|
data: data
|
|
25
|
-
}
|
|
26
26
|
});
|
|
27
|
-
|
|
27
|
+
console.log('payload is', payload)
|
|
28
|
+
|
|
28
29
|
// Send the notification
|
|
29
30
|
const result = await webpush.sendNotification(subscription, payload);
|
|
30
|
-
|
|
31
|
+
console.log('web push is', result)
|
|
31
32
|
return { success: true, statusCode: result.statusCode };
|
|
32
33
|
} catch (error) {
|
|
33
34
|
console.error('Web Push service error:', error);
|