@nka212bg/backend-utils 0.1.40 → 0.1.41
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/package.json +1 -1
- package/socket.js +48 -14
package/package.json
CHANGED
package/socket.js
CHANGED
|
@@ -1,13 +1,51 @@
|
|
|
1
1
|
const WebSocket = require("ws");
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const parseParams = (url) => {
|
|
4
|
+
try {
|
|
5
|
+
return Object.fromEntries(new URLSearchParams(decodeURIComponent(url || "").replace(/^(\/\?|\?|\/)/g, "")));
|
|
6
|
+
} catch (error) {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// options.authenticate (params, req) => boolean, rejects the connection with 401 during the handshake when falsy
|
|
12
|
+
module.exports = (server, { authenticate } = {}) => {
|
|
13
|
+
const socket = new WebSocket.Server({
|
|
14
|
+
server,
|
|
15
|
+
noServer: !server,
|
|
16
|
+
verifyClient: authenticate && (({ req }) => !!authenticate(parseParams(req.url), req)),
|
|
17
|
+
});
|
|
5
18
|
socket.on("connection", (ws, req) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
19
|
+
ws.params = parseParams(req.url);
|
|
20
|
+
const userId = ws.params.userId;
|
|
21
|
+
|
|
22
|
+
// first tab of this user -> online
|
|
23
|
+
if (userId && !isConnected(userId, ws)) notifyWatchers(userId, 1);
|
|
24
|
+
|
|
25
|
+
// { watchStatuses: [userIds] } -> replies { statuses: { userId: 0 | 1 } }, then pushes { status: { userId, online } } on changes
|
|
26
|
+
ws.on("message", (raw) => {
|
|
27
|
+
try {
|
|
28
|
+
const { watchStatuses } = JSON.parse(raw);
|
|
29
|
+
if (!watchStatuses) return;
|
|
30
|
+
ws.watching = new Set(watchStatuses.map(String));
|
|
31
|
+
ws.send(JSON.stringify({ statuses: socket.getStatuses(watchStatuses) }));
|
|
32
|
+
} catch (error) {}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// last tab closed -> offline
|
|
36
|
+
ws.on("close", () => userId && !isConnected(userId, ws) && notifyWatchers(userId, 0));
|
|
9
37
|
});
|
|
10
38
|
|
|
39
|
+
const isConnected = (userId, except) => {
|
|
40
|
+
for (const c of socket.clients) if (c !== except && c.params?.userId === userId) return true;
|
|
41
|
+
return false;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const notifyWatchers = (userId, online) => {
|
|
45
|
+
const data = JSON.stringify({ statuses: { [userId]: online } });
|
|
46
|
+
for (const c of socket.clients) if (c.watching?.has(userId)) c.send(data);
|
|
47
|
+
};
|
|
48
|
+
|
|
11
49
|
socket.broadcast = (data, { skipId, skipToken } = {}) => {
|
|
12
50
|
if (!data) return 0;
|
|
13
51
|
if (typeof data === "object") data = JSON.stringify(data);
|
|
@@ -50,23 +88,19 @@ module.exports = (server) => {
|
|
|
50
88
|
|
|
51
89
|
socket.getStatuses = (users) => {
|
|
52
90
|
users = new Set(String(users).split(/[,"' ]/g).filter(Boolean));
|
|
53
|
-
|
|
54
91
|
const usersObj = {};
|
|
55
|
-
for (const u of users) usersObj[u] = 0;
|
|
56
92
|
|
|
57
|
-
|
|
58
|
-
for (const client of socket.clients || []) {
|
|
59
|
-
if (!remaining) break;
|
|
60
|
-
if (!users.has(client.params.userId)) continue;
|
|
93
|
+
for (const u of users) usersObj[u] = 0;
|
|
61
94
|
|
|
62
|
-
|
|
63
|
-
|
|
95
|
+
for (const client of socket.clients || []) {
|
|
96
|
+
if (!users.size) break;
|
|
97
|
+
const id = client.params?.userId;
|
|
98
|
+
if (users.delete(id)) usersObj[id] = 1; // delete() is true only the first time -> extra tabs are ignored
|
|
64
99
|
}
|
|
65
100
|
|
|
66
101
|
return usersObj;
|
|
67
102
|
};
|
|
68
103
|
|
|
69
|
-
|
|
70
104
|
return socket;
|
|
71
105
|
};
|
|
72
106
|
|