From a4a455f31e848bd91f8aa33a5b051ddbf8df47a2 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Sun, 26 Oct 2025 11:41:18 +0100 Subject: [PATCH] Update network-metrics.tsx --- AppImage/components/network-metrics.tsx | 757 +++++++++++++----------- 1 file changed, 396 insertions(+), 361 deletions(-) diff --git a/AppImage/components/network-metrics.tsx b/AppImage/components/network-metrics.tsx index ac9bf9f..c3af53c 100644 --- a/AppImage/components/network-metrics.tsx +++ b/AppImage/components/network-metrics.tsx @@ -157,6 +157,12 @@ export function NetworkMetrics() { const [networkTotals, setNetworkTotals] = useState<{ received: number; sent: number }>({ received: 0, sent: 0 }) const [interfaceTotals, setInterfaceTotals] = useState<{ received: number; sent: number }>({ received: 0, sent: 0 }) + const { data: modalNetworkData } = useSWR(selectedInterface ? "/api/network" : null, fetcher, { + refreshInterval: 15000, // Refresh every 15 seconds when modal is open + revalidateOnFocus: false, + revalidateOnReconnect: true, + }) + const { data: interfaceHistoricalData } = useSWR(`/api/node/metrics?timeframe=${timeframe}`, fetcher, { refreshInterval: 30000, revalidateOnFocus: false, @@ -220,7 +226,11 @@ export function NetworkMetrics() { ...(networkData.vm_lxc_interfaces || []), ] - const vmLxcInterfaces = networkData.vm_lxc_interfaces || [] + const vmLxcInterfaces = (networkData.vm_lxc_interfaces || []).sort((a, b) => { + const vmidA = a.vmid ?? Number.MAX_SAFE_INTEGER + const vmidB = b.vmid ?? Number.MAX_SAFE_INTEGER + return vmidA - vmidB + }) const topInterface = vmLxcInterfaces.length > 0 @@ -586,7 +596,7 @@ export function NetworkMetrics() {
- {networkData.vm_lxc_interfaces.map((interface_, index) => { + {vmLxcInterfaces.map((interface_, index) => { const vmTypeBadge = getVMTypeBadge(interface_.vm_type) return ( @@ -688,374 +698,399 @@ export function NetworkMetrics() { {selectedInterface && (
- {/* Basic Information */} -
-

Basic Information

-
-
-
Interface Name
-
{selectedInterface.name}
-
-
-
Type
- - {getInterfaceTypeBadge(selectedInterface.type).label} - -
- {selectedInterface.type === "bridge" && selectedInterface.bridge_physical_interface && ( -
-
Physical Interface
-
- {selectedInterface.bridge_physical_interface} -
- {selectedInterface.bridge_physical_interface.startsWith("bond") && - networkData?.physical_interfaces && ( - <> - {(() => { - const bondInterface = networkData.physical_interfaces.find( - (iface) => iface.name === selectedInterface.bridge_physical_interface, - ) - if (bondInterface?.bond_slaves && bondInterface.bond_slaves.length > 0) { - return ( -
-
Bond Members
-
- {bondInterface.bond_slaves.map((slave, idx) => ( - - {slave} - - ))} -
-
- ) - } - return null - })()} - + {(() => { + // Find the current interface data from modalNetworkData if available + const currentInterfaceData = modalNetworkData + ? [ + ...(modalNetworkData.physical_interfaces || []), + ...(modalNetworkData.bridge_interfaces || []), + ...(modalNetworkData.vm_lxc_interfaces || []), + ].find((iface) => iface.name === selectedInterface.name) + : selectedInterface + + const displayInterface = currentInterfaceData || selectedInterface + + return ( + <> + {/* Basic Information */} +
+

Basic Information

+
+
+
Interface Name
+
{displayInterface.name}
+
+
+
Type
+ + {getInterfaceTypeBadge(displayInterface.type).label} + +
+ {displayInterface.type === "bridge" && displayInterface.bridge_physical_interface && ( +
+
Physical Interface
+
+ {displayInterface.bridge_physical_interface} +
+ {displayInterface.bridge_physical_interface.startsWith("bond") && + modalNetworkData?.physical_interfaces && ( + <> + {(() => { + const bondInterface = modalNetworkData.physical_interfaces.find( + (iface) => iface.name === displayInterface.bridge_physical_interface, + ) + if (bondInterface?.bond_slaves && bondInterface.bond_slaves.length > 0) { + return ( +
+
Bond Members
+
+ {bondInterface.bond_slaves.map((slave, idx) => ( + + {slave} + + ))} +
+
+ ) + } + return null + })()} + + )} + {displayInterface.bridge_bond_slaves && displayInterface.bridge_bond_slaves.length > 0 && ( +
+
Bond Members
+
+ {displayInterface.bridge_bond_slaves.map((slave, idx) => ( + + {slave} + + ))} +
+
+ )} +
)} - {selectedInterface.bridge_bond_slaves && selectedInterface.bridge_bond_slaves.length > 0 && ( -
-
Bond Members
-
- {selectedInterface.bridge_bond_slaves.map((slave, idx) => ( - - {slave} - - ))} + {displayInterface.type === "vm_lxc" && displayInterface.vm_name && ( +
+
VM/LXC Name
+
+ {displayInterface.vm_name} + {displayInterface.vm_type && ( + + {getVMTypeBadge(displayInterface.vm_type).label} + + )} +
+
+ )} +
+
Status
+ + {displayInterface.status.toUpperCase()} + +
+
+
Speed
+
{formatSpeed(displayInterface.speed)}
+
+
+
Duplex
+
{displayInterface.duplex}
+
+
+
MTU
+
{displayInterface.mtu}
+
+ {displayInterface.mac_address && ( +
+
MAC Address
+
{displayInterface.mac_address}
+
+ )} +
+
+ + {/* IP Addresses */} + {displayInterface.addresses.length > 0 && ( +
+

IP Addresses

+
+ {displayInterface.addresses.map((addr, idx) => ( +
+
+
{addr.ip}
+
Netmask: {addr.netmask}
+
+
+ ))} +
+
+ )} + + {/* Network Traffic Statistics - Only show if interface is UP and NOT a VM interface */} + {displayInterface.status.toLowerCase() === "up" && displayInterface.vm_type !== "vm" ? ( +
+

+ Network Traffic Statistics ( + {modalTimeframe === "hour" + ? "Last Hour" + : modalTimeframe === "day" + ? "Last 24 Hours" + : modalTimeframe === "week" + ? "Last 7 Days" + : modalTimeframe === "month" + ? "Last 30 Days" + : "Last Year"} + ) +

+
+ {/* Traffic Data - Top Row */} +
+
+
Bytes Received
+
+ {formatStorage(interfaceTotals.received * 1024 * 1024 * 1024)} +
+
+
+
Bytes Sent
+
+ {formatStorage(interfaceTotals.sent * 1024 * 1024 * 1024)} +
+
+
+ + {/* Network Traffic Chart - Full Width Below */} +
+
- )} -
- )} - {selectedInterface.type === "vm_lxc" && selectedInterface.vm_name && ( -
-
VM/LXC Name
-
- {selectedInterface.vm_name} - {selectedInterface.vm_type && ( - - {getVMTypeBadge(selectedInterface.vm_type).label} - - )}
-
- )} -
-
Status
- - {selectedInterface.status.toUpperCase()} - -
-
-
Speed
-
{formatSpeed(selectedInterface.speed)}
-
-
-
Duplex
-
{selectedInterface.duplex}
-
-
-
MTU
-
{selectedInterface.mtu}
-
- {selectedInterface.mac_address && ( -
-
MAC Address
-
{selectedInterface.mac_address}
-
- )} -
-
+ ) : displayInterface.status.toLowerCase() === "up" && displayInterface.vm_type === "vm" ? ( +
+

Traffic since last boot

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

Interface Inactive

+

+ This interface is currently down. Network traffic statistics are not available. +

+
+ )} - {/* IP Addresses */} - {selectedInterface.addresses.length > 0 && ( -
-

IP Addresses

-
- {selectedInterface.addresses.map((addr, idx) => ( -
+ {/* Cumulative Statistics - Only show if interface is UP and NOT a VM interface */} + {displayInterface.status.toLowerCase() === "up" && displayInterface.vm_type !== "vm" && ( +
+

+ Cumulative Statistics (Since Last Boot) +

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

Bond Configuration

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

Bridge Configuration

-
{addr.ip}
-
Netmask: {addr.netmask}
-
-
- ))} -
-
- )} - - {/* Network Traffic Statistics - Only show if interface is UP and NOT a VM interface */} - {selectedInterface.status.toLowerCase() === "up" && selectedInterface.vm_type !== "vm" ? ( -
-

- Network Traffic Statistics ( - {modalTimeframe === "hour" - ? "Last Hour" - : modalTimeframe === "day" - ? "Last 24 Hours" - : modalTimeframe === "week" - ? "Last 7 Days" - : modalTimeframe === "month" - ? "Last 30 Days" - : "Last Year"} - ) -

-
- {/* Traffic Data - Top Row */} -
-
-
Bytes Received
-
- {formatStorage(interfaceTotals.received * 1024 * 1024 * 1024)} -
-
-
-
Bytes Sent
-
- {formatStorage(interfaceTotals.sent * 1024 * 1024 * 1024)} -
-
-
- - {/* Network Traffic Chart - Full Width Below */} -
- -
-
-
- ) : selectedInterface.status.toLowerCase() === "up" && selectedInterface.vm_type === "vm" ? ( -
-

Traffic since last boot

-
-
-
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}% +
Virtual Member Interfaces
+
+ {displayInterface.bridge_members.length > 0 ? ( + displayInterface.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
+ )} +
)} - {selectedInterface.packet_loss_out !== undefined && ( -
-
Packet Loss Out
-
1 ? "text-red-500" : "text-green-500"}`} - > - {selectedInterface.packet_loss_out}% -
-
- )} -
-
- ) : ( -
- -

Interface Inactive

-

- This interface is currently down. Network traffic statistics are not available. -

-
- )} - - {/* Cumulative Statistics - Only show if interface is UP and NOT a VM interface */} - {selectedInterface.status.toLowerCase() === "up" && selectedInterface.vm_type !== "vm" && ( -
-

- Cumulative Statistics (Since Last Boot) -

-
-
-
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

-
-
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
- )} -
-
-
- )} + + ) + })()}
)}