mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-10-11 12:26:18 +00:00
Update AppImage
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
|||||||
FanIcon,
|
FanIcon,
|
||||||
PowerIcon,
|
PowerIcon,
|
||||||
Battery,
|
Battery,
|
||||||
|
Cpu,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import useSWR from "swr"
|
import useSWR from "swr"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
@@ -45,6 +46,115 @@ export default function Hardware() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 p-6">
|
<div className="space-y-6 p-6">
|
||||||
|
{/* CPU & Motherboard Info */}
|
||||||
|
{(hardwareData?.cpu || hardwareData?.motherboard) && (
|
||||||
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
|
<div className="mb-4 flex items-center gap-2">
|
||||||
|
<Cpu className="h-5 w-5 text-primary" />
|
||||||
|
<h2 className="text-lg font-semibold">System Information</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-6 md:grid-cols-2">
|
||||||
|
{/* CPU Info */}
|
||||||
|
{hardwareData?.cpu && Object.keys(hardwareData.cpu).length > 0 && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<h3 className="text-sm font-semibold text-muted-foreground">Processor</h3>
|
||||||
|
<div className="space-y-2 rounded-lg border border-border/30 bg-background/50 p-4">
|
||||||
|
{hardwareData.cpu.model && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Model</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.cpu.model}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.cpu.sockets && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Sockets</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.cpu.sockets}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.cpu.cores_per_socket && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Cores per Socket</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.cpu.cores_per_socket}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.cpu.total_threads && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Total Threads</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.cpu.total_threads}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.cpu.current_mhz && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Current Speed</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.cpu.current_mhz.toFixed(0)} MHz</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.cpu.max_mhz && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Max Speed</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.cpu.max_mhz.toFixed(0)} MHz</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Motherboard Info */}
|
||||||
|
{hardwareData?.motherboard && Object.keys(hardwareData.motherboard).length > 0 && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<h3 className="text-sm font-semibold text-muted-foreground">Motherboard</h3>
|
||||||
|
<div className="space-y-2 rounded-lg border border-border/30 bg-background/50 p-4">
|
||||||
|
{hardwareData.motherboard.manufacturer && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Manufacturer</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.motherboard.manufacturer}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.motherboard.model && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Model</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.motherboard.model}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.motherboard.version && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Version</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.motherboard.version}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.motherboard.bios && (
|
||||||
|
<>
|
||||||
|
<div className="mt-3 border-t border-border/30 pt-2">
|
||||||
|
<span className="text-xs font-semibold text-muted-foreground">BIOS</span>
|
||||||
|
</div>
|
||||||
|
{hardwareData.motherboard.bios.vendor && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Vendor</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.motherboard.bios.vendor}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.motherboard.bios.version && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Version</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.motherboard.bios.version}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{hardwareData.motherboard.bios.date && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Date</span>
|
||||||
|
<span className="text-sm font-medium">{hardwareData.motherboard.bios.date}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Thermal Monitoring */}
|
{/* Thermal Monitoring */}
|
||||||
{hardwareData?.temperatures && hardwareData.temperatures.length > 0 && (
|
{hardwareData?.temperatures && hardwareData.temperatures.length > 0 && (
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
@@ -81,87 +191,6 @@ export default function Hardware() {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hardwareData?.power_meter && (
|
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
|
||||||
<div className="mb-4 flex items-center gap-2">
|
|
||||||
<Zap className="h-5 w-5 text-yellow-500" />
|
|
||||||
<h2 className="text-lg font-semibold">Power Consumption</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-center justify-between rounded-lg border border-border/30 bg-background/50 p-4">
|
|
||||||
<div className="space-y-1">
|
|
||||||
<p className="text-sm font-medium">{hardwareData.power_meter.name}</p>
|
|
||||||
{hardwareData.power_meter.adapter && (
|
|
||||||
<p className="text-xs text-muted-foreground">{hardwareData.power_meter.adapter}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="text-right">
|
|
||||||
<p className="text-2xl font-bold text-yellow-500">{hardwareData.power_meter.watts.toFixed(1)} W</p>
|
|
||||||
<p className="text-xs text-muted-foreground">Current Draw</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Storage Summary */}
|
|
||||||
{hardwareData?.storage_devices && hardwareData.storage_devices.length > 0 && (
|
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
|
||||||
<div className="mb-4 flex items-center gap-2">
|
|
||||||
<HardDrive className="h-5 w-5 text-primary" />
|
|
||||||
<h2 className="text-lg font-semibold">Storage Summary</h2>
|
|
||||||
<Badge variant="outline" className="ml-auto">
|
|
||||||
{hardwareData.storage_devices.length} devices
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
|
||||||
{hardwareData.storage_devices.map((device, index) => (
|
|
||||||
<div key={index} className="rounded-lg border border-border/30 bg-background/50 p-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium">{device.name}</span>
|
|
||||||
<Badge className="bg-orange-500/10 text-orange-500 border-orange-500/20">{device.type}</Badge>
|
|
||||||
</div>
|
|
||||||
{device.size && <p className="mt-1 text-xs text-muted-foreground">{device.size}</p>}
|
|
||||||
{device.model && <p className="mt-1 text-xs text-muted-foreground">{device.model}</p>}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Network Summary */}
|
|
||||||
{hardwareData?.pci_devices &&
|
|
||||||
hardwareData.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && (
|
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
|
||||||
<div className="mb-4 flex items-center gap-2">
|
|
||||||
<Network className="h-5 w-5 text-primary" />
|
|
||||||
<h2 className="text-lg font-semibold">Network Summary</h2>
|
|
||||||
<Badge variant="outline" className="ml-auto">
|
|
||||||
{hardwareData.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length} interfaces
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
|
||||||
{hardwareData.pci_devices
|
|
||||||
.filter((d) => d.type.toLowerCase().includes("network"))
|
|
||||||
.map((device, index) => (
|
|
||||||
<div key={index} className="rounded-lg border border-border/30 bg-background/50 p-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm font-medium">{device.device}</span>
|
|
||||||
<Badge className="bg-blue-500/10 text-blue-500 border-blue-500/20">Ethernet</Badge>
|
|
||||||
</div>
|
|
||||||
<p className="mt-1 text-xs text-muted-foreground">{device.vendor}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<p className="mt-4 text-xs text-muted-foreground">
|
|
||||||
For detailed network information, see the Network section
|
|
||||||
</p>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* PCI Devices */}
|
{/* PCI Devices */}
|
||||||
{hardwareData?.pci_devices && hardwareData.pci_devices.length > 0 && (
|
{hardwareData?.pci_devices && hardwareData.pci_devices.length > 0 && (
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
@@ -229,7 +258,7 @@ export default function Hardware() {
|
|||||||
{device.driver && (
|
{device.driver && (
|
||||||
<div className="flex justify-between border-b border-border/50 pb-2">
|
<div className="flex justify-between border-b border-border/50 pb-2">
|
||||||
<span className="text-sm font-medium text-muted-foreground">Driver</span>
|
<span className="text-sm font-medium text-muted-foreground">Driver</span>
|
||||||
<span className="font-mono text-sm">{device.driver}</span>
|
<span className="font-mono text-sm text-green-500">{device.driver}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -239,40 +268,6 @@ export default function Hardware() {
|
|||||||
<span className="font-mono text-sm">{device.kernel_module}</span>
|
<span className="font-mono text-sm">{device.kernel_module}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{device.irq && (
|
|
||||||
<div className="flex justify-between border-b border-border/50 pb-2">
|
|
||||||
<span className="text-sm font-medium text-muted-foreground">IRQ</span>
|
|
||||||
<span className="font-mono text-sm">{device.irq}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{device.memory_address && (
|
|
||||||
<div className="flex justify-between border-b border-border/50 pb-2">
|
|
||||||
<span className="text-sm font-medium text-muted-foreground">Memory Address</span>
|
|
||||||
<span className="font-mono text-sm">{device.memory_address}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{device.link_speed && (
|
|
||||||
<div className="flex justify-between border-b border-border/50 pb-2">
|
|
||||||
<span className="text-sm font-medium text-muted-foreground">Link Speed</span>
|
|
||||||
<span className="font-mono text-sm">{device.link_speed}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{device.capabilities && device.capabilities.length > 0 && (
|
|
||||||
<div className="space-y-2 border-b border-border/50 pb-2">
|
|
||||||
<span className="text-sm font-medium text-muted-foreground">Capabilities</span>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{device.capabilities.map((cap, idx) => (
|
|
||||||
<Badge key={idx} variant="secondary" className="text-xs">
|
|
||||||
{cap}
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -282,6 +277,31 @@ export default function Hardware() {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Power Consumption */}
|
||||||
|
{hardwareData?.power_meter && (
|
||||||
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
|
<div className="mb-4 flex items-center gap-2">
|
||||||
|
<Zap className="h-5 w-5 text-yellow-500" />
|
||||||
|
<h2 className="text-lg font-semibold">Power Consumption</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between rounded-lg border border-border/30 bg-background/50 p-4">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-sm font-medium">{hardwareData.power_meter.name}</p>
|
||||||
|
{hardwareData.power_meter.adapter && (
|
||||||
|
<p className="text-xs text-muted-foreground">{hardwareData.power_meter.adapter}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<p className="text-2xl font-bold text-yellow-500">{hardwareData.power_meter.watts.toFixed(1)} W</p>
|
||||||
|
<p className="text-xs text-muted-foreground">Current Draw</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Fans */}
|
{/* Fans */}
|
||||||
{hardwareData?.fans && hardwareData.fans.length > 0 && (
|
{hardwareData?.fans && hardwareData.fans.length > 0 && (
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
@@ -294,17 +314,22 @@ export default function Hardware() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
{hardwareData.fans.map((fan, index) => (
|
{hardwareData.fans.map((fan, index) => {
|
||||||
<div key={index} className="space-y-2">
|
const maxRPM = 5000
|
||||||
<div className="flex items-center justify-between">
|
const percentage = Math.min((fan.speed / maxRPM) * 100, 100)
|
||||||
<span className="text-sm font-medium">{fan.name}</span>
|
|
||||||
<span className="text-sm font-semibold text-blue-500">
|
return (
|
||||||
{fan.speed.toFixed(1)} {fan.unit}
|
<div key={index} className="space-y-2">
|
||||||
</span>
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm font-medium">{fan.name}</span>
|
||||||
|
<span className="text-sm font-semibold text-blue-500">
|
||||||
|
{fan.speed.toFixed(0)} {fan.unit}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Progress value={percentage} className="h-2" />
|
||||||
</div>
|
</div>
|
||||||
<Progress value={fan.speed} className="h-2" />
|
)
|
||||||
</div>
|
})}
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
@@ -325,7 +350,9 @@ export default function Hardware() {
|
|||||||
<div key={index} className="rounded-lg border border-border/30 bg-background/50 p-4">
|
<div key={index} className="rounded-lg border border-border/30 bg-background/50 p-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm font-medium">{psu.name}</span>
|
<span className="text-sm font-medium">{psu.name}</span>
|
||||||
{psu.status && <Badge variant={psu.status === "OK" ? "default" : "destructive"}>{psu.status}</Badge>}
|
{psu.status && (
|
||||||
|
<Badge variant={psu.status.toLowerCase() === "ok" ? "default" : "destructive"}>{psu.status}</Badge>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-2 text-2xl font-bold text-primary">{psu.watts} W</p>
|
<p className="mt-2 text-2xl font-bold text-primary">{psu.watts} W</p>
|
||||||
<p className="text-xs text-muted-foreground">Current Output</p>
|
<p className="text-xs text-muted-foreground">Current Output</p>
|
||||||
@@ -335,8 +362,8 @@ export default function Hardware() {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* UPS */}
|
{/* UPS - Solo mostrar si hay datos */}
|
||||||
{hardwareData?.ups && (
|
{hardwareData?.ups && Object.keys(hardwareData.ups).length > 0 && (
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
<div className="mb-4 flex items-center gap-2">
|
<div className="mb-4 flex items-center gap-2">
|
||||||
<Battery className="h-5 w-5 text-primary" />
|
<Battery className="h-5 w-5 text-primary" />
|
||||||
@@ -346,51 +373,47 @@ export default function Hardware() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="rounded-lg border border-border/30 bg-background/50 p-4">
|
<div className="rounded-lg border border-border/30 bg-background/50 p-4">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<span className="text-sm font-medium">{hardwareData.ups.name}</span>
|
<span className="text-sm font-medium">{hardwareData.ups.model || "UPS"}</span>
|
||||||
<Badge variant={hardwareData.ups.status === "OL" ? "default" : "destructive"}>
|
<Badge variant={hardwareData.ups.status === "OL" ? "default" : "destructive"}>
|
||||||
{hardwareData.ups.status}
|
{hardwareData.ups.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-3 md:grid-cols-2">
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
{hardwareData.ups.battery_charge !== undefined && (
|
{hardwareData.ups.battery_charge && (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-xs text-muted-foreground">Battery Charge</span>
|
<span className="text-xs text-muted-foreground">Battery Charge</span>
|
||||||
<span className="text-sm font-semibold">{hardwareData.ups.battery_charge}%</span>
|
<span className="text-sm font-semibold">{hardwareData.ups.battery_charge}</span>
|
||||||
</div>
|
</div>
|
||||||
<Progress value={hardwareData.ups.battery_charge} className="h-2" />
|
<Progress
|
||||||
|
value={Number.parseInt(hardwareData.ups.battery_charge.replace("%", ""))}
|
||||||
|
className="h-2"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hardwareData.ups.load !== undefined && (
|
{hardwareData.ups.load_percent && (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-xs text-muted-foreground">Load</span>
|
<span className="text-xs text-muted-foreground">Load</span>
|
||||||
<span className="text-sm font-semibold">{hardwareData.ups.load}%</span>
|
<span className="text-sm font-semibold">{hardwareData.ups.load_percent}</span>
|
||||||
</div>
|
</div>
|
||||||
<Progress value={hardwareData.ups.load} className="h-2" />
|
<Progress value={Number.parseInt(hardwareData.ups.load_percent.replace("%", ""))} className="h-2" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hardwareData.ups.battery_runtime !== undefined && (
|
{hardwareData.ups.time_left && (
|
||||||
<div>
|
<div>
|
||||||
<span className="text-xs text-muted-foreground">Runtime</span>
|
<span className="text-xs text-muted-foreground">Runtime</span>
|
||||||
<p className="text-sm font-semibold">{Math.floor(hardwareData.ups.battery_runtime / 60)} min</p>
|
<p className="text-sm font-semibold">{hardwareData.ups.time_left}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hardwareData.ups.input_voltage !== undefined && (
|
{hardwareData.ups.line_voltage && (
|
||||||
<div>
|
<div>
|
||||||
<span className="text-xs text-muted-foreground">Input Voltage</span>
|
<span className="text-xs text-muted-foreground">Input Voltage</span>
|
||||||
<p className="text-sm font-semibold">{hardwareData.ups.input_voltage} V</p>
|
<p className="text-sm font-semibold">{hardwareData.ups.line_voltage}</p>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{hardwareData.ups.output_voltage !== undefined && (
|
|
||||||
<div>
|
|
||||||
<span className="text-xs text-muted-foreground">Output Voltage</span>
|
|
||||||
<p className="text-sm font-semibold">{hardwareData.ups.output_voltage} V</p>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -398,6 +421,65 @@ export default function Hardware() {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Network Summary */}
|
||||||
|
{hardwareData?.pci_devices &&
|
||||||
|
hardwareData.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && (
|
||||||
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
|
<div className="mb-4 flex items-center gap-2">
|
||||||
|
<Network className="h-5 w-5 text-primary" />
|
||||||
|
<h2 className="text-lg font-semibold">Network Summary</h2>
|
||||||
|
<Badge variant="outline" className="ml-auto">
|
||||||
|
{hardwareData.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length} interfaces
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{hardwareData.pci_devices
|
||||||
|
.filter((d) => d.type.toLowerCase().includes("network"))
|
||||||
|
.map((device, index) => (
|
||||||
|
<div key={index} className="rounded-lg border border-border/30 bg-background/50 p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm font-medium">{device.device}</span>
|
||||||
|
<Badge className="bg-blue-500/10 text-blue-500 border-blue-500/20">Ethernet</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="mt-1 text-xs text-muted-foreground">{device.vendor}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className="mt-4 text-xs text-muted-foreground">
|
||||||
|
For detailed network information, see the Network section
|
||||||
|
</p>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Storage Summary */}
|
||||||
|
{hardwareData?.storage_devices && hardwareData.storage_devices.length > 0 && (
|
||||||
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
|
<div className="mb-4 flex items-center gap-2">
|
||||||
|
<HardDrive className="h-5 w-5 text-primary" />
|
||||||
|
<h2 className="text-lg font-semibold">Storage Summary</h2>
|
||||||
|
<Badge variant="outline" className="ml-auto">
|
||||||
|
{hardwareData.storage_devices.length} devices
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{hardwareData.storage_devices.map((device, index) => (
|
||||||
|
<div key={index} className="rounded-lg border border-border/30 bg-background/50 p-3">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm font-medium">{device.name}</span>
|
||||||
|
<Badge className="bg-blue-500/10 text-blue-500 border-blue-500/20 text-xs px-2 py-1">
|
||||||
|
{device.type}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
{device.size && <p className="mt-1 text-xs text-muted-foreground">{device.size}</p>}
|
||||||
|
{device.model && <p className="mt-1 text-xs text-muted-foreground">{device.model}</p>}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -351,17 +351,6 @@ download_deb() {
|
|||||||
# Try to download packages (non-fatal if they fail)
|
# Try to download packages (non-fatal if they fail)
|
||||||
download_deb "http://deb.debian.org/debian/pool/main/i/ipmitool/ipmitool_1.8.19-4+deb12u2_amd64.deb" "ipmitool.deb" "ipmitool" || true
|
download_deb "http://deb.debian.org/debian/pool/main/i/ipmitool/ipmitool_1.8.19-4+deb12u2_amd64.deb" "ipmitool.deb" "ipmitool" || true
|
||||||
download_deb "http://deb.debian.org/debian/pool/main/f/freeipmi/libfreeipmi17_1.6.10-3_amd64.deb" "libfreeipmi17.deb" "libfreeipmi17" || true
|
download_deb "http://deb.debian.org/debian/pool/main/f/freeipmi/libfreeipmi17_1.6.10-3_amd64.deb" "libfreeipmi17.deb" "libfreeipmi17" || true
|
||||||
|
|
||||||
if [ ! -f "libfreeipmi17.deb" ]; then
|
|
||||||
echo " Fallback: apt-get download libfreeipmi17"
|
|
||||||
apt-get update -qq || true
|
|
||||||
apt-get download -y libfreeipmi17 || true
|
|
||||||
# renombra al nombre esperado si hace falta
|
|
||||||
for f in libfreeipmi17_*_amd64.deb; do
|
|
||||||
[ -f "$f" ] && mv "$f" libfreeipmi17.deb
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
download_deb "http://deb.debian.org/debian/pool/main/l/lm-sensors/lm-sensors_3.6.0-7.1_amd64.deb" "lm-sensors.deb" "lm-sensors" || true
|
download_deb "http://deb.debian.org/debian/pool/main/l/lm-sensors/lm-sensors_3.6.0-7.1_amd64.deb" "lm-sensors.deb" "lm-sensors" || true
|
||||||
download_deb "http://deb.debian.org/debian/pool/main/n/nut/nut-client_2.8.0-7_amd64.deb" "nut-client.deb" "nut-client" || true
|
download_deb "http://deb.debian.org/debian/pool/main/n/nut/nut-client_2.8.0-7_amd64.deb" "nut-client.deb" "nut-client" || true
|
||||||
download_deb "http://deb.debian.org/debian/pool/main/n/nut/libupsclient6_2.8.0-7_amd64.deb" "libupsclient6.deb" "libupsclient6" || true
|
download_deb "http://deb.debian.org/debian/pool/main/n/nut/libupsclient6_2.8.0-7_amd64.deb" "libupsclient6.deb" "libupsclient6" || true
|
||||||
|
@@ -1708,7 +1708,8 @@ def get_hardware_info():
|
|||||||
print(f"[v0] Error getting graphics cards: {e}")
|
print(f"[v0] Error getting graphics cards: {e}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("[v0] Getting all PCI devices...")
|
print("[v0] Getting PCI devices with driver information...")
|
||||||
|
# First get basic device info with lspci -vmm
|
||||||
result = subprocess.run(['lspci', '-vmm'], capture_output=True, text=True, timeout=10)
|
result = subprocess.run(['lspci', '-vmm'], capture_output=True, text=True, timeout=10)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
current_device = {}
|
current_device = {}
|
||||||
@@ -1721,6 +1722,7 @@ def get_hardware_info():
|
|||||||
device_class = current_device.get('Class', '')
|
device_class = current_device.get('Class', '')
|
||||||
device_name = current_device.get('Device', '')
|
device_name = current_device.get('Device', '')
|
||||||
vendor = current_device.get('Vendor', '')
|
vendor = current_device.get('Vendor', '')
|
||||||
|
slot = current_device.get('Slot', 'Unknown')
|
||||||
|
|
||||||
# Categorize and add important devices
|
# Categorize and add important devices
|
||||||
device_type = 'Other'
|
device_type = 'Other'
|
||||||
@@ -1730,88 +1732,79 @@ def get_hardware_info():
|
|||||||
if any(keyword in device_class for keyword in ['VGA', 'Display', '3D']):
|
if any(keyword in device_class for keyword in ['VGA', 'Display', '3D']):
|
||||||
device_type = 'Graphics Card'
|
device_type = 'Graphics Card'
|
||||||
include_device = True
|
include_device = True
|
||||||
# Also add to graphics_cards list
|
|
||||||
gpu_vendor = 'Unknown'
|
|
||||||
if 'NVIDIA' in vendor or 'NVIDIA' in device_name:
|
|
||||||
gpu_vendor = 'NVIDIA'
|
|
||||||
elif 'AMD' in vendor or 'AMD' in device_name or 'ATI' in vendor:
|
|
||||||
gpu_vendor = 'AMD'
|
|
||||||
elif 'Intel' in vendor or 'Intel' in device_name:
|
|
||||||
gpu_vendor = 'Intel'
|
|
||||||
|
|
||||||
# Check if not already in graphics_cards
|
|
||||||
already_exists = False
|
|
||||||
for existing_gpu in hardware_data['graphics_cards']:
|
|
||||||
if device_name in existing_gpu['name'] or existing_gpu['name'] in device_name:
|
|
||||||
already_exists = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not already_exists:
|
|
||||||
hardware_data['graphics_cards'].append({
|
|
||||||
'name': device_name,
|
|
||||||
'vendor': gpu_vendor
|
|
||||||
})
|
|
||||||
|
|
||||||
# Storage controllers
|
# Storage controllers
|
||||||
elif any(keyword in device_class for keyword in ['SATA', 'RAID', 'Mass storage', 'Non-Volatile memory']):
|
elif any(keyword in device_class for keyword in ['SATA', 'RAID', 'Mass storage', 'Non-Volatile memory']):
|
||||||
device_type = 'Storage Controller'
|
device_type = 'Storage Controller'
|
||||||
include_device = True
|
include_device = True
|
||||||
|
|
||||||
# Network controllers
|
# Network controllers
|
||||||
elif 'Ethernet' in device_class or 'Network' in device_class:
|
elif 'Ethernet' in device_class or 'Network' in device_class:
|
||||||
device_type = 'Network Controller'
|
device_type = 'Network Controller'
|
||||||
include_device = True
|
include_device = True
|
||||||
# Also add to network_cards if not already there
|
|
||||||
already_exists = False
|
|
||||||
for existing_nic in hardware_data['network_cards']:
|
|
||||||
if device_name in existing_nic['name'] or existing_nic['name'] in device_name:
|
|
||||||
already_exists = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not already_exists:
|
|
||||||
hardware_data['network_cards'].append({
|
|
||||||
'name': device_name,
|
|
||||||
'type': 'Ethernet' if 'Ethernet' in device_class else 'Network'
|
|
||||||
})
|
|
||||||
|
|
||||||
# USB controllers
|
# USB controllers
|
||||||
elif 'USB' in device_class:
|
elif 'USB' in device_class:
|
||||||
device_type = 'USB Controller'
|
device_type = 'USB Controller'
|
||||||
include_device = True
|
include_device = True
|
||||||
|
|
||||||
# Audio devices
|
# Audio devices
|
||||||
elif 'Audio' in device_class or 'Multimedia' in device_class:
|
elif 'Audio' in device_class or 'Multimedia' in device_class:
|
||||||
device_type = 'Audio Controller'
|
device_type = 'Audio Controller'
|
||||||
include_device = True
|
include_device = True
|
||||||
|
|
||||||
# Special devices (Coral TPU, etc.)
|
# Special devices (Coral TPU, etc.)
|
||||||
elif any(keyword in device_name.lower() for keyword in ['coral', 'tpu', 'edge']):
|
elif any(keyword in device_name.lower() for keyword in ['coral', 'tpu', 'edge']):
|
||||||
device_type = 'AI Accelerator'
|
device_type = 'AI Accelerator'
|
||||||
include_device = True
|
include_device = True
|
||||||
|
|
||||||
# PCI bridges (usually not interesting for users)
|
# PCI bridges (usually not interesting for users)
|
||||||
elif 'Bridge' in device_class:
|
elif 'Bridge' in device_class:
|
||||||
include_device = False
|
include_device = False
|
||||||
|
|
||||||
if include_device:
|
if include_device:
|
||||||
pci_device = {
|
pci_device = {
|
||||||
'slot': current_device.get('Slot', 'Unknown'),
|
'slot': slot,
|
||||||
'type': device_type,
|
'type': device_type,
|
||||||
'vendor': vendor,
|
'vendor': vendor,
|
||||||
'device': device_name,
|
'device': device_name,
|
||||||
'class': device_class
|
'class': device_class
|
||||||
}
|
}
|
||||||
hardware_data['pci_devices'].append(pci_device)
|
hardware_data['pci_devices'].append(pci_device)
|
||||||
print(f"[v0] PCI Device: {device_type} - {device_name}")
|
|
||||||
|
|
||||||
current_device = {}
|
current_device = {}
|
||||||
elif ':' in line:
|
elif ':' in line:
|
||||||
key, value = line.split(':', 1)
|
key, value = line.split(':', 1)
|
||||||
current_device[key.strip()] = value.strip()
|
current_device[key.strip()] = value.strip()
|
||||||
|
|
||||||
print(f"[v0] Total PCI devices found: {len(hardware_data['pci_devices'])}")
|
# Now get driver information with lspci -k
|
||||||
print(f"[v0] Graphics cards: {len(hardware_data['graphics_cards'])}")
|
result_k = subprocess.run(['lspci', '-k'], capture_output=True, text=True, timeout=10)
|
||||||
print(f"[v0] Network cards: {len(hardware_data['network_cards'])}")
|
if result_k.returncode == 0:
|
||||||
|
current_slot = None
|
||||||
|
current_driver = None
|
||||||
|
current_module = None
|
||||||
|
|
||||||
|
for line in result_k.stdout.split('\n'):
|
||||||
|
# Match PCI slot line (e.g., "00:1f.2 SATA controller: ...")
|
||||||
|
if line and not line.startswith('\t'):
|
||||||
|
parts = line.split(' ', 1)
|
||||||
|
if parts:
|
||||||
|
current_slot = parts[0]
|
||||||
|
current_driver = None
|
||||||
|
current_module = None
|
||||||
|
# Match driver lines (indented with tab)
|
||||||
|
elif line.startswith('\t'):
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('Kernel driver in use:'):
|
||||||
|
current_driver = line.split(':', 1)[1].strip()
|
||||||
|
elif line.startswith('Kernel modules:'):
|
||||||
|
current_module = line.split(':', 1)[1].strip()
|
||||||
|
|
||||||
|
# Update the corresponding PCI device
|
||||||
|
if current_slot and (current_driver or current_module):
|
||||||
|
for device in hardware_data['pci_devices']:
|
||||||
|
if device['slot'] == current_slot:
|
||||||
|
if current_driver:
|
||||||
|
device['driver'] = current_driver
|
||||||
|
if current_module:
|
||||||
|
device['kernel_module'] = current_module
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"[v0] Total PCI devices found: {len(hardware_data['pci_devices'])}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[v0] Error getting PCI devices: {e}")
|
print(f"[v0] Error getting PCI devices: {e}")
|
||||||
|
|
||||||
@@ -2047,12 +2040,29 @@ def api_hardware():
|
|||||||
"""Get comprehensive hardware information"""
|
"""Get comprehensive hardware information"""
|
||||||
hardware_info = get_hardware_info()
|
hardware_info = get_hardware_info()
|
||||||
|
|
||||||
network_info = get_network_info()
|
# Format data for frontend
|
||||||
hardware_info['network_interfaces'] = network_info.get('physical_interfaces', []) + network_info.get('bridge_interfaces', [])
|
formatted_data = {
|
||||||
|
'cpu': hardware_info.get('cpu', {}),
|
||||||
|
'motherboard': hardware_info.get('motherboard', {}),
|
||||||
|
'memory_modules': hardware_info.get('memory_modules', []),
|
||||||
|
'storage_devices': hardware_info.get('storage_devices', []),
|
||||||
|
'pci_devices': hardware_info.get('pci_devices', []),
|
||||||
|
'temperatures': hardware_info.get('sensors', {}).get('temperatures', []),
|
||||||
|
'fans': hardware_info.get('ipmi_fans', []),
|
||||||
|
'power_supplies': hardware_info.get('ipmi_power', {}).get('power_supplies', []),
|
||||||
|
'power_meter': hardware_info.get('power_meter'),
|
||||||
|
'ups': hardware_info.get('ups') if hardware_info.get('ups') else None
|
||||||
|
}
|
||||||
|
|
||||||
print(f"[v0] /api/hardware returning {len(hardware_info.get('network_interfaces', []))} network interfaces")
|
print(f"[v0] /api/hardware returning data")
|
||||||
|
print(f"[v0] - CPU: {formatted_data['cpu'].get('model', 'Unknown')}")
|
||||||
|
print(f"[v0] - Temperatures: {len(formatted_data['temperatures'])} sensors")
|
||||||
|
print(f"[v0] - Fans: {len(formatted_data['fans'])} fans")
|
||||||
|
print(f"[v0] - Power supplies: {len(formatted_data['power_supplies'])} PSUs")
|
||||||
|
print(f"[v0] - Power meter: {'Yes' if formatted_data['power_meter'] else 'No'}")
|
||||||
|
print(f"[v0] - UPS: {'Yes' if formatted_data['ups'] else 'No'}")
|
||||||
|
|
||||||
return jsonify(hardware_info)
|
return jsonify(formatted_data)
|
||||||
|
|
||||||
@app.route('/api/vms/<int:vmid>', methods=['GET'])
|
@app.route('/api/vms/<int:vmid>', methods=['GET'])
|
||||||
def api_vm_details(vmid):
|
def api_vm_details(vmid):
|
||||||
|
@@ -38,6 +38,17 @@ export interface PCIDevice {
|
|||||||
memory_address?: string
|
memory_address?: string
|
||||||
link_speed?: string
|
link_speed?: string
|
||||||
capabilities?: string[]
|
capabilities?: string[]
|
||||||
|
gpu_memory?: string
|
||||||
|
gpu_driver_version?: string
|
||||||
|
gpu_cuda_version?: string
|
||||||
|
gpu_compute_capability?: string
|
||||||
|
gpu_power_draw?: string
|
||||||
|
gpu_temperature?: number
|
||||||
|
gpu_utilization?: number
|
||||||
|
gpu_memory_used?: string
|
||||||
|
gpu_memory_total?: string
|
||||||
|
gpu_clock_speed?: string
|
||||||
|
gpu_memory_clock?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Fan {
|
export interface Fan {
|
||||||
|
Reference in New Issue
Block a user