mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-08-28 11:44:39 +00:00
fix: network status computed server-side across ALL nodes, not just top 50
This commit is contained in:
+6
-21
@@ -1140,9 +1140,10 @@
|
||||
async function renderNodesTab(el) {
|
||||
el.innerHTML = '<div style="padding:40px;text-align:center;color:var(--text-muted)">Loading node analytics…</div>';
|
||||
try {
|
||||
const [nodesResp, bulkHealth] = await Promise.all([
|
||||
const [nodesResp, bulkHealth, netStatus] = await Promise.all([
|
||||
api('/nodes?limit=200&sortBy=lastSeen'),
|
||||
api('/nodes/bulk-health?limit=50')
|
||||
api('/nodes/bulk-health?limit=50'),
|
||||
api('/nodes/network-status')
|
||||
]);
|
||||
const nodes = nodesResp.nodes || nodesResp;
|
||||
const myNodes = JSON.parse(localStorage.getItem('meshcore-my-nodes') || '[]');
|
||||
@@ -1159,24 +1160,8 @@
|
||||
const byObservers = [...enriched].sort((a, b) => (b.health.observers?.length || 0) - (a.health.observers?.length || 0));
|
||||
const byRecent = [...enriched].filter(n => n.health.stats.lastHeard).sort((a, b) => new Date(b.health.stats.lastHeard) - new Date(a.health.stats.lastHeard));
|
||||
|
||||
// Status counts
|
||||
const now = Date.now();
|
||||
let active = 0, degraded = 0, silent = 0;
|
||||
enriched.forEach(n => {
|
||||
const lh = n.health.stats.lastHeard;
|
||||
const age = lh ? now - new Date(lh).getTime() : Infinity;
|
||||
const role = (n.role || '').toLowerCase();
|
||||
const isInfra = role === 'repeater' || role === 'room';
|
||||
const degradedMs = isInfra ? 86400000 : 3600000;
|
||||
const silentMs = isInfra ? 259200000 : 86400000;
|
||||
if (age < degradedMs) active++;
|
||||
else if (age < silentMs) degraded++;
|
||||
else silent++;
|
||||
});
|
||||
|
||||
// Role breakdown
|
||||
const roleCounts = {};
|
||||
nodes.forEach(n => { const r = n.role || 'unknown'; roleCounts[r] = (roleCounts[r] || 0) + 1; });
|
||||
// Use server-computed status across ALL nodes
|
||||
const { active, degraded, silent, total: totalNodes, roleCounts } = netStatus;
|
||||
|
||||
function nodeLink(n) {
|
||||
return `<a href="#/nodes/${encodeURIComponent(n.public_key)}/analytics" class="analytics-link">${esc(n.name || n.public_key.slice(0, 12))}</a>`;
|
||||
@@ -1204,7 +1189,7 @@
|
||||
<div style="font-size:11px;text-transform:uppercase;color:var(--text-muted)">🔴 Silent</div>
|
||||
</div>
|
||||
<div class="analytics-stat-card" style="flex:1;min-width:120px;text-align:center;padding:16px;background:var(--card-bg);border:1px solid var(--border);border-radius:8px">
|
||||
<div style="font-size:28px;font-weight:700">${nodes.length}</div>
|
||||
<div style="font-size:28px;font-weight:700">${totalNodes}</div>
|
||||
<div style="font-size:11px;text-transform:uppercase;color:var(--text-muted)">Total Nodes</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -83,9 +83,9 @@
|
||||
<script src="channels.js?v=1774079160" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="nodes.js?v=1773959793" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="traces.js?v=1774079160" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="analytics.js?v=1773960384" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="analytics.js?v=1773961035" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="live.js?v=1774079160" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="observers.js?v=1774079160" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="node-analytics.js?v=1773960384" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="node-analytics.js?v=1773961035" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -619,6 +619,26 @@ app.get('/api/nodes/bulk-health', (req, res) => {
|
||||
res.json(results);
|
||||
});
|
||||
|
||||
app.get('/api/nodes/network-status', (req, res) => {
|
||||
const now = Date.now();
|
||||
const allNodes = db.db.prepare('SELECT public_key, name, role, last_seen FROM nodes').all();
|
||||
let active = 0, degraded = 0, silent = 0;
|
||||
const roleCounts = {};
|
||||
allNodes.forEach(n => {
|
||||
const r = n.role || 'unknown';
|
||||
roleCounts[r] = (roleCounts[r] || 0) + 1;
|
||||
const ls = n.last_seen ? new Date(n.last_seen).getTime() : 0;
|
||||
const age = now - ls;
|
||||
const isInfra = r === 'repeater' || r === 'room';
|
||||
const degradedMs = isInfra ? 86400000 : 3600000;
|
||||
const silentMs = isInfra ? 259200000 : 86400000;
|
||||
if (age < degradedMs) active++;
|
||||
else if (age < silentMs) degraded++;
|
||||
else silent++;
|
||||
});
|
||||
res.json({ total: allNodes.length, active, degraded, silent, roleCounts });
|
||||
});
|
||||
|
||||
app.get('/api/nodes/:pubkey', (req, res) => {
|
||||
const node = db.getNode(req.params.pubkey);
|
||||
if (!node) return res.status(404).json({ error: 'Not found' });
|
||||
|
||||
Reference in New Issue
Block a user