"use client" import { useState } from "react" import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" import { Badge } from "./ui/badge" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./ui/dialog" import { Wifi, Globe, Shield, Activity, Network, Router, AlertCircle, Zap } from "lucide-react" import useSWR from "swr" interface NetworkData { physical_interfaces?: NetworkInterface[] bridge_interfaces?: NetworkInterface[] vm_lxc_interfaces?: NetworkInterface[] traffic: { bytes_sent: number bytes_recv: number packets_sent?: number packets_recv?: number packet_loss_in?: number packet_loss_out?: number } physical_active_count?: number physical_total_count?: number bridge_active_count?: number bridge_total_count?: number vm_lxc_active_count?: number vm_lxc_total_count?: number } interface NetworkInterface { name: string type: string status: string speed: number duplex: string mtu: number mac_address: string | null addresses: Array<{ ip: string netmask: string }> bytes_sent?: number bytes_recv?: number packets_sent?: number packets_recv?: number errors_in?: number errors_out?: number drops_in?: number drops_out?: number bond_mode?: string bond_slaves?: string[] bond_active_slave?: string | null bridge_members?: string[] packet_loss_in?: number packet_loss_out?: number vmid?: number vm_name?: string vm_type?: string vm_status?: string } const getInterfaceTypeBadge = (type: string) => { switch (type) { case "physical": return { color: "bg-blue-500/10 text-blue-500 border-blue-500/20", label: "Physical" } case "bridge": return { color: "bg-green-500/10 text-green-500 border-green-500/20", label: "Bridge" } case "bond": return { color: "bg-purple-500/10 text-purple-500 border-purple-500/20", label: "Bond" } case "vlan": return { color: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20", label: "VLAN" } default: return { color: "bg-gray-500/10 text-gray-500 border-gray-500/20", label: "Unknown" } } } const getVMTypeBadge = (vmType: string | undefined) => { if (vmType === "lxc") { return { color: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20", label: "LXC" } } else if (vmType === "vm") { return { color: "bg-purple-500/10 text-purple-500 border-purple-500/20", label: "VM" } } return { color: "bg-gray-500/10 text-gray-500 border-gray-500/20", label: "Unknown" } } const formatBytes = (bytes: number | undefined): string => { if (!bytes || bytes === 0) return "0 B" const k = 1024 const sizes = ["B", "KB", "MB", "GB", "TB"] const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}` } const formatSpeed = (speed: number): string => { if (speed === 0) return "N/A" if (speed >= 1000) return `${(speed / 1000).toFixed(1)} Gbps` return `${speed} Mbps` } const fetcher = async (url: string): Promise => { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" const fullUrl = `${baseUrl}${url}` const response = await fetch(fullUrl, { method: "GET", headers: { "Content-Type": "application/json", }, signal: AbortSignal.timeout(5000), }) if (!response.ok) { throw new Error(`Flask server responded with status: ${response.status}`) } return response.json() } export function NetworkMetrics() { const { data: networkData, error, isLoading, } = useSWR("/api/network", fetcher, { refreshInterval: 30000, revalidateOnFocus: false, revalidateOnReconnect: true, }) const [selectedInterface, setSelectedInterface] = useState(null) if (isLoading) { return (
Loading network data...
) } if (error || !networkData) { return (
Flask Server Not Available
{error?.message || "Unable to connect to the Flask server. Please ensure the server is running and try again."}
) } const trafficInGB = (networkData.traffic.bytes_recv / 1024 ** 3).toFixed(2) const trafficOutGB = (networkData.traffic.bytes_sent / 1024 ** 3).toFixed(2) const packetsRecvK = networkData.traffic.packets_recv ? (networkData.traffic.packets_recv / 1000).toFixed(0) : "0" const physicalInterfaces = networkData.physical_interfaces || [] const bridgeInterfaces = networkData.bridge_interfaces || [] const vmLxcInterfaces = networkData.vm_lxc_interfaces || [] return (
{/* Network Overview Cards */}
Network Traffic
{trafficInGB} GB
↓ {trafficInGB} GB ↑ {trafficOutGB} GB

Total data transferred

Active Interfaces
{(networkData.physical_active_count ?? 0) + (networkData.bridge_active_count ?? 0)}
Physical: {networkData.physical_active_count ?? 0}/{networkData.physical_total_count ?? 0}
Bridges: {networkData.bridge_active_count ?? 0}/{networkData.bridge_total_count ?? 0}
Firewall Status
Active
Protected

System protected

Packets
{packetsRecvK}K
Received
{networkData.traffic.packet_loss_in !== undefined && networkData.traffic.packet_loss_in > 0 && (

Loss: {networkData.traffic.packet_loss_in}%

)} {(!networkData.traffic.packet_loss_in || networkData.traffic.packet_loss_in === 0) && (

No packet loss

)}
{/* Physical Interfaces */} {physicalInterfaces.length > 0 && ( Physical Interfaces {networkData.physical_active_count ?? 0}/{networkData.physical_total_count ?? 0} Active
{physicalInterfaces.map((iface, index) => { const typeBadge = getInterfaceTypeBadge(iface.type) return (
setSelectedInterface(iface)} >
{iface.name}
{typeBadge.label}
{iface.status.toUpperCase()}
IP Address
{iface.addresses.length > 0 ? iface.addresses[0].ip : "N/A"}
Speed
{formatSpeed(iface.speed)}
Traffic
↓ {formatBytes(iface.bytes_recv)} {" / "} ↑ {formatBytes(iface.bytes_sent)}
{iface.mac_address && (
MAC
{iface.mac_address}
)}
) })}
)} {/* Bridge Interfaces */} {bridgeInterfaces.length > 0 && ( Bridge Interfaces {networkData.bridge_active_count ?? 0}/{networkData.bridge_total_count ?? 0} Active
{bridgeInterfaces.map((iface, index) => { const typeBadge = getInterfaceTypeBadge(iface.type) return (
setSelectedInterface(iface)} >
{iface.name}
{typeBadge.label}
{iface.status.toUpperCase()}
IP Address
{iface.addresses.length > 0 ? iface.addresses[0].ip : "N/A"}
Speed
{formatSpeed(iface.speed)}
Traffic
↓ {formatBytes(iface.bytes_recv)} {" / "} ↑ {formatBytes(iface.bytes_sent)}
{iface.mac_address && (
MAC
{iface.mac_address}
)}
) })}
)} {/* VM & LXC Network Interfaces */} {vmLxcInterfaces.length > 0 && ( VM & LXC Network Interfaces {networkData.vm_lxc_active_count ?? 0}/{networkData.vm_lxc_total_count ?? 0} Active
{vmLxcInterfaces.map((iface, index) => { const vmTypeBadge = getVMTypeBadge(iface.vm_type) return (
setSelectedInterface(iface)} >
{iface.name}
{vmTypeBadge.label} {iface.vm_name && (
→ {iface.vm_name}
)}
{iface.status.toUpperCase()}
VMID
{iface.vmid ?? "N/A"}
Speed
{formatSpeed(iface.speed)}
Traffic
↓ {formatBytes(iface.bytes_recv)} {" / "} ↑ {formatBytes(iface.bytes_sent)}
{iface.mac_address && (
MAC
{iface.mac_address}
)}
) })}
)} {/* Interface Details Dialog */} setSelectedInterface(null)}> {selectedInterface?.name} - Interface Details {selectedInterface && (
{/* Basic Information */}

Basic Information

Interface Name
{selectedInterface.name}
Type
{getInterfaceTypeBadge(selectedInterface.type).label}
Status
{selectedInterface.status.toUpperCase()}
Speed
{formatSpeed(selectedInterface.speed)}
Duplex
{selectedInterface.duplex}
MTU
{selectedInterface.mtu}
{selectedInterface.mac_address && (
MAC Address
{selectedInterface.mac_address}
)}
{/* IP Addresses */} {selectedInterface.addresses.length > 0 && (

IP Addresses

{selectedInterface.addresses.map((addr, idx) => (
{addr.ip}
Netmask: {addr.netmask}
))}
)} {/* Traffic Statistics */}

Traffic Statistics

Bytes Received
{formatBytes(selectedInterface.bytes_recv)}
Bytes Sent
{formatBytes(selectedInterface.bytes_sent)}
Packets Received
{selectedInterface.packets_recv?.toLocaleString() || "N/A"}
Packets Sent
{selectedInterface.packets_sent?.toLocaleString() || "N/A"}
Errors In
{selectedInterface.errors_in || 0}
Errors Out
{selectedInterface.errors_out || 0}
Drops In
{selectedInterface.drops_in || 0}
Drops Out
{selectedInterface.drops_out || 0}
{selectedInterface.packet_loss_in !== undefined && (
Packet Loss In
1 ? "text-red-500" : "text-green-500"}`} > {selectedInterface.packet_loss_in}%
)} {selectedInterface.packet_loss_out !== undefined && (
Packet Loss Out
1 ? "text-red-500" : "text-green-500"}`} > {selectedInterface.packet_loss_out}%
)}
{/* Bond Information */} {selectedInterface.type === "bond" && selectedInterface.bond_slaves && (

Bond Configuration

Bonding Mode
{selectedInterface.bond_mode || "Unknown"}
{selectedInterface.bond_active_slave && (
Active Slave
{selectedInterface.bond_active_slave}
)}
Slave Interfaces
{selectedInterface.bond_slaves.map((slave, idx) => ( {slave} ))}
)} {/* Bridge Information */} {selectedInterface.type === "bridge" && selectedInterface.bridge_members && (

Bridge Configuration

Member Interfaces
{selectedInterface.bridge_members.length > 0 ? ( selectedInterface.bridge_members.map((member, idx) => ( {member} )) ) : (
No members
)}
)}
)}
) }