"use client" import { useState, useEffect } from "react" import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" 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 memory_usage: number memory_total: number memory_used: number temperature: number uptime: string load_average: number[] hostname: string node_id: string timestamp: string cpu_cores?: number cpu_threads?: number proxmox_version?: string kernel_version?: string available_updates?: number } interface VMData { vmid: number name: string status: string cpu: number mem: number maxmem: number disk: number maxdisk: number uptime: number type?: string } interface StorageData { total: number used: number available: number disk_count: number disks: Array<{ name: string mountpoint: string total: number used: number available: number usage_percent: number }> } interface NetworkData { interfaces: Array<{ name: string status: string addresses: Array<{ ip: string; netmask: string }> }> traffic: { bytes_sent: number bytes_recv: number packets_sent: number packets_recv: number } physical_active_count?: number physical_total_count?: number bridge_active_count?: number bridge_total_count?: number physical_interfaces?: Array<{ name: string status: string addresses: Array<{ ip: string; netmask: string }> }> bridge_interfaces?: Array<{ name: string status: string addresses: Array<{ ip: string; netmask: string }> }> } interface ProxmoxStorageData { storage: Array<{ name: string type: string status: string total: number used: number available: number percent: number }> } const fetchSystemData = async (): Promise => { try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const apiUrl = `${baseUrl}/api/system` const response = await fetch(apiUrl, { method: "GET", headers: { "Content-Type": "application/json", }, cache: "no-store", }) if (!response.ok) { throw new Error(`Flask server responded with status: ${response.status}`) } const data = await response.json() return data } catch (error) { console.error("[v0] Failed to fetch system data:", error) return null } } const fetchVMData = async (): Promise => { try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const apiUrl = `${baseUrl}/api/vms` const response = await fetch(apiUrl, { method: "GET", headers: { "Content-Type": "application/json", }, cache: "no-store", }) if (!response.ok) { throw new Error(`Flask server responded with status: ${response.status}`) } const data = await response.json() return Array.isArray(data) ? data : data.vms || [] } catch (error) { console.error("[v0] Failed to fetch VM data:", error) return [] } } const fetchStorageData = async (): Promise => { try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const apiUrl = `${baseUrl}/api/storage/summary` const response = await fetch(apiUrl, { method: "GET", headers: { "Content-Type": "application/json", }, cache: "no-store", }) if (!response.ok) { console.log("[v0] Storage API not available (this is normal if not configured)") return null } const data = await response.json() return data } catch (error) { console.log("[v0] Storage data unavailable:", error instanceof Error ? error.message : "Unknown error") return null } } const fetchNetworkData = async (): Promise => { try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const apiUrl = `${baseUrl}/api/network/summary` const response = await fetch(apiUrl, { method: "GET", headers: { "Content-Type": "application/json", }, cache: "no-store", }) if (!response.ok) { console.log("[v0] Network API not available (this is normal if not configured)") return null } const data = await response.json() return data } catch (error) { console.log("[v0] Network data unavailable:", error instanceof Error ? error.message : "Unknown error") return null } } const fetchProxmoxStorageData = async (): Promise => { try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const apiUrl = `${baseUrl}/api/proxmox-storage` const response = await fetch(apiUrl, { method: "GET", headers: { "Content-Type": "application/json", }, cache: "no-store", }) if (!response.ok) { console.log("[v0] Proxmox storage API not available") return null } const data = await response.json() return data } catch (error) { console.log("[v0] Proxmox storage data unavailable:", error instanceof Error ? error.message : "Unknown error") return null } } export function SystemOverview() { const [systemData, setSystemData] = useState(null) const [vmData, setVmData] = useState([]) const [storageData, setStorageData] = useState(null) const [proxmoxStorageData, setProxmoxStorageData] = useState(null) const [networkData, setNetworkData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [networkTimeframe, setNetworkTimeframe] = useState("day") const [networkTotals, setNetworkTotals] = useState<{ received: number; sent: number }>({ received: 0, sent: 0 }) useEffect(() => { const fetchData = async () => { try { setLoading(true) setError(null) const systemResult = await fetchSystemData() if (!systemResult) { setError("Flask server not available. Please ensure the server is running.") setLoading(false) return } setSystemData(systemResult) } catch (err) { console.error("[v0] Error fetching system data:", err) setError("Failed to connect to Flask server. Please check your connection.") } finally { setLoading(false) } } fetchData() const systemInterval = setInterval(() => { fetchSystemData().then((data) => { if (data) setSystemData(data) }) }, 10000) return () => { clearInterval(systemInterval) } }, []) useEffect(() => { const fetchVMs = async () => { const vmResult = await fetchVMData() setVmData(vmResult) } fetchVMs() const vmInterval = setInterval(fetchVMs, 60000) return () => { clearInterval(vmInterval) } }, []) useEffect(() => { const fetchStorage = async () => { const storageResult = await fetchStorageData() setStorageData(storageResult) const proxmoxStorageResult = await fetchProxmoxStorageData() setProxmoxStorageData(proxmoxStorageResult) } fetchStorage() const storageInterval = setInterval(fetchStorage, 60000) return () => { clearInterval(storageInterval) } }, []) useEffect(() => { const fetchNetwork = async () => { const networkResult = await fetchNetworkData() setNetworkData(networkResult) } fetchNetwork() const networkInterval = setInterval(fetchNetwork, 60000) return () => { clearInterval(networkInterval) } }, []) if (loading) { return (
Connecting to ProxMenux Monitor...
Fetching real-time system data
{[...Array(4)].map((_, i) => (
))}
) } if (error || !systemData) { return (
Flask Server Not Available
{error || "Unable to connect to the Flask server. Please ensure the server is running and try again."}
) } const vmStats = { total: vmData.length, running: vmData.filter((vm) => vm.status === "running").length, stopped: vmData.filter((vm) => vm.status === "stopped").length, lxc: vmData.filter((vm) => vm.type === "lxc").length, vms: vmData.filter((vm) => vm.type === "qemu" || !vm.type).length, } const getTemperatureStatus = (temp: number) => { if (temp === 0) return { status: "N/A", color: "bg-gray-500/10 text-gray-500 border-gray-500/20" } if (temp < 60) return { status: "Normal", color: "bg-green-500/10 text-green-500 border-green-500/20" } if (temp < 75) return { status: "Warm", color: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20" } return { status: "Hot", color: "bg-red-500/10 text-red-500 border-red-500/20" } } const formatUptime = (seconds: number) => { if (!seconds || seconds === 0) return "Stopped" const days = Math.floor(seconds / 86400) const hours = Math.floor((seconds % 86400) / 3600) const minutes = Math.floor((seconds % 3600) / 60) if (days > 0) return `${days}d ${hours}h` if (hours > 0) return `${hours}h ${minutes}m` return `${minutes}m` } const formatBytes = (bytes: number) => { return (bytes / 1024 ** 3).toFixed(2) } const formatStorage = (sizeInGB: number): string => { if (sizeInGB < 1) { // Less than 1 GB, show in MB return `${(sizeInGB * 1024).toFixed(1)} MB` } else if (sizeInGB < 1024) { // Less than 1024 GB, show in GB return `${sizeInGB.toFixed(1)} GB` } else { // 1024 GB or more, show in TB return `${(sizeInGB / 1024).toFixed(2)} TB` } } const tempStatus = getTemperatureStatus(systemData.temperature) const localStorage = proxmoxStorageData?.storage.find((s) => s.name === "local") const vmLxcStorages = proxmoxStorageData?.storage.filter( (s) => // Include only local storage types that can host VMs/LXCs (s.type === "lvm" || s.type === "lvmthin" || s.type === "zfspool" || s.type === "btrfs" || s.type === "dir") && // Exclude network storage s.type !== "nfs" && s.type !== "cifs" && s.type !== "iscsi" && // Exclude the "local" storage (used for ISOs/templates) s.name !== "local", ) const vmLxcStorageTotal = vmLxcStorages?.reduce((acc, s) => acc + s.total, 0) || 0 const vmLxcStorageUsed = vmLxcStorages?.reduce((acc, s) => acc + s.used, 0) || 0 const vmLxcStorageAvailable = vmLxcStorages?.reduce((acc, s) => acc + s.available, 0) || 0 const vmLxcStoragePercent = vmLxcStorageTotal > 0 ? (vmLxcStorageUsed / vmLxcStorageTotal) * 100 : 0 const getLoadStatus = (load: number, cores: number) => { if (load < cores) { return { status: "Normal", color: "bg-green-500/10 text-green-500 border-green-500/20" } } else if (load < cores * 1.5) { return { status: "Moderate", color: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20" } } else { return { status: "High", color: "bg-red-500/10 text-red-500 border-red-500/20" } } } const systemAlerts = [] if (systemData.available_updates && systemData.available_updates > 0) { systemAlerts.push({ type: "warning", message: `${systemData.available_updates} updates available`, }) } if (vmStats.stopped > 0) { systemAlerts.push({ type: "info", message: `${vmStats.stopped} VM${vmStats.stopped > 1 ? "s" : ""} stopped`, }) } if (systemData.temperature > 75) { systemAlerts.push({ type: "warning", message: "High temperature detected", }) } if (localStorage && localStorage.percent > 90) { systemAlerts.push({ type: "warning", message: "System storage almost full", }) } const loadStatus = getLoadStatus(systemData.load_average[0], systemData.cpu_cores || 8) const getTimeframeLabel = (timeframe: string): string => { switch (timeframe) { case "hour": return "1h" case "day": return "24h" case "week": return "7d" case "month": return "30d" case "year": return "1y" default: return timeframe } } return (
{/* Key Metrics Cards */}
CPU Usage
{systemData.cpu_usage}%

Real-time usage

Memory Usage
{systemData.memory_used.toFixed(1)} GB

{systemData.memory_usage.toFixed(1)}% of{" "} {systemData.memory_total} GB

Temperature
{systemData.temperature === 0 ? "N/A" : `${systemData.temperature}°C`}
{tempStatus.status}

{systemData.temperature === 0 ? "No sensor available" : "Live temperature reading"}

Active VM & LXC
{vmStats.running}
{vmStats.running} Running {vmStats.stopped > 0 && ( {vmStats.stopped} Stopped )}

Total: {vmStats.vms} VMs, {vmStats.lxc} LXC

{/* Node Metrics Charts */}
{/* Storage Summary */} Storage Overview {storageData ? (
Total Capacity: {storageData.total} TB
Physical Disks: {storageData.disk_count} disk{storageData.disk_count !== 1 ? "s" : ""}
{vmLxcStorages && vmLxcStorages.length > 0 ? (
VM/LXC Storage
Used: {formatStorage(vmLxcStorageUsed)}
Available: {formatStorage(vmLxcStorageAvailable)}
{formatStorage(vmLxcStorageUsed)} / {formatStorage(vmLxcStorageTotal)} {vmLxcStoragePercent.toFixed(1)}%
{vmLxcStorages.length > 1 && (
{vmLxcStorages.length} storage volume{vmLxcStorages.length > 1 ? "s" : ""}
)}
) : (
VM/LXC Storage
No VM/LXC storage configured
)} {localStorage && (
Local Storage (System)
Used: {formatStorage(localStorage.used)}
Available: {formatStorage(localStorage.available)}
{formatStorage(localStorage.used)} / {formatStorage(localStorage.total)} {localStorage.percent.toFixed(1)}%
)}
) : (
Storage data not available
)}
{/* Network Summary */}
Network Overview
{networkData ? (
Active Interfaces: {(networkData.physical_active_count || 0) + (networkData.bridge_active_count || 0)}
{networkData.physical_interfaces && networkData.physical_interfaces.length > 0 && (
{networkData.physical_interfaces .filter((iface) => iface.status === "up") .map((iface) => ( {iface.name} ))}
)} {networkData.bridge_interfaces && networkData.bridge_interfaces.length > 0 && (
{networkData.bridge_interfaces .filter((iface) => iface.status === "up") .map((iface) => ( {iface.name} ))}
)}
Received: ↓ {formatStorage(networkTotals.received)} ({getTimeframeLabel(networkTimeframe)})
Sent: ↑ {formatStorage(networkTotals.sent)} ({getTimeframeLabel(networkTimeframe)})
) : (
Network data not available
)}
{/* System Information */}
System Information
Uptime: {systemData.uptime}
Proxmox Version: {systemData.proxmox_version || "N/A"}
Kernel: {systemData.kernel_version || "Linux"}
{systemData.available_updates !== undefined && systemData.available_updates > 0 && (
Available Updates: {systemData.available_updates} packages
)}
{/* System Health & Alerts */} System Overview
Load Average (1m):
{systemData.load_average[0].toFixed(2)} {loadStatus.status}
CPU Threads: {systemData.cpu_threads || "N/A"}
Physical Disks: {storageData?.disk_count || "N/A"}
Network Interfaces: {networkData?.physical_total_count || networkData?.physical_interfaces?.length || "N/A"}
) }