diff --git a/neode-ui/mock-backend.js b/neode-ui/mock-backend.js index 2ffd8de6..c33f17e5 100755 --- a/neode-ui/mock-backend.js +++ b/neode-ui/mock-backend.js @@ -2778,6 +2778,153 @@ app.post('/rpc/v1', (req, res) => { }) } + // ── Monitoring ────────────────────────────────────────────── + case 'monitoring.current': { + return res.json({ + result: { + system: { + cpu_percent: +(12 + Math.random() * 18).toFixed(1), + load_avg_1: +(0.5 + Math.random() * 1.5).toFixed(2), + load_avg_5: +(0.8 + Math.random()).toFixed(2), + load_avg_15: +(0.6 + Math.random()).toFixed(2), + mem_used_bytes: 6_200_000_000 + Math.floor(Math.random() * 500_000_000), + mem_total_bytes: 16_000_000_000, + disk_used_bytes: 620_000_000_000, + disk_total_bytes: 1_800_000_000_000, + net_rx_bytes: 12_400_000_000 + Math.floor(Math.random() * 100_000_000), + net_tx_bytes: 8_900_000_000 + Math.floor(Math.random() * 50_000_000), + uptime_secs: Math.floor(process.uptime()) + 604800, + }, + rpc: { avg_latency_ms: +(2 + Math.random() * 8).toFixed(1), requests_per_minute: Math.floor(30 + Math.random() * 40) }, + }, + }) + } + + case 'monitoring.history': { + const points = 60 + const history = [] + for (let i = 0; i < points; i++) { + history.push({ + timestamp: new Date(Date.now() - (points - i) * 60000).toISOString(), + system: { + cpu_percent: +(10 + Math.random() * 25).toFixed(1), + mem_used_bytes: 5_800_000_000 + Math.floor(Math.random() * 1_000_000_000), + mem_total_bytes: 16_000_000_000, + net_rx_bytes: Math.floor(Math.random() * 5_000_000), + net_tx_bytes: Math.floor(Math.random() * 3_000_000), + }, + rpc: { avg_latency_ms: +(1 + Math.random() * 10).toFixed(1) }, + }) + } + return res.json({ result: { history } }) + } + + case 'monitoring.alerts': { + return res.json({ + result: { + alerts: [ + { id: 'a1', kind: 'cpu_high', message: 'CPU usage exceeded 80% for 5 minutes', timestamp: new Date(Date.now() - 7200000).toISOString(), acknowledged: false }, + { id: 'a2', kind: 'disk_high', message: 'Disk usage at 85%', timestamp: new Date(Date.now() - 86400000).toISOString(), acknowledged: true }, + ], + }, + }) + } + + case 'monitoring.alert-rules': { + return res.json({ + result: { + rules: [ + { kind: 'cpu_high', enabled: true, threshold: 80, description: 'Alert when CPU usage exceeds threshold' }, + { kind: 'mem_high', enabled: true, threshold: 85, description: 'Alert when memory usage exceeds threshold' }, + { kind: 'disk_high', enabled: true, threshold: 90, description: 'Alert when disk usage exceeds threshold' }, + { kind: 'backend_error_spike', enabled: false, threshold: 500, description: 'Alert when RPC latency exceeds threshold' }, + ], + }, + }) + } + + case 'monitoring.configure-alert': + case 'monitoring.acknowledge-alert': { + return res.json({ result: { success: true } }) + } + + case 'monitoring.export': { + return res.json({ result: { data: 'timestamp,cpu,mem\n2026-04-10T12:00:00Z,15.2,38.5\n' } }) + } + + case 'monitoring.container-metrics': { + return res.json({ + result: { + containers: [ + { name: 'bitcoin-knots', cpu_percent: 8.2, mem_used_bytes: 1_200_000_000, net_rx_bytes: 5_000_000, net_tx_bytes: 3_200_000 }, + { name: 'lnd', cpu_percent: 3.1, mem_used_bytes: 480_000_000, net_rx_bytes: 2_100_000, net_tx_bytes: 1_800_000 }, + { name: 'electrs', cpu_percent: 12.4, mem_used_bytes: 890_000_000, net_rx_bytes: 800_000, net_tx_bytes: 600_000 }, + { name: 'mempool', cpu_percent: 5.6, mem_used_bytes: 320_000_000, net_rx_bytes: 1_500_000, net_tx_bytes: 900_000 }, + { name: 'filebrowser', cpu_percent: 0.8, mem_used_bytes: 45_000_000, net_rx_bytes: 200_000, net_tx_bytes: 150_000 }, + ], + }, + }) + } + + // ── Fleet / Telemetry ───────────────────────────────────── + case 'telemetry.fleet-status': { + return res.json({ + result: { + nodes: [ + { + id: 'node-1', name: 'archy-main', did: 'did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2ReMkBe4bR6XBIDNq9', + status: 'online', last_seen: new Date().toISOString(), + version: '1.3.0', uptime_secs: 604800, + system: { cpu_percent: 15.2, mem_used_bytes: 6_200_000_000, mem_total_bytes: 16_000_000_000, disk_used_bytes: 620_000_000_000, disk_total_bytes: 1_800_000_000_000 }, + apps: ['bitcoin-knots', 'lnd', 'electrs', 'mempool', 'filebrowser'], + tor_connected: true, + }, + { + id: 'node-2', name: 'archy-198', did: 'did:key:z6Mkp2z3PJbJHbQ95fGk3CqYVNEPE3VNnFGA7yUYkQjXoZTL', + status: 'online', last_seen: new Date(Date.now() - 120000).toISOString(), + version: '1.2.1', uptime_secs: 259200, + system: { cpu_percent: 8.7, mem_used_bytes: 3_100_000_000, mem_total_bytes: 8_000_000_000, disk_used_bytes: 180_000_000_000, disk_total_bytes: 500_000_000_000 }, + apps: ['bitcoin-knots', 'lnd', 'mempool'], + tor_connected: true, + }, + { + id: 'node-3', name: 'archy-vps', did: 'did:key:z6MknGc3ocHs3zdPiJbnaaqDi5ER7eLYwBqPR4NkDhCUD7Li', + status: 'offline', last_seen: new Date(Date.now() - 3600000).toISOString(), + version: '1.3.0', uptime_secs: 0, + system: { cpu_percent: 0, mem_used_bytes: 0, mem_total_bytes: 4_000_000_000, disk_used_bytes: 45_000_000_000, disk_total_bytes: 80_000_000_000 }, + apps: ['lnd'], + tor_connected: false, + }, + ], + }, + }) + } + + case 'telemetry.fleet-alerts': { + return res.json({ + result: { + alerts: [ + { id: 'fa1', node_id: 'node-3', node_name: 'archy-vps', kind: 'node_offline', message: 'Node went offline', timestamp: new Date(Date.now() - 3600000).toISOString(), acknowledged: false }, + { id: 'fa2', node_id: 'node-2', node_name: 'archy-198', kind: 'disk_high', message: 'Disk usage at 36%', timestamp: new Date(Date.now() - 86400000).toISOString(), acknowledged: true }, + ], + }, + }) + } + + case 'telemetry.fleet-node-history': { + const nodeId = params?.node_id || 'node-1' + const points = 60 + const history = [] + for (let i = 0; i < points; i++) { + history.push({ + timestamp: new Date(Date.now() - (points - i) * 60000).toISOString(), + cpu_percent: +(8 + Math.random() * 20).toFixed(1), + mem_used_bytes: 5_000_000_000 + Math.floor(Math.random() * 2_000_000_000), + }) + } + return res.json({ result: { node_id: nodeId, history } }) + } + default: { console.log(`[RPC] Unknown method: ${method}`) return res.json({ diff --git a/neode-ui/src/components/Screensaver.vue b/neode-ui/src/components/Screensaver.vue index ff463d3c..06193c9f 100644 --- a/neode-ui/src/components/Screensaver.vue +++ b/neode-ui/src/components/Screensaver.vue @@ -7,8 +7,12 @@ @click="store.deactivate()" @keydown.escape="store.deactivate()" > - -
System resources & health
+