diff --git a/AppImage/components/network-metrics.tsx b/AppImage/components/network-metrics.tsx index 4169270..cc153ea 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)}> @@ -562,6 +532,51 @@ export function NetworkMetrics() {
{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 Slave Interfaces
+
+ {bondInterface.bond_slaves.map((slave, idx) => ( + + {slave} + + ))} +
+
+ ) + } + return null + })()} + + )} + {selectedInterface.bridge_bond_slaves && selectedInterface.bridge_bond_slaves.length > 0 && ( +
+
Bond Slave Interfaces
+
+ {selectedInterface.bridge_bond_slaves.map((slave, idx) => ( + + {slave} + + ))} +
+
+ )}
)} {selectedInterface.type === "vm_lxc" && selectedInterface.vm_name && ( diff --git a/AppImage/components/virtual-machines.tsx b/AppImage/components/virtual-machines.tsx index 75abecd..ac5c561 100644 --- a/AppImage/components/virtual-machines.tsx +++ b/AppImage/components/virtual-machines.tsx @@ -19,6 +19,7 @@ import { RotateCcw, StopCircle, AlertTriangle, + CheckCircle, } from "lucide-react" import useSWR from "swr" @@ -37,6 +38,7 @@ interface VMData { netout?: number diskread?: number diskwrite?: number + ipaddress?: string } interface VMConfig { @@ -251,6 +253,32 @@ export function VirtualMachines() { return false }, [totalAllocatedMemoryGB, physicalMemoryGB]) + const getMemoryOvercommitBadge = () => { + if (!physicalMemoryGB) return null + + const allocated = Number.parseFloat(totalAllocatedMemoryGB) + const physical = Number.parseFloat(physicalMemoryGB) + const overcommitPercent = ((allocated / physical) * 100).toFixed(0) + + if (allocated > physical) { + return { + color: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20", + icon: , + label: "Overcommit", + message: `${overcommitPercent}% de memoria física`, + } + } + + return { + color: "bg-green-500/10 text-green-500 border-green-500/20", + icon: , + label: "Normal", + message: `${overcommitPercent}% de memoria física`, + } + } + + const memoryBadge = getMemoryOvercommitBadge() + if (isLoading) { return (
@@ -306,22 +334,20 @@ export function VirtualMachines() { Total Memory -
- {isMemoryOvercommit && } - -
+
{totalAllocatedMemoryGB} GB
- {isMemoryOvercommit ? ( -

- - Overcommit: Excede memoria física ({physicalMemoryGB} GB) -

- ) : ( -

Allocated RAM

+ {memoryBadge && ( +
+ + {memoryBadge.icon} + {memoryBadge.label} + +

{memoryBadge.message}

+
)}
@@ -396,6 +422,13 @@ export function VirtualMachines() {
+ {vm.type === "lxc" && vm.ipaddress && ( +
+
IP Address
+
{vm.ipaddress}
+
+ )} +
CPU Usage
@@ -496,6 +529,12 @@ export function VirtualMachines() {
VMID
{selectedVM.vmid}
+ {selectedVM.type === "lxc" && selectedVM.ipaddress && ( +
+
IP Address
+
{selectedVM.ipaddress}
+
+ )}
CPU Usage
Uptime
-
{formatUptime(selectedVM.uptime)}
+
{formatUptime(selectedVM.uptime)}
Disk I/O