From be3607dd4d77ce79c24dedb0f90c385984323353 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Fri, 24 Oct 2025 18:56:39 +0200 Subject: [PATCH] Update AppImage --- AppImage/components/network-traffic-chart.tsx | 227 ++++++++++++++++++ AppImage/components/system-overview.tsx | 26 +- 2 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 AppImage/components/network-traffic-chart.tsx diff --git a/AppImage/components/network-traffic-chart.tsx b/AppImage/components/network-traffic-chart.tsx new file mode 100644 index 0000000..48afdc9 --- /dev/null +++ b/AppImage/components/network-traffic-chart.tsx @@ -0,0 +1,227 @@ +"use client" + +import { useState, useEffect } from "react" +import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from "recharts" +import { Loader2 } from "lucide-react" + +interface NetworkMetricsData { + time: string + timestamp: number + netIn: number + netOut: number +} + +interface NetworkTrafficChartProps { + timeframe: string +} + +const CustomNetworkTooltip = ({ active, payload, label }: any) => { + if (active && payload && payload.length) { + return ( +
+

{label}

+
+ {payload.map((entry: any, index: number) => ( +
+
+ {entry.name}: + {entry.value} GB +
+ ))} +
+
+ ) + } + return null +} + +export function NetworkTrafficChart({ timeframe }: NetworkTrafficChartProps) { + const [data, setData] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const [visibleLines, setVisibleLines] = useState({ + netIn: true, + netOut: true, + }) + + useEffect(() => { + fetchMetrics() + }, [timeframe]) + + const fetchMetrics = async () => { + setLoading(true) + setError(null) + + try { + const baseUrl = + typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" + const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}` + + const response = await fetch(apiUrl) + + if (!response.ok) { + throw new Error(`Failed to fetch node metrics: ${response.status}`) + } + + const result = await response.json() + + if (!result.data || !Array.isArray(result.data)) { + throw new Error("Invalid data format received from server") + } + + if (result.data.length === 0) { + setData([]) + setLoading(false) + return + } + + const transformedData = result.data.map((item: any) => { + const date = new Date(item.time * 1000) + let timeLabel = "" + + if (timeframe === "hour") { + timeLabel = date.toLocaleString("en-US", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }) + } else if (timeframe === "day") { + timeLabel = date.toLocaleString("en-US", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }) + } else if (timeframe === "week") { + timeLabel = date.toLocaleString("en-US", { + month: "short", + day: "numeric", + hour: "2-digit", + hour12: false, + }) + } else { + timeLabel = date.toLocaleString("en-US", { + month: "short", + day: "numeric", + }) + } + + return { + time: timeLabel, + timestamp: item.time, + netIn: item.netin ? Number((item.netin / 1024 / 1024 / 1024).toFixed(2)) : 0, + netOut: item.netout ? Number((item.netout / 1024 / 1024 / 1024).toFixed(2)) : 0, + } + }) + + setData(transformedData) + } catch (err: any) { + console.error("[v0] Error fetching network metrics:", err) + setError(err.message || "Error loading metrics") + } finally { + setLoading(false) + } + } + + const tickInterval = Math.ceil(data.length / 8) + + const handleLegendClick = (dataKey: string) => { + setVisibleLines((prev) => ({ + ...prev, + [dataKey]: !prev[dataKey as keyof typeof prev], + })) + } + + const renderLegend = (props: any) => { + const { payload } = props + return ( +
+ {payload.map((entry: any, index: number) => { + const isVisible = visibleLines[entry.dataKey as keyof typeof visibleLines] + return ( +
handleLegendClick(entry.dataKey)} + style={{ opacity: isVisible ? 1 : 0.4 }} + > +
+ {entry.value} +
+ ) + })} +
+ ) + } + + if (loading) { + return ( +
+ +
+ ) + } + + if (error) { + return ( +
+

Network metrics not available yet

+

{error}

+
+ ) + } + + if (data.length === 0) { + return ( +
+

No network metrics available

+
+ ) + } + + return ( + + + + + + } /> + + + + + + ) +} diff --git a/AppImage/components/system-overview.tsx b/AppImage/components/system-overview.tsx index 4f8ade5..387b146 100644 --- a/AppImage/components/system-overview.tsx +++ b/AppImage/components/system-overview.tsx @@ -6,6 +6,8 @@ import { Progress } from "./ui/progress" import { Badge } from "./ui/badge" import { Cpu, MemoryStick, Thermometer, Server, Zap, AlertCircle, HardDrive, Network } from "lucide-react" import { NodeMetricsCharts } from "./node-metrics-charts" +import { NetworkTrafficChart } from "./network-traffic-chart" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" interface SystemData { cpu_usage: number @@ -229,6 +231,7 @@ export function SystemOverview() { const [networkData, setNetworkData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) + const [networkTimeframe, setNetworkTimeframe] = useState("day") useEffect(() => { const fetchData = async () => { @@ -624,9 +627,22 @@ export function SystemOverview() { {/* Network Summary */} - - - Network Overview + +
+ + Network Overview +
+
@@ -687,6 +703,10 @@ export function SystemOverview() {
+ +
+ +
) : (
Network data not available