From 84eec4655aef149dd5587ce130fcbde137816f1e Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 6 Oct 2025 11:02:00 +0200 Subject: [PATCH] Update AppImage --- AppImage/components/hardware.tsx | 102 ++++++++++++++++++++++++++- AppImage/scripts/build_appimage.sh | 42 +++++++++++- AppImage/scripts/flask_server.py | 106 ++++++++++++----------------- 3 files changed, 185 insertions(+), 65 deletions(-) diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index 0ea25ef..df55980 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -3,8 +3,10 @@ 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 { Cpu, MemoryStick, HardDrive, Network, Thermometer, Fan, Battery, Server, CpuIcon } from "lucide-react" import useSWR from "swr" +import { useState } from "react" const fetcher = (url: string) => fetch(url).then((res) => res.json()) @@ -91,6 +93,12 @@ interface PCIDevice { vendor: string device: string class: string + driver?: string + kernel_module?: string + irq?: string + memory_address?: string + link_speed?: string + capabilities?: string[] } interface HardwareData { @@ -113,6 +121,8 @@ export default function Hardware() { refreshInterval: 5000, }) + const [selectedPCIDevice, setSelectedPCIDevice] = useState(null) + if (error) { return (
@@ -430,7 +440,8 @@ export default function Hardware() { {hardwareData.pci_devices.map((device, index) => (
setSelectedPCIDevice(device)} + className="flex cursor-pointer items-start justify-between rounded-lg border border-border/30 bg-background/50 p-4 transition-colors hover:border-primary/50 hover:bg-background/80" >
@@ -550,6 +561,95 @@ export default function Hardware() {
)} + + {/* PCI Device Details Modal */} + setSelectedPCIDevice(null)}> + + + PCI Device Details + Detailed information about the selected PCI device + + + {selectedPCIDevice && ( +
+
+
+ Device Type + {selectedPCIDevice.type} +
+ +
+ PCI Slot + {selectedPCIDevice.slot} +
+ +
+ Device Name + {selectedPCIDevice.device} +
+ +
+ Vendor + {selectedPCIDevice.vendor} +
+ +
+ Class + {selectedPCIDevice.class} +
+ + {selectedPCIDevice.driver && ( +
+ Driver + {selectedPCIDevice.driver} +
+ )} + + {selectedPCIDevice.kernel_module && ( +
+ Kernel Module + {selectedPCIDevice.kernel_module} +
+ )} + + {selectedPCIDevice.irq && ( +
+ IRQ + {selectedPCIDevice.irq} +
+ )} + + {selectedPCIDevice.memory_address && ( +
+ Memory Address + {selectedPCIDevice.memory_address} +
+ )} + + {selectedPCIDevice.link_speed && ( +
+ Link Speed + {selectedPCIDevice.link_speed} +
+ )} + + {selectedPCIDevice.capabilities && selectedPCIDevice.capabilities.length > 0 && ( +
+ Capabilities +
+ {selectedPCIDevice.capabilities.map((cap, idx) => ( + + {cap} + + ))} +
+
+ )} +
+
+ )} +
+
) } diff --git a/AppImage/scripts/build_appimage.sh b/AppImage/scripts/build_appimage.sh index 0e731e8..f2a0d83 100644 --- a/AppImage/scripts/build_appimage.sh +++ b/AppImage/scripts/build_appimage.sh @@ -14,7 +14,7 @@ APPIMAGE_ROOT="$SCRIPT_DIR/.." VERSION=$(node -p "require('$APPIMAGE_ROOT/package.json').version") APPIMAGE_NAME="ProxMenux-${VERSION}.AppImage" -echo "🚀 Building ProxMenux Monitor AppImage v${VERSION} with translation support..." +echo "🚀 Building ProxMenux Monitor AppImage v${VERSION} with hardware monitoring tools..." # Clean and create work directory rm -rf "$WORK_DIR" @@ -317,6 +317,46 @@ def parse_header(value: str) -> Tuple[str, Dict[str, str]]: return key, params PYEOF +echo "🔧 Installing hardware monitoring tools..." +mkdir -p "$WORK_DIR/debs" +cd "$WORK_DIR/debs" + +# Download .deb packages +echo "📥 Downloading ipmitool..." +wget -q http://deb.debian.org/debian/pool/main/i/ipmitool/ipmitool_1.8.19-4_amd64.deb -O ipmitool.deb || true + +echo "📥 Downloading lm-sensors..." +wget -q http://deb.debian.org/debian/pool/main/l/lm-sensors/lm-sensors_3.6.0-7.1_amd64.deb -O lm-sensors.deb || true + +echo "📥 Downloading nut-client..." +wget -q http://deb.debian.org/debian/pool/main/n/nut/nut-client_2.8.0-7_amd64.deb -O nut-client.deb || true +wget -q http://deb.debian.org/debian/pool/main/n/nut/libupsclient6_2.8.0-7_amd64.deb -O libupsclient6.deb || true + +# Extract binaries from .deb packages +echo "📦 Extracting binaries..." +for deb in *.deb; do + if [ -f "$deb" ]; then + dpkg-deb -x "$deb" "$WORK_DIR/extracted" + fi +done + +# Copy binaries to AppDir +if [ -d "$WORK_DIR/extracted/usr/bin" ]; then + echo "📋 Copying monitoring tools to AppDir..." + cp -r "$WORK_DIR/extracted/usr/bin"/* "$APP_DIR/usr/bin/" 2>/dev/null || true +fi + +if [ -d "$WORK_DIR/extracted/usr/sbin" ]; then + cp -r "$WORK_DIR/extracted/usr/sbin"/* "$APP_DIR/usr/bin/" 2>/dev/null || true +fi + +if [ -d "$WORK_DIR/extracted/usr/lib" ]; then + mkdir -p "$APP_DIR/usr/lib" + cp -r "$WORK_DIR/extracted/usr/lib"/* "$APP_DIR/usr/lib/" 2>/dev/null || true +fi + +echo "✅ Hardware monitoring tools installed" + # Build AppImage echo "🔨 Building unified AppImage v${VERSION}..." cd "$WORK_DIR" diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index 0c01334..1b63e0e 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -69,40 +69,39 @@ def get_vm_lxc_names(): def serve_dashboard(): """Serve the main dashboard page from Next.js build""" try: - # Detectar si estamos ejecutándose desde AppImage appimage_root = os.environ.get('APPDIR') if not appimage_root: - # Fallback: detectar desde la ubicación del script - # Si el script está en usr/bin/, necesitamos subir 2 niveles para llegar a la raíz + # Fallback: detect from script location base_dir = os.path.dirname(os.path.abspath(__file__)) - # Verificar si estamos en usr/bin/ if base_dir.endswith('usr/bin'): - # Subir 2 niveles: usr/bin/ -> usr/ -> AppImage root + # We're in usr/bin/, go up 2 levels to AppImage root appimage_root = os.path.dirname(os.path.dirname(base_dir)) else: - # Fallback genérico: subir 1 nivel + # Fallback: assume we're in the root appimage_root = os.path.dirname(base_dir) print(f"[v0] Detected AppImage root: {appimage_root}") - index_paths = [ - os.path.join(appimage_root, 'web', 'index.html'), # Ruta principal para AppImage - os.path.join(appimage_root, 'usr', 'web', 'index.html'), # Fallback con usr/ - os.path.join(appimage_root, 'web', 'out', 'index.html'), # Fallback si está en subcarpeta - os.path.join(appimage_root, 'usr', 'web', 'out', 'index.html'), # Fallback con usr/out/ - ] + index_path = os.path.join(appimage_root, 'web', 'index.html') + abs_path = os.path.abspath(index_path) - print(f"[v0] Flask server looking for index.html in:") - for path in index_paths: - abs_path = os.path.abspath(path) - exists = os.path.exists(abs_path) - print(f"[v0] {abs_path} - {'EXISTS' if exists else 'NOT FOUND'}") - if exists: - print(f"[v0] Found index.html, serving from: {abs_path}") - return send_file(abs_path) + print(f"[v0] Looking for index.html at: {abs_path}") + + if os.path.exists(abs_path): + print(f"[v0] ✅ Found index.html, serving from: {abs_path}") + return send_file(abs_path) + + # If not found, show detailed error + print(f"[v0] ❌ index.html NOT found at: {abs_path}") + print(f"[v0] Checking web directory contents:") + web_dir = os.path.join(appimage_root, 'web') + if os.path.exists(web_dir): + print(f"[v0] Contents of {web_dir}:") + for item in os.listdir(web_dir): + print(f"[v0] - {item}") + else: + print(f"[v0] Web directory does not exist: {web_dir}") - # If no Next.js build found, return error message with actual paths checked - actual_paths = [os.path.abspath(path) for path in index_paths] return f''' @@ -110,8 +109,8 @@ def serve_dashboard():

🚨 ProxMenux Monitor - Build Error

Next.js application not found. The AppImage may not have been built correctly.

-

Expected paths checked:

-
    {''.join([f'
  • {path}
  • ' for path in actual_paths])}
+

Expected path: {abs_path}

+

APPDIR: {appimage_root}

API endpoints are still available:

  • /api/system
  • @@ -203,17 +202,13 @@ def serve_next_static(filename): else: appimage_root = os.path.dirname(base_dir) - static_paths = [ - os.path.join(appimage_root, 'web', '_next'), # Ruta principal - os.path.join(appimage_root, 'usr', 'web', '_next'), # Fallback con usr/ - os.path.join(appimage_root, 'web', 'out', '_next'), # Fallback con out/ - os.path.join(appimage_root, 'usr', 'web', 'out', '_next'), # Fallback con usr/out/ - ] + static_dir = os.path.join(appimage_root, 'web', '_next') + file_path = os.path.join(static_dir, filename) - for static_dir in static_paths: - file_path = os.path.join(static_dir, filename) - if os.path.exists(file_path): - return send_file(file_path) + if os.path.exists(file_path): + return send_file(file_path) + + print(f"[v0] ❌ Next.js static file not found: {file_path}") return '', 404 except Exception as e: print(f"Error serving Next.js static file {filename}: {e}") @@ -231,18 +226,12 @@ def serve_static_files(filename): else: appimage_root = os.path.dirname(base_dir) - public_paths = [ - os.path.join(appimage_root, 'web'), # Raíz web para exportación estática - os.path.join(appimage_root, 'usr', 'web'), # Fallback con usr/ - os.path.join(appimage_root, 'web', 'out'), # Fallback con out/ - os.path.join(appimage_root, 'usr', 'web', 'out'), # Fallback con usr/out/ - ] + web_dir = os.path.join(appimage_root, 'web') + file_path = os.path.join(web_dir, filename) + + if os.path.exists(file_path): + return send_from_directory(web_dir, filename) - for public_dir in public_paths: - file_path = os.path.join(public_dir, filename) - if os.path.exists(file_path): - return send_from_directory(public_dir, filename) - return '', 404 except Exception as e: print(f"Error serving static file {filename}: {e}") @@ -260,26 +249,17 @@ def serve_images(filename): else: appimage_root = os.path.dirname(base_dir) - image_paths = [ - os.path.join(appimage_root, 'web', 'images'), # Ruta principal para exportación estática - os.path.join(appimage_root, 'usr', 'web', 'images'), # Fallback con usr/ - os.path.join(appimage_root, 'web', 'public', 'images'), # Ruta con public/ - os.path.join(appimage_root, 'usr', 'web', 'public', 'images'), # Fallback usr/public/ - os.path.join(appimage_root, 'public', 'images'), # Ruta directa a public - os.path.join(appimage_root, 'usr', 'public', 'images'), # Fallback usr/public - ] + image_dir = os.path.join(appimage_root, 'web', 'images') + file_path = os.path.join(image_dir, filename) + abs_path = os.path.abspath(file_path) - print(f"[v0] Looking for image: {filename}") - for image_dir in image_paths: - file_path = os.path.join(image_dir, filename) - abs_path = os.path.abspath(file_path) - exists = os.path.exists(abs_path) - print(f"[v0] Checking: {abs_path} - {'FOUND' if exists else 'NOT FOUND'}") - if exists: - print(f"[v0] Serving image from: {abs_path}") - return send_from_directory(image_dir, filename) + print(f"[v0] Looking for image: {filename} at {abs_path}") - print(f"[v0] Image not found: {filename}") + if os.path.exists(abs_path): + print(f"[v0] ✅ Serving image from: {abs_path}") + return send_from_directory(image_dir, filename) + + print(f"[v0] ❌ Image not found: {abs_path}") return '', 404 except Exception as e: print(f"Error serving image {filename}: {e}")