Lumi/src/services/web-events.js
2026-06-16 08:30:40 +02:00

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