From 6b8392102eb9f510f56c9a8381a23b52ed311fd8 Mon Sep 17 00:00:00 2001 From: anti Date: Thu, 9 Apr 2026 19:23:24 -0400 Subject: [PATCH] fix: emit stats/histogram snapshot on SSE connect; remove polling api.get('/stats') from Dashboard --- decnet/web/router/stream/api_stream_events.py | 9 +++++++ decnet_web/src/components/Dashboard.tsx | 27 ++----------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/decnet/web/router/stream/api_stream_events.py b/decnet/web/router/stream/api_stream_events.py index ad51125..e76a0de 100644 --- a/decnet/web/router/stream/api_stream_events.py +++ b/decnet/web/router/stream/api_stream_events.py @@ -32,6 +32,15 @@ async def stream_events( if last_id == 0: last_id = await repo.get_max_log_id() + # Emit initial snapshot immediately so the client never needs to poll /stats + stats = await repo.get_stats_summary() + yield f"event: message\ndata: {json.dumps({'type': 'stats', 'data': stats})}\n\n" + histogram = await repo.get_log_histogram( + search=search, start_time=start_time, + end_time=end_time, interval_minutes=15, + ) + yield f"event: message\ndata: {json.dumps({'type': 'histogram', 'data': histogram})}\n\n" + while True: if await request.is_disconnected(): break diff --git a/decnet_web/src/components/Dashboard.tsx b/decnet_web/src/components/Dashboard.tsx index 4ac9b57..c32717d 100644 --- a/decnet_web/src/components/Dashboard.tsx +++ b/decnet_web/src/components/Dashboard.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState } from 'react'; -import api from '../utils/api'; import './Dashboard.css'; import { Shield, Users, Activity, Clock } from 'lucide-react'; @@ -31,26 +30,7 @@ const Dashboard: React.FC = ({ searchQuery }) => { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); - const fetchData = async () => { - try { - const [statsRes, logsRes] = await Promise.all([ - api.get('/stats'), - api.get('/logs', { params: { limit: 50, search: searchQuery } }) - ]); - setStats(statsRes.data); - setLogs(logsRes.data.data); - } catch (err) { - console.error('Failed to fetch dashboard data', err); - } finally { - setLoading(false); - } - }; - useEffect(() => { - // Initial fetch to populate UI immediately - fetchData(); - - // Setup SSE connection const token = localStorage.getItem('token'); const baseUrl = import.meta.env.VITE_API_URL || 'http://localhost:8000/api/v1'; let url = `${baseUrl}/stream?token=${token}`; @@ -64,13 +44,10 @@ const Dashboard: React.FC = ({ searchQuery }) => { try { const payload = JSON.parse(event.data); if (payload.type === 'logs') { - setLogs(prev => { - const newLogs = payload.data; - // Prepend new logs, keep up to 100 in UI to prevent infinite DOM growth - return [...newLogs, ...prev].slice(0, 100); - }); + setLogs(prev => [...payload.data, ...prev].slice(0, 100)); } else if (payload.type === 'stats') { setStats(payload.data); + setLoading(false); } } catch (err) { console.error('Failed to parse SSE payload', err);