From 42535528eb3a897512e41c6fda55e7a1c6b337a9 Mon Sep 17 00:00:00 2001
From: kilyabin <65072190+kilyabin@users.noreply.github.com>
Date: Thu, 28 May 2026 14:43:03 +0400
Subject: [PATCH] feat: redesign Overview with active threats, severity donut,
top processes bar
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add /api/active-anomalies endpoint — latest snapshot per PID where is_anomaly=1
- Active threats card with pulsing indicator, CPU/RAM/TCP per process
- Alerts by severity donut chart (high/medium/low)
- Top processes horizontal bar chart weighted by alert count
---
backend/main.py | 18 ++
frontend/src/components/Overview.tsx | 259 +++++++++++++++++++++++++--
frontend/src/lib/api.ts | 3 +-
3 files changed, 264 insertions(+), 16 deletions(-)
diff --git a/backend/main.py b/backend/main.py
index 21faf35..fbdc2ff 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -227,6 +227,24 @@ async def get_system_metrics(limit: int = 60) -> list[dict]:
return list(reversed([dict(r) for r in rows]))
+@app.get("/api/active-anomalies", summary="Currently anomalous processes")
+async def get_active_anomalies() -> list[dict]:
+ """Latest snapshot per PID — only rows where is_anomaly=1."""
+ async with aiosqlite.connect(DB_PATH) as db:
+ db.row_factory = aiosqlite.Row
+ async with db.execute("""
+ SELECT m.*
+ FROM metrics m
+ INNER JOIN (
+ SELECT pid, MAX(id) AS max_id FROM metrics GROUP BY pid
+ ) latest ON m.id = latest.max_id
+ WHERE m.is_anomaly = 1
+ ORDER BY m.cpu_percent DESC
+ LIMIT 20
+ """) as cur:
+ return [dict(r) for r in await cur.fetchall()]
+
+
@app.get("/api/file-events", summary="Recent filesystem events")
async def get_file_events(limit: int = 100) -> list[dict]:
"""Return the most recent *limit* filesystem events."""
diff --git a/frontend/src/components/Overview.tsx b/frontend/src/components/Overview.tsx
index d5f975a..803caa5 100644
--- a/frontend/src/components/Overview.tsx
+++ b/frontend/src/components/Overview.tsx
@@ -1,9 +1,36 @@
import { useQuery } from '@tanstack/react-query'
-import { Activity, AlertTriangle, Bell, Cpu, Database } from 'lucide-react'
-import { api } from '@/lib/api'
+import { Activity, AlertTriangle, Bell, Cpu, Database, ShieldAlert } from 'lucide-react'
+import {
+ PieChart,
+ Pie,
+ Cell,
+ Tooltip as ChartTooltip,
+ BarChart,
+ Bar,
+ XAxis,
+ YAxis,
+ ResponsiveContainer,
+} from 'recharts'
+import { api, type Alert, type ProcessMetric } from '@/lib/api'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
import { SystemChart } from '@/components/SystemChart'
+// ── shared tooltip style ──────────────────────────────────────────────────────
+
+const TOOLTIP_STYLE = {
+ contentStyle: {
+ background: '#111118',
+ border: '1px solid #00ff9d33',
+ borderRadius: 6,
+ fontSize: 12,
+ fontFamily: 'JetBrains Mono, monospace',
+ },
+ labelStyle: { color: '#6b7280' },
+}
+
+// ── stat card ─────────────────────────────────────────────────────────────────
+
function StatCard({
title,
value,
@@ -36,6 +63,186 @@ function StatCard({
)
}
+// ── active anomalies list ─────────────────────────────────────────────────────
+
+function ActiveAnomaliesList({ procs }: { procs: ProcessMetric[] }) {
+ return (
+
+ {procs.map((p) => (
+
+ )}
+