You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
67 lines
1.9 KiB
67 lines
1.9 KiB
/**
|
|
* Realtime distributor for the dating server. Holds an in-memory map
|
|
* of `userId -> Set<WebSocket>` so handlers can broadcast events to
|
|
* specific users without each handler caring about transport details.
|
|
*
|
|
* Single-process only: this hub does NOT persist subscriptions or
|
|
* coordinate across instances. Sufficient for the local demo; a
|
|
* production deployment would replace this with Redis pub/sub or
|
|
* similar.
|
|
*/
|
|
|
|
const sockets = new Map(); // userId -> Set<WebSocket>
|
|
|
|
export function attach(userId, socket) {
|
|
if (!userId || !socket) return () => {};
|
|
let set = sockets.get(userId);
|
|
if (!set) {
|
|
set = new Set();
|
|
sockets.set(userId, set);
|
|
}
|
|
set.add(socket);
|
|
|
|
const detach = () => {
|
|
const current = sockets.get(userId);
|
|
if (!current) return;
|
|
current.delete(socket);
|
|
if (current.size === 0) sockets.delete(userId);
|
|
};
|
|
socket.on('close', detach);
|
|
socket.on('error', detach);
|
|
return detach;
|
|
}
|
|
|
|
/**
|
|
* Send `event` to every socket subscribed by any of `userIds`. Each
|
|
* `event` is a plain object that gets JSON-encoded once for all
|
|
* recipients.
|
|
*
|
|
* Failures on individual sockets are swallowed: the close/error
|
|
* handlers attached in `attach` clean them up later, and a single bad
|
|
* socket must never block the rest of the broadcast.
|
|
*/
|
|
export function broadcast(userIds, event) {
|
|
if (!Array.isArray(userIds) || userIds.length === 0) return;
|
|
const payload = JSON.stringify(event);
|
|
const seen = new Set();
|
|
for (const userId of userIds) {
|
|
if (!userId || seen.has(userId)) continue;
|
|
seen.add(userId);
|
|
const set = sockets.get(userId);
|
|
if (!set || set.size === 0) continue;
|
|
for (const socket of set) {
|
|
try {
|
|
if (socket.readyState === socket.OPEN) socket.send(payload);
|
|
} catch {
|
|
// best-effort
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function snapshot() {
|
|
const result = {};
|
|
for (const [userId, set] of sockets) result[userId] = set.size;
|
|
return result;
|
|
}
|