diff --git a/AppImage/components/network-metrics.tsx b/AppImage/components/network-metrics.tsx index 4169270..a10db1e 100644 --- a/AppImage/components/network-metrics.tsx +++ b/AppImage/components/network-metrics.tsx @@ -4,7 +4,7 @@ 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, Activity, Network, Router, AlertCircle, Zap, Shield } from "lucide-react" +import { Wifi, Activity, Network, Router, AlertCircle, Zap } from "lucide-react" import useSWR from "swr" interface NetworkData { @@ -214,36 +214,6 @@ export function NetworkMetrics() { - - - Firewall Status - - - -
Active
- - Protected - -

System protected

-
-
- - - - Packets - - - -
{packetsRecvK}K
- - Received - -

No packet loss

-
-
- - - {networkData.physical_interfaces && networkData.physical_interfaces.length > 0 && ( @@ -268,7 +238,7 @@ export function NetworkMetrics() { {/* First row: Icon, Name, Type Badge, Status */}
-
+
{interface_.name}
{typeBadge.label} @@ -327,208 +297,208 @@ export function NetworkMetrics() {
- )} - {networkData.bridge_interfaces && networkData.bridge_interfaces.length > 0 && ( - - - - - Bridge Interfaces - - {networkData.bridge_active_count ?? 0} / {networkData.bridge_total_count ?? 0} Active - - - - -
- {networkData.bridge_interfaces.map((interface_, index) => { - const typeBadge = getInterfaceTypeBadge(interface_.type) + {networkData.bridge_interfaces && networkData.bridge_interfaces.length > 0 && ( + + + + + Bridge Interfaces + + {networkData.bridge_active_count ?? 0} / {networkData.bridge_total_count ?? 0} Active + + + + +
+ {networkData.bridge_interfaces.map((interface_, index) => { + const typeBadge = getInterfaceTypeBadge(interface_.type) - return ( -
setSelectedInterface(interface_)} - > - {/* First row: Icon, Name, Type Badge, Physical Interface, Status */} -
- -
-
{interface_.name}
- - {typeBadge.label} - - {interface_.bridge_physical_interface && ( -
- → {interface_.bridge_physical_interface} - {interface_.bridge_physical_interface.startsWith("bond") && - networkData.physical_interfaces && ( - <> - {(() => { - const bondInterface = networkData.physical_interfaces.find( - (iface) => iface.name === interface_.bridge_physical_interface, - ) - if (bondInterface?.bond_slaves && bondInterface.bond_slaves.length > 0) { - return ( - - ({bondInterface.bond_slaves.join(", ")}) - + return ( +
setSelectedInterface(interface_)} + > + {/* First row: Icon, Name, Type Badge, Physical Interface, Status */} +
+ +
+
{interface_.name}
+ + {typeBadge.label} + + {interface_.bridge_physical_interface && ( +
+ → {interface_.bridge_physical_interface} + {interface_.bridge_physical_interface.startsWith("bond") && + networkData.physical_interfaces && ( + <> + {(() => { + const bondInterface = networkData.physical_interfaces.find( + (iface) => iface.name === interface_.bridge_physical_interface, ) - } - return null - })()} - + if (bondInterface?.bond_slaves && bondInterface.bond_slaves.length > 0) { + return ( + + ({bondInterface.bond_slaves.join(", ")}) + + ) + } + return null + })()} + + )} + {interface_.bridge_bond_slaves && interface_.bridge_bond_slaves.length > 0 && ( + + ({interface_.bridge_bond_slaves.join(", ")}) + )} - {interface_.bridge_bond_slaves && interface_.bridge_bond_slaves.length > 0 && ( - - ({interface_.bridge_bond_slaves.join(", ")}) - - )} -
- )} -
- - {interface_.status.toUpperCase()} - -
- - {/* Second row: Details - Responsive layout */} -
-
-
IP Address
-
- {interface_.addresses.length > 0 ? interface_.addresses[0].ip : "N/A"} +
+ )}
-
- -
-
Speed
-
- - {formatSpeed(interface_.speed)} -
-
- -
-
Traffic
-
- ↓ {formatBytes(interface_.bytes_recv)} - {" / "} - ↑ {formatBytes(interface_.bytes_sent)} -
-
- - {interface_.mac_address && ( -
-
MAC
-
- {interface_.mac_address} -
-
- )} -
-
- ) - })} -
- - - )} - - {networkData.vm_lxc_interfaces && networkData.vm_lxc_interfaces.length > 0 && ( - - - - - VM & LXC Network Interfaces - - {networkData.vm_lxc_active_count ?? 0} / {networkData.vm_lxc_total_count ?? 0} Active - - - - -
- {networkData.vm_lxc_interfaces.map((interface_, index) => { - const vmTypeBadge = getVMTypeBadge(interface_.vm_type) - - return ( -
setSelectedInterface(interface_)} - > - {/* First row: Icon, Name, VM/LXC Badge, VM Name, Status */} -
- -
-
{interface_.name}
- - {vmTypeBadge.label} + + {interface_.status.toUpperCase()} - {interface_.vm_name && ( -
→ {interface_.vm_name}
- )} -
- - {interface_.status.toUpperCase()} - -
- - {/* Second row: Details - Responsive layout */} -
-
-
VMID
-
{interface_.vmid ?? "N/A"}
-
-
Speed
-
- - {formatSpeed(interface_.speed)} -
-
- -
-
Traffic
-
- ↓ {formatBytes(interface_.bytes_recv)} - {" / "} - ↑ {formatBytes(interface_.bytes_sent)} -
-
- - {interface_.mac_address && ( -
-
MAC
-
- {interface_.mac_address} + {/* Second row: Details - Responsive layout */} +
+
+
IP Address
+
+ {interface_.addresses.length > 0 ? interface_.addresses[0].ip : "N/A"}
- )} + +
+
Speed
+
+ + {formatSpeed(interface_.speed)} +
+
+ +
+
Traffic
+
+ ↓ {formatBytes(interface_.bytes_recv)} + {" / "} + ↑ {formatBytes(interface_.bytes_sent)} +
+
+ + {interface_.mac_address && ( +
+
MAC
+
+ {interface_.mac_address} +
+
+ )} +
-
- ) - })} -
- - - )} + ) + })} +
+ + + )} + + {networkData.vm_lxc_interfaces && networkData.vm_lxc_interfaces.length > 0 && ( + + + + + VM & LXC Network Interfaces + + {networkData.vm_lxc_active_count ?? 0} / {networkData.vm_lxc_total_count ?? 0} Active + + + + +
+ {networkData.vm_lxc_interfaces.map((interface_, index) => { + const vmTypeBadge = getVMTypeBadge(interface_.vm_type) + + return ( +
setSelectedInterface(interface_)} + > + {/* First row: Icon, Name, VM/LXC Badge, VM Name, Status */} +
+ +
+
{interface_.name}
+ + {vmTypeBadge.label} + + {interface_.vm_name && ( +
→ {interface_.vm_name}
+ )} +
+ + {interface_.status.toUpperCase()} + +
+ + {/* Second row: Details - Responsive layout */} +
+
+
VMID
+
{interface_.vmid ?? "N/A"}
+
+ +
+
Speed
+
+ + {formatSpeed(interface_.speed)} +
+
+ +
+
Traffic
+
+ ↓ {formatBytes(interface_.bytes_recv)} + {" / "} + ↑ {formatBytes(interface_.bytes_sent)} +
+
+ + {interface_.mac_address && ( +
+
MAC
+
+ {interface_.mac_address} +
+
+ )} +
+
+ ) + })} +
+
+
+ )} +
{/* Interface Details Modal */} setSelectedInterface(null)}> @@ -721,36 +691,64 @@ export function NetworkMetrics() { )} {/* Bridge Information */} - {selectedInterface.type === "bridge" && selectedInterface.bridge_members && ( + {selectedInterface.type === "bridge" && (

Bridge Configuration

-
-
Virtual Member Interfaces
-
- {selectedInterface.bridge_members.length > 0 ? ( - selectedInterface.bridge_members - .filter( - (member) => - !member.startsWith("enp") && - !member.startsWith("eth") && - !member.startsWith("eno") && - !member.startsWith("ens") && - !member.startsWith("wlan") && - !member.startsWith("wlp"), - ) - .map((member, idx) => ( +
+ {selectedInterface.bridge_physical_interface && ( +
+
Physical Interface
+
+ {selectedInterface.bridge_physical_interface} +
+
+ )} + {selectedInterface.bridge_bond_slaves && selectedInterface.bridge_bond_slaves.length > 0 && ( +
+
Bond Slave Interfaces
+
+ {selectedInterface.bridge_bond_slaves.map((slave, idx) => ( - {member} + {slave} - )) - ) : ( -
No virtual members
- )} -
+ ))} +
+
+ )} + {selectedInterface.bridge_members && ( +
+
Virtual Member Interfaces
+
+ {selectedInterface.bridge_members.length > 0 ? ( + selectedInterface.bridge_members + .filter( + (member) => + !member.startsWith("enp") && + !member.startsWith("eth") && + !member.startsWith("eno") && + !member.startsWith("ens") && + !member.startsWith("wlan") && + !member.startsWith("wlp"), + ) + .map((member, idx) => ( + + {member} + + )) + ) : ( +
No virtual members
+ )} +
+
+ )}
)} diff --git a/AppImage/components/virtual-machines.tsx b/AppImage/components/virtual-machines.tsx index 75abecd..1027705 100644 --- a/AppImage/components/virtual-machines.tsx +++ b/AppImage/components/virtual-machines.tsx @@ -94,6 +94,24 @@ const formatBytes = (bytes: number | undefined): string => { return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}` } +const extractIPFromNetConfig = (netConfig: string | undefined): string => { + if (!netConfig) return "N/A" + + // Parse the network config string: name=eth0,bridge=vmbr0,gw=192.168.0.1,hwaddr=...,ip=192.168.0.4/24,type=veth + const ipMatch = netConfig.match(/ip=([^,]+)/) + if (ipMatch && ipMatch[1]) { + const ip = ipMatch[1] + // Check if it's DHCP + if (ip.toLowerCase() === "dhcp") { + return "DHCP" + } + // Return the IP without the subnet mask for cleaner display + return ip.split("/")[0] + } + + return "N/A" +} + export function VirtualMachines() { const { data: vmData, @@ -396,7 +414,7 @@ export function VirtualMachines() {
-
+
CPU Usage
{cpuPercent}%
@@ -624,16 +642,31 @@ export function VirtualMachines() {
{Object.keys(vmDetails.config) .filter((key) => key.match(/^net\d+$/)) - .map((netKey) => ( -
-
- Network Interface {netKey.replace("net", "")} + .map((netKey) => { + const netConfig = vmDetails.config[netKey] + const ipAddress = selectedVM?.type === "lxc" ? extractIPFromNetConfig(netConfig) : null + + return ( +
+
+ Network Interface {netKey.replace("net", "")} +
+ {ipAddress && ( +
+ IP Address: + + {ipAddress} + +
+ )} +
+ {netConfig} +
-
- {vmDetails.config[netKey]} -
-
- ))} + ) + })} {vmDetails.config.nameserver && (
DNS Nameserver