diff --git a/bouncer.js b/bouncer.js index 66450cc..5d6026e 100644 --- a/bouncer.js +++ b/bouncer.js @@ -20,8 +20,16 @@ const csess = new Map(); // this is used for relays. const userRelays = new Map(); // per ID contains Set() of const idleSess = new Set(); +let stats = { + _global: { + rx: 0, + tx: 0, + f: 0 + } +}; + // CL - User socket -module.exports = (ws, req, onClose) => { +function handleConnection(ws, req, onClose) { let query = querystring.parse(req.url.slice(2)); let authKey = null; let authorized = true; @@ -257,6 +265,7 @@ function getIdleSess(ws) { // WS - Sessions function newConn(addr, id, reconn_t = 0) { if (!csess.has(id)) return; + if (!stats[addr]) stats[addr] = { rx: 0, tx: 0, f: 0 }; const relay = new WebSocket(addr, { headers: { "User-Agent": `Bostr ${version}; The nostr relay bouncer; https://github.com/Yonle/bostr`, @@ -315,6 +324,9 @@ function newConn(addr, id, reconn_t = 0) { client.events.get(data[1]).add(data[2]?.id); client.send(JSON.stringify(data)); + stats[addr].rx++; + stats[addr].rx++; + // Now count for REQ limit requested by client. // If it's at the limit, Send EOSE to client and delete pendingEOSE of subID @@ -362,6 +374,9 @@ function newConn(addr, id, reconn_t = 0) { if (log_about_relays) console.log(process.pid, id, addr, data[0], data[1]); + stats._global.f++ + stats[addr].f++ + break; case "CLOSED": @@ -371,8 +386,22 @@ function newConn(addr, id, reconn_t = 0) { if (log_about_relays) console.log(process.pid, id, addr, data[0], data[1], data[2]); - if ((data[0] === "CLOSED") && client.pendingEOSE.has(data[1])) + if ((data[0]) === "OK") { + switch (data[1]) { + case true: + stats._global.tx++; + stats[addr].tx++; + case false: + stats._global.f++ + stats[addr].f++ + } + } + + if ((data[0] === "CLOSED") && client.pendingEOSE.has(data[1])) { + stats._global.f++ + stats[addr].f++ client.pendingEOSE.set(data[1], client.pendingEOSE.get(data[1]) + 1); + } break; } @@ -390,6 +419,9 @@ function newConn(addr, id, reconn_t = 0) { setTimeout(_ => { newConn(addr, id, reconn_t); }, reconn_t); + + stats._global.f++ + stats[addr].f++ }); relay.on('unexpected-response', (req, res) => { @@ -406,3 +438,13 @@ function newConn(addr, id, reconn_t = 0) { for (let i = 1; i <= (idle_sessions || 1); i++) { newsess(); } + +function getStat(n) { + if (!n) return stats; + return stats[n]; +} + +module.exports = { + handleConnection, + getStat +} diff --git a/http.js b/http.js index b460a58..7a78f8d 100644 --- a/http.js +++ b/http.js @@ -49,6 +49,7 @@ const lastConn = new Map(); const favicon = fs.existsSync(config.favicon) ? fs.readFileSync(config.favicon) : null; server.on('request', (req, res) => { + const globalStat = bouncer.getStat("_global"); const serverAddr = `${req.headers["x-forwarded-proto"]?.replace(/http/i, "ws") || (server.isStandaloneHTTPS ? "wss" : "ws")}://${req.headers.host}${req.url}`; log(`${req.headers["x-forwarded-for"]?.split(",")[0] || req.socket.address()?.address} - ${req.method} ${req.url} [${req.headers["user-agent"] || ""}]`) @@ -64,10 +65,22 @@ server.on('request', (req, res) => { }); res.write("Hello. This nostr bouncer (bostr) is bouncing the following relays:\n\n"); config.relays.forEach(_ => { - res.write("- " + _ + "\n"); + const { rx, tx, f } = bouncer.getStat(_); + res.write("- " + _ + ` (rx: ${rx}; tx: ${tx}; fail: ${f})` + "\n"); }); res.write(`\nI have ${wss.clients.size} clients currently connected to this bouncer${(process.env.CLUSTERS || config.clusters) > 1 ? " on this cluster" : ""}.\n`); + + res.write(`\nBouncer statistics:`); + res.write(`\n- rx: ${globalStat.rx}`); + res.write(`\n- tx: ${globalStat.tx}`); + res.write(`\n- fail: ${globalStat.f}`); + + res.write(`\n\nStatistics legends:`); + res.write(`\n- rx: received events`); + res.write(`\n- tx: succesfully transmitted events`); + res.write(`\n- fail: failed transmissions or upstream errors\n`); + if (config?.authorized_keys?.length) res.write("\nNOTE: This relay has configured for personal use only. Only authorized users could use this bostr relay.\n"); res.write(`\nConnect to this bouncer with nostr client: ${serverAddr}`); res.write(`\n\n- To make connection that only send whitelisted kind of events, Connect:`); @@ -112,7 +125,7 @@ server.on('upgrade', (req, sock, head) => { lastConn.set(ip, Date.now()); - wss.handleUpgrade(req, sock, head, _ => bouncer(_, req, _ => lastConn.set(ip, Date.now()))); + wss.handleUpgrade(req, sock, head, _ => bouncer.handleConnection(_, req, _ => lastConn.set(ip, Date.now()))); }); const listened = server.listen(process.env.PORT || config.port, config.address || "0.0.0.0", _ => { diff --git a/package.json b/package.json index e8bd79e..78e54ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bostr", - "version": "2.1.4", + "version": "2.1.5-dev", "description": "Nostr relay bouncer", "main": "index.js", "scripts": {