import { useCallback, useEffect, useRef, useState } from "react"; import { BarChart3, Activity, Cpu, HardDrive, Database, RefreshCw, Server, Wifi, Layers, } from "lucide-react"; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, } from "recharts"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; // ─── Prometheus API ───────────────────────────────────────────────── const PROM = "https://telemetry.imrnes.team/prometheus/api/v1"; const INST = "100.96.248.86"; // orange VPS interface PromValue { time: string; value: number; } async function queryRange(query: string, steps = 60): Promise { const now = Math.floor(Date.now() / 1000); const start = now - 3600; const q = `query=${encodeURIComponent(query)}&start=${start}&end=${now}&step=${steps}`; const res = await fetch(`${PROM}/query_range?${q}`); if (!res.ok) throw new Error(`Prometheus: ${res.status}`); const data = await res.json(); const results = data?.data?.result ?? []; if (results.length === 0) return []; return results[0].values.map((v: [number, string]) => ({ time: new Date(v[0] * 1000).toISOString(), value: parseFloat(v[1]), })); } async function queryInstant(query: string): Promise { const res = await fetch(`${PROM}/query?query=${encodeURIComponent(query)}`); if (!res.ok) return null; const data = await res.json(); const results = data?.data?.result ?? []; if (results.length === 0) return null; return parseFloat(results[0].value[1]); } // ─── Helpers ───────────────────────────────────────────────────────── function fmtPct(v: number): string { return v.toFixed(1) + "%"; } function fmtBytes(v: number): string { if (v >= 1 << 30) return (v / (1 << 30)).toFixed(1) + " GiB"; if (v >= 1 << 20) return (v / (1 << 20)).toFixed(1) + " MiB"; if (v >= 1 << 10) return (v / (1 << 10)).toFixed(1) + " KiB"; return v.toFixed(0) + " B"; } // ─── UI Components ────────────────────────────────────────────────── function StatCard({ icon: Icon, label, value, sub, color }: { icon: typeof BarChart3; label: string; value: string; sub?: string; color: string; }) { return ( {label}
{value}
{sub &&

{sub}

}
); } function ChartCard({ title, data, color, unit = "", domain, valueFormatter }: { title: string; data: PromValue[]; color: string; unit?: string; domain?: [number, number]; valueFormatter?: (v: number) => string; }) { if (!data || data.length === 0) { return ( {title}
No data
); } return ( {title} new Date(v).toLocaleTimeString()} formatter={(val: unknown) => { const v = typeof val === "number" ? val : 0; return [valueFormatter ? valueFormatter(v) : v.toFixed(2), title]; }} /> ); } function GaugeCard({ label, value, max, unit, color }: { label: string; value: number; max: number; unit: string; color: string; }) { const pct = max > 0 ? Math.min((value / max) * 100, 100) : 0; return ( {label}
{typeof value === "number" ? value.toFixed(1) : "?"} {unit}
); } // ─── Section Header ───────────────────────────────────────────────── function SectionTitle({ icon: Icon, title, color }: { icon: typeof Layers; title: string; color?: string }) { return (

{title}

); } // ─── Main Page ─────────────────────────────────────────────────────── export function TelemetryPage() { // System const [cpuData, setCpuData] = useState([]); const [memData, setMemData] = useState([]); const [diskData, setDiskData] = useState([]); const [cpuNow, setCpuNow] = useState(null); const [memNow, setMemNow] = useState(null); const [diskNow, setDiskNow] = useState(null); const [loadNow, setLoadNow] = useState(null); const [netRx, setNetRx] = useState(null); const [netTx, setNetTx] = useState(null); const [netRxData, setNetRxData] = useState([]); const [netTxData, setNetTxData] = useState([]); // ZeaVis API const [apiReqsTotal, setApiReqsTotal] = useState(null); const [apiReqsActive, setApiReqsActive] = useState(null); const [apiLatency, setApiLatency] = useState(null); const [apiReqsData, setApiReqsData] = useState([]); const [apiLatencyData, setApiLatencyData] = useState([]); // ML const [mlModelLoaded, setMlModelLoaded] = useState(null); // NodeJS const [heapUsed, setHeapUsed] = useState(null); const [heapTotal, setHeapTotal] = useState(null); const [eventLoopLag, setEventLoopLag] = useState(null); const [activeHandles, setActiveHandles] = useState(null); const [activeRequests, setActiveRequests] = useState(null); const [heapData, setHeapData] = useState([]); const [elLagData, setElLagData] = useState([]); // Process const [procCpu, setProcCpu] = useState(null); const [procMem, setProcMem] = useState(null); const [procFds, setProcFds] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [refreshing, setRefreshing] = useState(false); const intRef = useRef(0); const fetchAll = useCallback(async (isRefresh = false) => { try { if (isRefresh) setRefreshing(true); else setLoading(true); setError(null); const [ cpuR, memR, diskR, cpuN, memN, diskN, loadN, netRxR, netTxR, netRxN, netTxN, apiReqsN, apiActiveN, apiLatN, apiReqsR, apiLatR, mlN, heapN, heapTN, elN, ahN, arN, heapR, elR, procCpuN, procMemN, procFdsN, ] = await Promise.all([ // Range queries queryRange(`100 - (avg(rate(node_cpu_seconds_total{mode="idle",instance="${INST}:9100"}[5m])) * 100)`, 60), queryRange(`(1 - node_memory_MemAvailable_bytes{instance="${INST}:9100"} / node_memory_MemTotal_bytes{instance="${INST}:9100"}) * 100`, 60), queryRange(`(1 - node_filesystem_avail_bytes{instance="${INST}:9100",mountpoint="/"} / node_filesystem_size_bytes{instance="${INST}:9100",mountpoint="/"}) * 100`, 60), // Instant queries - system queryInstant(`100 - (avg(rate(node_cpu_seconds_total{mode="idle",instance="${INST}:9100"}[5m])) * 100)`), queryInstant(`(1 - node_memory_MemAvailable_bytes{instance="${INST}:9100"} / node_memory_MemTotal_bytes{instance="${INST}:9100"}) * 100`), queryInstant(`(1 - node_filesystem_avail_bytes{instance="${INST}:9100",mountpoint="/"} / node_filesystem_size_bytes{instance="${INST}:9100",mountpoint="/"}) * 100`), queryInstant(`node_load15{instance="${INST}:9100"}`), // Network queryRange(`rate(node_network_receive_bytes_total{instance="${INST}:9100",device="eth0"}[5m])`, 60), queryRange(`rate(node_network_transmit_bytes_total{instance="${INST}:9100",device="eth0"}[5m])`, 60), queryInstant(`rate(node_network_receive_bytes_total{instance="${INST}:9100",device="eth0"}[5m])`), queryInstant(`rate(node_network_transmit_bytes_total{instance="${INST}:9100",device="eth0"}[5m])`), // API queryInstant(`zeavis_api_http_requests_total{instance="${INST}:3000"}`), queryInstant(`zeavis_api_http_requests_active{instance="${INST}:3000"}`), queryInstant(`zeavis_api_http_request_duration_seconds_sum{instance="${INST}:3000"} / zeavis_api_http_request_duration_seconds_count{instance="${INST}:3000"}`), queryRange(`zeavis_api_http_requests_total{instance="${INST}:3000"}`, 60), queryRange(`zeavis_api_http_request_duration_seconds_sum{instance="${INST}:3000"} / zeavis_api_http_request_duration_seconds_count{instance="${INST}:3000"}`, 60), // ML queryInstant(`zeavis_ml_zeavis_ml_model_load_status{instance="${INST}:8000"}`), // NodeJS queryInstant(`nodejs_heap_size_used_bytes{instance="${INST}:3000"}`), queryInstant(`nodejs_heap_size_total_bytes{instance="${INST}:3000"}`), queryInstant(`nodejs_eventloop_lag_seconds{instance="${INST}:3000"}`), queryInstant(`nodejs_active_handles_total{instance="${INST}:3000"}`), queryInstant(`nodejs_active_requests_total{instance="${INST}:3000"}`), queryRange(`nodejs_heap_size_used_bytes{instance="${INST}:3000"}`, 60), queryRange(`nodejs_eventloop_lag_seconds{instance="${INST}:3000"}`, 60), // Process queryInstant(`rate(process_cpu_seconds_total{instance="${INST}:3000"}[5m])`), queryInstant(`process_resident_memory_bytes{instance="${INST}:3000"}`), queryInstant(`process_open_fds{instance="${INST}:3000"}`), ]); setCpuData(cpuR); setMemData(memR); setDiskData(diskR); setCpuNow(cpuN); setMemNow(memN); setDiskNow(diskN); setLoadNow(loadN); setNetRx(netRxN); setNetTx(netTxN); setNetRxData(netRxR); setNetTxData(netTxR); setApiReqsTotal(apiReqsN); setApiReqsActive(apiActiveN); setApiLatency(apiLatN); setApiReqsData(apiReqsR); setApiLatencyData(apiLatR); setMlModelLoaded(mlN); setHeapUsed(heapN); setHeapTotal(heapTN); setEventLoopLag(elN); setActiveHandles(ahN); setActiveRequests(arN); setHeapData(heapR); setElLagData(elR); setProcCpu(procCpuN); setProcMem(procMemN); setProcFds(procFdsN); } catch (e) { setError(e instanceof Error ? e.message : "Unknown error"); } finally { setLoading(false); setRefreshing(false); } }, []); useEffect(() => { fetchAll(); intRef.current = window.setInterval(() => fetchAll(true), 30_000); return () => clearInterval(intRef.current); }, [fetchAll]); if (loading && cpuNow === null) { return (

Loading telemetry data...

); } return (
{/* Header */}

Telemetry Dashboard

Real-time monitoring dari performa sistem dan aplikasi ZeaVis Edu {error && (partial — {error})}

{/* ===== SYSTEM ===== */}
{/* ===== APPLICATION ===== */}
{/* ===== ML SERVICE ===== */}
{/* ===== NODEJS DETAIL ===== */}
(v / 1024 / 1024).toFixed(1) + " MiB"} /> (v * 1000).toFixed(2) + " ms"} />
); }