From 166fc6dad975c7b4304aa4e9519db3021171cc37 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 17 Nov 2025 17:47:58 +0100 Subject: [PATCH] Update AppImage --- AppImage/components/hardware.tsx | 248 +++++++++++++++---------------- AppImage/lib/api-config.ts | 2 +- AppImage/scripts/flask_server.py | 2 +- 3 files changed, 120 insertions(+), 132 deletions(-) diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index 4c92378..404529a 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -4,30 +4,11 @@ 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 as swrFetcher, -} from "../types/hardware" -import { fetchApi } from "@/lib/api-config" +import { type HardwareData, type GPU, type PCIDevice, type StorageDevice, fetcher } from "../types/hardware" +import { API_PORT } from "@/lib/api-config" const parseLsblkSize = (sizeStr: string | undefined): number => { if (!sizeStr) return 0 @@ -35,8 +16,11 @@ 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 = cleaned.match(/^([\d.]+)([KMGT]?)$/) + const match = normalized.match(/^([\d.]+)([KMGT]?)$/) if (!match) return 0 const value = Number.parseFloat(match[1]) @@ -175,7 +159,7 @@ export default function Hardware() { data: staticHardwareData, error: staticError, isLoading: staticLoading, - } = useSWR("/api/hardware", swrFetcher, { + } = useSWR("/api/hardware", fetcher, { revalidateOnFocus: false, revalidateOnReconnect: false, refreshInterval: 0, // No auto-refresh for static data @@ -186,7 +170,7 @@ export default function Hardware() { data: dynamicHardwareData, error: dynamicError, isLoading: dynamicLoading, - } = useSWR("/api/hardware", swrFetcher, { + } = useSWR("/api/hardware", fetcher, { refreshInterval: 7000, }) @@ -237,21 +221,6 @@ 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 @@ -264,10 +233,30 @@ export default function Hardware() { const fetchRealtimeData = async () => { try { - const data = await fetchApi(`/api/gpu/${fullSlot}/realtime`) + 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() 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) } @@ -276,7 +265,10 @@ export default function Hardware() { } } + // Initial fetch fetchRealtimeData() + + // Poll every 3 seconds const interval = setInterval(fetchRealtimeData, 3000) return () => { @@ -292,14 +284,14 @@ export default function Hardware() { } const findPCIDeviceForGPU = (gpu: GPU): PCIDevice | null => { - if (!hardwareDataSWR?.pci_devices || !gpu.slot) return null + if (!hardwareData?.pci_devices || !gpu.slot) return null // Try to find exact match first (e.g., "00:02.0") - let pciDevice = hardwareDataSWR.pci_devices.find((d) => d.slot === gpu.slot) + let pciDevice = hardwareData.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 = hardwareDataSWR.pci_devices.find( + pciDevice = hardwareData.pci_devices.find( (d) => d.slot.startsWith(gpu.slot + ":") && (d.type.toLowerCase().includes("vga") || @@ -318,7 +310,7 @@ export default function Hardware() { return realtimeGPUData.has_monitoring_tool === true } - if (swrLoading) { + if (isLoading) { return (
@@ -331,7 +323,7 @@ export default function Hardware() { return (
{/* System Information - CPU & Motherboard */} - {(hardwareDataSWR?.cpu || hardwareDataSWR?.motherboard) && ( + {(hardwareData?.cpu || hardwareData?.motherboard) && (
@@ -340,44 +332,44 @@ export default function Hardware() {
{/* CPU Info */} - {hardwareDataSWR?.cpu && Object.keys(hardwareDataSWR.cpu).length > 0 && ( + {hardwareData?.cpu && Object.keys(hardwareData.cpu).length > 0 && (

CPU

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

Motherboard

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

Memory Modules

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

Thermal Monitoring

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

CPU

- {groupAndSortTemperatures(hardwareDataSWR.temperatures).CPU.length} + {groupAndSortTemperatures(hardwareData.temperatures).CPU.length}
- {groupAndSortTemperatures(hardwareDataSWR.temperatures).CPU.map((temp, index) => { + {groupAndSortTemperatures(hardwareData.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) @@ -530,21 +522,21 @@ export default function Hardware() { )} {/* GPU Sensors */} - {groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.length > 0 && ( + {groupAndSortTemperatures(hardwareData.temperatures).GPU.length > 0 && (
1 ? "md:col-span-2" : ""} + className={groupAndSortTemperatures(hardwareData.temperatures).GPU.length > 1 ? "md:col-span-2" : ""} >

GPU

- {groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.length} + {groupAndSortTemperatures(hardwareData.temperatures).GPU.length}
1 ? "md:grid-cols-2" : ""}`} + className={`grid gap-4 ${groupAndSortTemperatures(hardwareData.temperatures).GPU.length > 1 ? "md:grid-cols-2" : ""}`} > - {groupAndSortTemperatures(hardwareDataSWR.temperatures).GPU.map((temp, index) => { + {groupAndSortTemperatures(hardwareData.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) @@ -575,23 +567,21 @@ export default function Hardware() { )} {/* NVME Sensors */} - {groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.length > 0 && ( + {groupAndSortTemperatures(hardwareData.temperatures).NVME.length > 0 && (
1 ? "md:col-span-2" : "" - } + className={groupAndSortTemperatures(hardwareData.temperatures).NVME.length > 1 ? "md:col-span-2" : ""} >

NVME

- {groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.length} + {groupAndSortTemperatures(hardwareData.temperatures).NVME.length}
1 ? "md:grid-cols-2" : ""}`} + className={`grid gap-4 ${groupAndSortTemperatures(hardwareData.temperatures).NVME.length > 1 ? "md:grid-cols-2" : ""}`} > - {groupAndSortTemperatures(hardwareDataSWR.temperatures).NVME.map((temp, index) => { + {groupAndSortTemperatures(hardwareData.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) @@ -622,21 +612,21 @@ export default function Hardware() { )} {/* PCI Sensors */} - {groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.length > 0 && ( + {groupAndSortTemperatures(hardwareData.temperatures).PCI.length > 0 && (
1 ? "md:col-span-2" : ""} + className={groupAndSortTemperatures(hardwareData.temperatures).PCI.length > 1 ? "md:col-span-2" : ""} >

PCI

- {groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.length} + {groupAndSortTemperatures(hardwareData.temperatures).PCI.length}
1 ? "md:grid-cols-2" : ""}`} + className={`grid gap-4 ${groupAndSortTemperatures(hardwareData.temperatures).PCI.length > 1 ? "md:grid-cols-2" : ""}`} > - {groupAndSortTemperatures(hardwareDataSWR.temperatures).PCI.map((temp, index) => { + {groupAndSortTemperatures(hardwareData.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) @@ -667,23 +657,21 @@ export default function Hardware() { )} {/* OTHER Sensors */} - {groupAndSortTemperatures(hardwareDataSWR.temperatures).OTHER.length > 0 && ( + {groupAndSortTemperatures(hardwareData.temperatures).OTHER.length > 0 && (
1 ? "md:col-span-2" : "" - } + className={groupAndSortTemperatures(hardwareData.temperatures).OTHER.length > 1 ? "md:col-span-2" : ""} >

OTHER

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

Graphics Cards

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

PCI Devices

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

{hardwareDataSWR.power_meter.name}

- {hardwareDataSWR.power_meter.adapter && ( -

{hardwareDataSWR.power_meter.adapter}

+

{hardwareData.power_meter.name}

+ {hardwareData.power_meter.adapter && ( +

{hardwareData.power_meter.adapter}

)}
-

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

+

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

Current Draw

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

Power Supplies

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

System Fans

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

UPS Status

- {hardwareDataSWR.ups.length} UPS + {hardwareData.ups.length} UPS
- {hardwareDataSWR.ups.map((ups: any, index: number) => { + {hardwareData.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") @@ -1562,19 +1550,19 @@ export default function Hardware() { {/* Network Summary - Clickable */} - {hardwareDataSWR?.pci_devices && - hardwareDataSWR.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && ( + {hardwareData?.pci_devices && + hardwareData.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && (

Network Summary

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

Storage Summary

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

{formatMemory(parseLsblkSize(device.size))}

} + {device.size &&

{device.size}

} {device.model && (

{device.model}

)} @@ -1857,7 +1845,7 @@ export default function Hardware() { {selectedDisk.size && (
Capacity - {formatMemory(parseLsblkSize(selectedDisk.size))} + {selectedDisk.size}
)} diff --git a/AppImage/lib/api-config.ts b/AppImage/lib/api-config.ts index 34175c9..3e8b352 100644 --- a/AppImage/lib/api-config.ts +++ b/AppImage/lib/api-config.ts @@ -9,7 +9,7 @@ * Can be changed to 8009 for beta testing * This can also be set via NEXT_PUBLIC_API_PORT environment variable */ -export const API_PORT = process.env.NEXT_PUBLIC_API_PORT || "8008" +export const API_PORT = process.env.NEXT_PUBLIC_API_PORT || "8009" /** * Gets the base URL for API calls diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index f040ef9..b6a64fe 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -6110,4 +6110,4 @@ if __name__ == '__main__': # Print only essential information # print("API endpoints available at: /api/system, /api/system-info, /api/storage, /api/proxmox-storage, /api/network, /api/vms, /api/logs, /api/health, /api/hardware, /api/prometheus, /api/node/metrics") - app.run(host='0.0.0.0', port=8008, debug=False) + app.run(host='0.0.0.0', port=8009, debug=False)