diff --git a/AppImage/ProxMenux-1.0.1.AppImage b/AppImage/ProxMenux-1.0.1.AppImage deleted file mode 100755 index 2011a10..0000000 Binary files a/AppImage/ProxMenux-1.0.1.AppImage and /dev/null differ diff --git a/AppImage/ProxMenux-Monitor.AppImage.sha256 b/AppImage/ProxMenux-Monitor.AppImage.sha256 deleted file mode 100644 index 8ae47f1..0000000 --- a/AppImage/ProxMenux-Monitor.AppImage.sha256 +++ /dev/null @@ -1 +0,0 @@ -f35de512c1a19843d15a9a3263a5104759d041ffc9d01249450babe0b0c3f889 ProxMenux-1.0.1.AppImage diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index 404529a..4c92378 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -4,11 +4,30 @@ import { Card } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Progress } from "@/components/ui/progress" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" -import { Thermometer, CpuIcon, Zap, HardDrive, Network, FanIcon, PowerIcon, Battery, Cpu, MemoryStick, Cpu as Gpu, Loader2 } from 'lucide-react' +import { + Thermometer, + CpuIcon, + Zap, + HardDrive, + Network, + FanIcon, + PowerIcon, + Battery, + Cpu, + MemoryStick, + Cpu as Gpu, + Loader2, +} from "lucide-react" import useSWR from "swr" import { useState, useEffect } from "react" -import { type HardwareData, type GPU, type PCIDevice, type StorageDevice, fetcher } from "../types/hardware" -import { API_PORT } from "@/lib/api-config" +import { + type HardwareData, + type GPU, + type PCIDevice, + type StorageDevice, + fetcher as swrFetcher, +} from "../types/hardware" +import { fetchApi } from "@/lib/api-config" const parseLsblkSize = (sizeStr: string | undefined): number => { if (!sizeStr) return 0 @@ -16,11 +35,8 @@ const parseLsblkSize = (sizeStr: string | undefined): number => { // Remove spaces and convert to uppercase const cleaned = sizeStr.trim().toUpperCase() - // Replace comma with dot to normalize decimal separator (for European locales) - const normalized = cleaned.replace(',', '.') - // Extract number and unit - const match = normalized.match(/^([\d.]+)([KMGT]?)$/) + const match = cleaned.match(/^([\d.]+)([KMGT]?)$/) if (!match) return 0 const value = Number.parseFloat(match[1]) @@ -159,7 +175,7 @@ export default function Hardware() { data: staticHardwareData, error: staticError, isLoading: staticLoading, - } = useSWR("/api/hardware", fetcher, { + } = useSWR("/api/hardware", swrFetcher, { revalidateOnFocus: false, revalidateOnReconnect: false, refreshInterval: 0, // No auto-refresh for static data @@ -170,7 +186,7 @@ export default function Hardware() { data: dynamicHardwareData, error: dynamicError, isLoading: dynamicLoading, - } = useSWR("/api/hardware", fetcher, { + } = useSWR("/api/hardware", swrFetcher, { refreshInterval: 7000, }) @@ -221,6 +237,21 @@ export default function Hardware() { const [selectedNetwork, setSelectedNetwork] = useState(null) const [selectedUPS, setSelectedUPS] = useState(null) + const fetcher = async (url: string) => { + const data = await fetchApi(url) + return data + } + + const { + data: hardwareDataSWR, + error: swrError, + isLoading: swrLoading, + mutate, + } = useSWR("/api/hardware", fetcher, { + refreshInterval: 30000, + revalidateOnFocus: false, + }) + useEffect(() => { if (!selectedGPU) return @@ -233,30 +264,10 @@ export default function Hardware() { const fetchRealtimeData = async () => { try { - const { protocol, hostname, port } = window.location - const isStandardPort = port === "" || port === "80" || port === "443" - - const apiUrl = isStandardPort - ? `/api/gpu/${fullSlot}/realtime` - : `${protocol}//${hostname}:${API_PORT}/api/gpu/${fullSlot}/realtime` - - const response = await fetch(apiUrl, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - signal: abortController.signal, - }) - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`) - } - - const data = await response.json() + const data = await fetchApi(`/api/gpu/${fullSlot}/realtime`) setRealtimeGPUData(data) setDetailsLoading(false) } catch (error) { - // Only log non-abort errors if (error instanceof Error && error.name !== "AbortError") { console.error("[v0] Error fetching GPU realtime data:", error) } @@ -265,10 +276,7 @@ export default function Hardware() { } } - // Initial fetch fetchRealtimeData() - - // Poll every 3 seconds const interval = setInterval(fetchRealtimeData, 3000) return () => { @@ -284,14 +292,14 @@ export default function Hardware() { } const findPCIDeviceForGPU = (gpu: GPU): PCIDevice | null => { - if (!hardwareData?.pci_devices || !gpu.slot) return null + if (!hardwareDataSWR?.pci_devices || !gpu.slot) return null // Try to find exact match first (e.g., "00:02.0") - let pciDevice = hardwareData.pci_devices.find((d) => d.slot === gpu.slot) + let pciDevice = hardwareDataSWR.pci_devices.find((d) => d.slot === gpu.slot) // If not found, try to match by partial slot (e.g., "00" matches "00:02.0") if (!pciDevice && gpu.slot.length <= 2) { - pciDevice = hardwareData.pci_devices.find( + pciDevice = hardwareDataSWR.pci_devices.find( (d) => d.slot.startsWith(gpu.slot + ":") && (d.type.toLowerCase().includes("vga") || @@ -310,7 +318,7 @@ export default function Hardware() { return realtimeGPUData.has_monitoring_tool === true } - if (isLoading) { + if (swrLoading) { return (
@@ -323,7 +331,7 @@ export default function Hardware() { return (
{/* System Information - CPU & Motherboard */} - {(hardwareData?.cpu || hardwareData?.motherboard) && ( + {(hardwareDataSWR?.cpu || hardwareDataSWR?.motherboard) && (
@@ -332,44 +340,44 @@ export default function Hardware() {
{/* CPU Info */} - {hardwareData?.cpu && Object.keys(hardwareData.cpu).length > 0 && ( + {hardwareDataSWR?.cpu && Object.keys(hardwareDataSWR.cpu).length > 0 && (

CPU

- {hardwareData.cpu.model && ( + {hardwareDataSWR.cpu.model && (
Model - {hardwareData.cpu.model} + {hardwareDataSWR.cpu.model}
)} - {hardwareData.cpu.cores_per_socket && hardwareData.cpu.sockets && ( + {hardwareDataSWR.cpu.cores_per_socket && hardwareDataSWR.cpu.sockets && (
Cores - {hardwareData.cpu.sockets} × {hardwareData.cpu.cores_per_socket} ={" "} - {hardwareData.cpu.sockets * hardwareData.cpu.cores_per_socket} cores + {hardwareDataSWR.cpu.sockets} × {hardwareDataSWR.cpu.cores_per_socket} ={" "} + {hardwareDataSWR.cpu.sockets * hardwareDataSWR.cpu.cores_per_socket} cores
)} - {hardwareData.cpu.total_threads && ( + {hardwareDataSWR.cpu.total_threads && (
Threads - {hardwareData.cpu.total_threads} + {hardwareDataSWR.cpu.total_threads}
)} - {hardwareData.cpu.l3_cache && ( + {hardwareDataSWR.cpu.l3_cache && (
L3 Cache - {hardwareData.cpu.l3_cache} + {hardwareDataSWR.cpu.l3_cache}
)} - {hardwareData.cpu.virtualization && ( + {hardwareDataSWR.cpu.virtualization && (
Virtualization - {hardwareData.cpu.virtualization} + {hardwareDataSWR.cpu.virtualization}
)}
@@ -377,41 +385,41 @@ export default function Hardware() { )} {/* Motherboard Info */} - {hardwareData?.motherboard && Object.keys(hardwareData.motherboard).length > 0 && ( + {hardwareDataSWR?.motherboard && Object.keys(hardwareDataSWR.motherboard).length > 0 && (

Motherboard

- {hardwareData.motherboard.manufacturer && ( + {hardwareDataSWR.motherboard.manufacturer && (
Manufacturer - {hardwareData.motherboard.manufacturer} + {hardwareDataSWR.motherboard.manufacturer}
)} - {hardwareData.motherboard.model && ( + {hardwareDataSWR.motherboard.model && (
Model - {hardwareData.motherboard.model} + {hardwareDataSWR.motherboard.model}
)} - {hardwareData.motherboard.bios?.vendor && ( + {hardwareDataSWR.motherboard.bios?.vendor && (
BIOS - {hardwareData.motherboard.bios.vendor} + {hardwareDataSWR.motherboard.bios.vendor}
)} - {hardwareData.motherboard.bios?.version && ( + {hardwareDataSWR.motherboard.bios?.version && (
Version - {hardwareData.motherboard.bios.version} + {hardwareDataSWR.motherboard.bios.version}
)} - {hardwareData.motherboard.bios?.date && ( + {hardwareDataSWR.motherboard.bios?.date && (
Date - {hardwareData.motherboard.bios.date} + {hardwareDataSWR.motherboard.bios.date}
)}
@@ -422,18 +430,18 @@ export default function Hardware() { )} {/* Memory Modules */} - {hardwareData?.memory_modules && hardwareData.memory_modules.length > 0 && ( + {hardwareDataSWR?.memory_modules && hardwareDataSWR.memory_modules.length > 0 && (

Memory Modules

- {hardwareData.memory_modules.length} installed + {hardwareDataSWR.memory_modules.length} installed
- {hardwareData.memory_modules.map((module, index) => ( + {hardwareDataSWR.memory_modules.map((module, index) => (
{module.slot}
@@ -469,29 +477,29 @@ export default function Hardware() { )} {/* Thermal Monitoring */} - {hardwareData?.temperatures && hardwareData.temperatures.length > 0 && ( + {hardwareDataSWR?.temperatures && hardwareDataSWR.temperatures.length > 0 && (

Thermal Monitoring

- {hardwareData.temperatures.length} sensors + {hardwareDataSWR.temperatures.length} sensors
{/* CPU Sensors */} - {groupAndSortTemperatures(hardwareData.temperatures).CPU.length > 0 && ( + {groupAndSortTemperatures(hardwareDataSWR.temperatures).CPU.length > 0 && (

CPU

- {groupAndSortTemperatures(hardwareData.temperatures).CPU.length} + {groupAndSortTemperatures(hardwareDataSWR.temperatures).CPU.length}
- {groupAndSortTemperatures(hardwareData.temperatures).CPU.map((temp, index) => { + {groupAndSortTemperatures(hardwareDataSWR.temperatures).CPU.map((temp, index) => { const percentage = temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 const isHot = temp.current > (temp.high || 80) @@ -522,21 +530,21 @@ export default function Hardware() { )} {/* GPU Sensors */} - {groupAndSortTemperatures(hardwareData.temperatures).GPU.length > 0 && ( + {groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.length > 0 && (
1 ? "md:col-span-2" : ""} + className={groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.length > 1 ? "md:col-span-2" : ""} >

GPU

- {groupAndSortTemperatures(hardwareData.temperatures).GPU.length} + {groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.length}
1 ? "md:grid-cols-2" : ""}`} + className={`grid gap-4 ${groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.length > 1 ? "md:grid-cols-2" : ""}`} > - {groupAndSortTemperatures(hardwareData.temperatures).GPU.map((temp, index) => { + {groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.map((temp, index) => { const percentage = temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 const isHot = temp.current > (temp.high || 80) @@ -567,21 +575,23 @@ export default function Hardware() { )} {/* NVME Sensors */} - {groupAndSortTemperatures(hardwareData.temperatures).NVME.length > 0 && ( + {groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.length > 0 && (
1 ? "md:col-span-2" : ""} + className={ + groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.length > 1 ? "md:col-span-2" : "" + } >

NVME

- {groupAndSortTemperatures(hardwareData.temperatures).NVME.length} + {groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.length}
1 ? "md:grid-cols-2" : ""}`} + className={`grid gap-4 ${groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.length > 1 ? "md:grid-cols-2" : ""}`} > - {groupAndSortTemperatures(hardwareData.temperatures).NVME.map((temp, index) => { + {groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.map((temp, index) => { const percentage = temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 const isHot = temp.current > (temp.high || 80) @@ -612,21 +622,21 @@ export default function Hardware() { )} {/* PCI Sensors */} - {groupAndSortTemperatures(hardwareData.temperatures).PCI.length > 0 && ( + {groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.length > 0 && (
1 ? "md:col-span-2" : ""} + className={groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.length > 1 ? "md:col-span-2" : ""} >

PCI

- {groupAndSortTemperatures(hardwareData.temperatures).PCI.length} + {groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.length}
1 ? "md:grid-cols-2" : ""}`} + className={`grid gap-4 ${groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.length > 1 ? "md:grid-cols-2" : ""}`} > - {groupAndSortTemperatures(hardwareData.temperatures).PCI.map((temp, index) => { + {groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.map((temp, index) => { const percentage = temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 const isHot = temp.current > (temp.high || 80) @@ -657,21 +667,23 @@ export default function Hardware() { )} {/* OTHER Sensors */} - {groupAndSortTemperatures(hardwareData.temperatures).OTHER.length > 0 && ( + {groupAndSortTemperatures(hardwareDataSWR.temperatures).OTHER.length > 0 && (
1 ? "md:col-span-2" : ""} + className={ + groupAndSortTemperatures(hardwareDataSWR.temperatures).OTHER.length > 1 ? "md:col-span-2" : "" + } >

OTHER

- {groupAndSortTemperatures(hardwareData.temperatures).OTHER.length} + {groupAndSortTemperatures(hardwareDataSWR.temperatures).OTHER.length}
1 ? "md:grid-cols-2" : ""}`} + className={`grid gap-4 ${groupAndSortTemperatures(hardwareDataSWR.temperatures).OTHER.length > 1 ? "md:grid-cols-2" : ""}`} > - {groupAndSortTemperatures(hardwareData.temperatures).OTHER.map((temp, index) => { + {groupAndSortTemperatures(hardwareDataSWR.temperatures).OTHER.map((temp, index) => { const percentage = temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 const isHot = temp.current > (temp.high || 80) @@ -705,18 +717,18 @@ export default function Hardware() { )} {/* GPU Information - Enhanced with on-demand data fetching */} - {hardwareData?.gpus && hardwareData.gpus.length > 0 && ( + {hardwareDataSWR?.gpus && hardwareDataSWR.gpus.length > 0 && (

Graphics Cards

- {hardwareData.gpus.length} GPU{hardwareData.gpus.length > 1 ? "s" : ""} + {hardwareDataSWR.gpus.length} GPU{hardwareDataSWR.gpus.length > 1 ? "s" : ""}
- {hardwareData.gpus.map((gpu, index) => { + {hardwareDataSWR.gpus.map((gpu, index) => { const pciDevice = findPCIDeviceForGPU(gpu) const fullSlot = pciDevice?.slot || gpu.slot @@ -1094,18 +1106,18 @@ export default function Hardware() { {/* PCI Devices - Changed to modal */} - {hardwareData?.pci_devices && hardwareData.pci_devices.length > 0 && ( + {hardwareDataSWR?.pci_devices && hardwareDataSWR.pci_devices.length > 0 && (

PCI Devices

- {hardwareData.pci_devices.length} devices + {hardwareDataSWR.pci_devices.length} devices
- {hardwareData.pci_devices.map((device, index) => ( + {hardwareDataSWR.pci_devices.map((device, index) => (
setSelectedPCIDevice(device)} @@ -1180,7 +1192,7 @@ export default function Hardware() { {/* Power Consumption */} - {hardwareData?.power_meter && ( + {hardwareDataSWR?.power_meter && (
@@ -1190,13 +1202,13 @@ export default function Hardware() {
-

{hardwareData.power_meter.name}

- {hardwareData.power_meter.adapter && ( -

{hardwareData.power_meter.adapter}

+

{hardwareDataSWR.power_meter.name}

+ {hardwareDataSWR.power_meter.adapter && ( +

{hardwareDataSWR.power_meter.adapter}

)}
-

{hardwareData.power_meter.watts.toFixed(1)} W

+

{hardwareDataSWR.power_meter.watts.toFixed(1)} W

Current Draw

@@ -1205,18 +1217,18 @@ export default function Hardware() { )} {/* Power Supplies */} - {hardwareData?.power_supplies && hardwareData.power_supplies.length > 0 && ( + {hardwareDataSWR?.power_supplies && hardwareDataSWR.power_supplies.length > 0 && (

Power Supplies

- {hardwareData.power_supplies.length} PSUs + {hardwareDataSWR.power_supplies.length} PSUs
- {hardwareData.power_supplies.map((psu, index) => ( + {hardwareDataSWR.power_supplies.map((psu, index) => (
{psu.name} @@ -1233,18 +1245,18 @@ export default function Hardware() { )} {/* Fans */} - {hardwareData?.fans && hardwareData.fans.length > 0 && ( + {hardwareDataSWR?.fans && hardwareDataSWR.fans.length > 0 && (

System Fans

- {hardwareData.fans.length} fans + {hardwareDataSWR.fans.length} fans
- {hardwareData.fans.map((fan, index) => { + {hardwareDataSWR.fans.map((fan, index) => { const isPercentage = fan.unit === "percent" || fan.unit === "%" const percentage = isPercentage ? fan.speed : Math.min((fan.speed / 5000) * 100, 100) @@ -1268,18 +1280,18 @@ export default function Hardware() { )} {/* UPS */} - {hardwareData?.ups && Array.isArray(hardwareData.ups) && hardwareData.ups.length > 0 && ( + {hardwareDataSWR?.ups && Array.isArray(hardwareDataSWR.ups) && hardwareDataSWR.ups.length > 0 && (

UPS Status

- {hardwareData.ups.length} UPS + {hardwareDataSWR.ups.length} UPS
- {hardwareData.ups.map((ups: any, index: number) => { + {hardwareDataSWR.ups.map((ups: any, index: number) => { const batteryCharge = ups.battery_charge_raw || Number.parseFloat(ups.battery_charge?.replace("%", "") || "0") const loadPercent = ups.load_percent_raw || Number.parseFloat(ups.load_percent?.replace("%", "") || "0") @@ -1550,19 +1562,19 @@ export default function Hardware() { {/* Network Summary - Clickable */} - {hardwareData?.pci_devices && - hardwareData.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && ( + {hardwareDataSWR?.pci_devices && + hardwareDataSWR.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && (

Network Summary

- {hardwareData.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length} interfaces + {hardwareDataSWR.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length} interfaces
- {hardwareData.pci_devices + {hardwareDataSWR.pci_devices .filter((d) => d.type.toLowerCase().includes("network")) .map((device, index) => (
{/* Storage Summary - Clickable */} - {hardwareData?.storage_devices && hardwareData.storage_devices.length > 0 && ( + {hardwareDataSWR?.storage_devices && hardwareDataSWR.storage_devices.length > 0 && (

Storage Summary

{ - hardwareData.storage_devices.filter( + hardwareDataSWR.storage_devices.filter( (device) => device.type === "disk" && !device.name.startsWith("zd") && !device.name.startsWith("loop"), ).length @@ -1659,7 +1671,7 @@ export default function Hardware() {
- {hardwareData.storage_devices + {hardwareDataSWR.storage_devices .filter( (device) => device.type === "disk" && !device.name.startsWith("zd") && !device.name.startsWith("loop"), ) @@ -1768,7 +1780,7 @@ export default function Hardware() { {device.name} {diskBadge.label}
- {device.size &&

{device.size}

} + {device.size &&

{formatMemory(parseLsblkSize(device.size))}

} {device.model && (

{device.model}

)} @@ -1845,7 +1857,7 @@ export default function Hardware() { {selectedDisk.size && (
Capacity - {selectedDisk.size} + {formatMemory(parseLsblkSize(selectedDisk.size))}
)} diff --git a/AppImage/components/login.tsx b/AppImage/components/login.tsx index d1de5d7..0c6c4f9 100644 --- a/AppImage/components/login.tsx +++ b/AppImage/components/login.tsx @@ -237,7 +237,7 @@ export function Login({ onLogin }: LoginProps) {
-

ProxMenux Monitor v1.0.2

+

ProxMenux Monitor v1.0.1

) diff --git a/AppImage/components/proxmox-dashboard.tsx b/AppImage/components/proxmox-dashboard.tsx index b674924..214c81f 100644 --- a/AppImage/components/proxmox-dashboard.tsx +++ b/AppImage/components/proxmox-dashboard.tsx @@ -646,7 +646,7 @@ export function ProxmoxDashboard() {