64 lines
1.7 KiB
JavaScript
64 lines
1.7 KiB
JavaScript
const clients = new Map();
|
|
|
|
function subscribe(req, res) {
|
|
const id = `${Date.now()}:${Math.random().toString(16).slice(2)}`;
|
|
const user = req.session?.user || null;
|
|
const client = { id, res, user, connectedAt: Date.now() };
|
|
clients.set(id, client);
|
|
|
|
res.writeHead(200, {
|
|
"Content-Type": "text/event-stream",
|
|
"Cache-Control": "no-store, no-transform",
|
|
Connection: "keep-alive",
|
|
"X-Accel-Buffering": "no"
|
|
});
|
|
send(client, "server:status", {
|
|
status: "connected",
|
|
message: "Lumi event stream connected.",
|
|
connected_at: client.connectedAt
|
|
});
|
|
|
|
const keepAlive = setInterval(() => {
|
|
send(client, "server:status", { status: "heartbeat", at: Date.now() });
|
|
}, 25000);
|
|
|
|
req.on("close", () => {
|
|
clearInterval(keepAlive);
|
|
clients.delete(id);
|
|
});
|
|
}
|
|
|
|
function publish(event, payload = {}, options = {}) {
|
|
let delivered = 0;
|
|
for (const client of clients.values()) {
|
|
if (!canReceive(client.user, options)) continue;
|
|
send(client, event, payload);
|
|
delivered += 1;
|
|
}
|
|
return delivered;
|
|
}
|
|
|
|
function send(client, event, payload) {
|
|
try {
|
|
client.res.write(`event: ${event}\n`);
|
|
client.res.write(`data: ${JSON.stringify({ ...payload, event, at: Date.now() })}\n\n`);
|
|
} catch {
|
|
clients.delete(client.id);
|
|
}
|
|
}
|
|
|
|
function canReceive(user, options = {}) {
|
|
const role = options.role || "public";
|
|
if (role === "public") return true;
|
|
if (!user) return false;
|
|
if (role === "user") return true;
|
|
if (role === "mod") return Boolean(user.isMod || user.isAdmin);
|
|
if (role === "admin") return Boolean(user.isAdmin);
|
|
return false;
|
|
}
|
|
|
|
module.exports = {
|
|
publishWebEvent: publish,
|
|
subscribeWebEvents: subscribe
|
|
};
|