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

/**
* 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;
}

Powered by TurnKey Linux.