diff --git a/AppImage/components/storage-overview.tsx b/AppImage/components/storage-overview.tsx index b1acf0d..a7b1182 100644 --- a/AppImage/components/storage-overview.tsx +++ b/AppImage/components/storage-overview.tsx @@ -48,8 +48,24 @@ interface StorageData { error?: string } +interface ProxmoxStorage { + name: string + type: string + status: string + total: number + used: number + available: number + percent: number +} + +interface ProxmoxStorageData { + storage: ProxmoxStorage[] + error?: string +} + export function StorageOverview() { const [storageData, setStorageData] = useState(null) + const [proxmoxStorage, setProxmoxStorage] = useState(null) const [loading, setLoading] = useState(true) const [selectedDisk, setSelectedDisk] = useState(null) const [detailsOpen, setDetailsOpen] = useState(false) @@ -58,10 +74,20 @@ export function StorageOverview() { try { const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : "" - const response = await fetch(`${baseUrl}/api/storage`) - const data = await response.json() + + const [storageResponse, proxmoxResponse] = await Promise.all([ + fetch(`${baseUrl}/api/storage`), + fetch(`${baseUrl}/api/proxmox-storage`), + ]) + + const data = await storageResponse.json() + const proxmoxData = await proxmoxResponse.json() + console.log("[v0] Storage data received:", data) + console.log("[v0] Proxmox storage data received:", proxmoxData) + setStorageData(data) + setProxmoxStorage(proxmoxData) } catch (error) { console.error("Error fetching storage data:", error) } finally { @@ -131,6 +157,18 @@ export function StorageOverview() { setDetailsOpen(true) } + const getStorageTypeBadge = (type: string) => { + const typeColors: Record = { + pbs: "bg-purple-500/10 text-purple-500 border-purple-500/20", + dir: "bg-blue-500/10 text-blue-500 border-blue-500/20", + lvmthin: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20", + zfspool: "bg-green-500/10 text-green-500 border-green-500/20", + nfs: "bg-orange-500/10 text-orange-500 border-orange-500/20", + cifs: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20", + } + return typeColors[type.toLowerCase()] || "bg-gray-500/10 text-gray-500 border-gray-500/20" + } + if (loading) { return (
@@ -215,63 +253,78 @@ export function StorageOverview() {
- {storageData.disks.some((disk) => disk.mountpoint) && ( + {proxmoxStorage && proxmoxStorage.storage && proxmoxStorage.storage.length > 0 && ( - Mounted Partitions + Proxmox Storage
- {storageData.disks - .filter((disk) => disk.mountpoint) - .map((disk) => ( -
-
+ {proxmoxStorage.storage.map((storage) => ( +
+
+
+
-

{disk.mountpoint}

-

- /dev/{disk.name} ({disk.fstype}) +

{storage.name}

+ {storage.type} +
+
+
+ + {storage.status} + + {storage.percent}% +
+
+ +
+ 90 + ? "[&>div]:bg-red-500" + : storage.percent > 75 + ? "[&>div]:bg-yellow-500" + : "[&>div]:bg-blue-500" + }`} + /> +
+
+

Total

+

{storage.total.toLocaleString()} GB

+
+
+

Used

+

90 + ? "text-red-400" + : storage.percent > 75 + ? "text-yellow-400" + : "text-blue-400" + }`} + > + {storage.used.toLocaleString()} GB

- {disk.usage_percent !== undefined && ( - {disk.usage_percent}% - )} -
- {disk.usage_percent !== undefined && ( -
- 90 - ? "[&>div]:bg-red-500" - : disk.usage_percent > 75 - ? "[&>div]:bg-yellow-500" - : "[&>div]:bg-blue-500" - }`} - /> -
- 90 - ? "text-red-400" - : disk.usage_percent > 75 - ? "text-yellow-400" - : "text-blue-400" - } - > - {disk.used} GB used - - - {disk.available} GB free of {disk.total} GB - -
+
+

Available

+

{storage.available.toLocaleString()} GB

- )} +
- ))} +
+ ))}
@@ -384,32 +437,6 @@ export function StorageOverview() {
)}
- - {disk.mountpoint && ( -
-
-
- Mounted at: - {disk.mountpoint} - {disk.fstype && ({disk.fstype})} -
- {disk.usage_percent !== undefined && ( - {disk.usage_percent}% - )} -
- {disk.usage_percent !== undefined && ( -
- -
- {disk.used} GB used - - {disk.available} GB free of {disk.total} GB - -
-
- )} -
- )} ))} @@ -494,46 +521,6 @@ export function StorageOverview() { - - {selectedDisk.mountpoint && ( -
-

Mount Information

-
-
- Mount Point: - {selectedDisk.mountpoint} -
-
- Filesystem: - {selectedDisk.fstype} -
- {selectedDisk.total && ( - <> -
- Total: - {selectedDisk.total} GB -
-
- Used: - {selectedDisk.used} GB -
-
- Available: - {selectedDisk.available} GB -
- {selectedDisk.usage_percent !== undefined && ( -
- -

- {selectedDisk.usage_percent}% used -

-
- )} - - )} -
-
- )} )} diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py index ee6756b..b096a38 100644 --- a/AppImage/scripts/flask_server.py +++ b/AppImage/scripts/flask_server.py @@ -517,10 +517,16 @@ def get_smart_data(disk_name): ['smartctl', '-a', '-j', '-d', 'ata', f'/dev/{disk_name}'], # JSON with ATA device type ['smartctl', '-a', '-j', '-d', 'sat', f'/dev/{disk_name}'], # JSON with SAT device type ['smartctl', '-a', '-j', '-d', 'scsi', f'/dev/{disk_name}'], # JSON with SCSI device type + ['smartctl', '-a', '-j', '-d', 'sat,12', f'/dev/{disk_name}'], # SAT with 12-byte commands + ['smartctl', '-a', '-j', '-d', 'sat,16', f'/dev/{disk_name}'], # SAT with 16-byte commands ['smartctl', '-a', f'/dev/{disk_name}'], # Text output ['smartctl', '-a', '-d', 'ata', f'/dev/{disk_name}'], # Text with ATA device type ['smartctl', '-a', '-d', 'sat', f'/dev/{disk_name}'], # Text with SAT device type + ['smartctl', '-a', '-d', 'sat,12', f'/dev/{disk_name}'], # Text SAT with 12-byte commands + ['smartctl', '-a', '-d', 'sat,16', f'/dev/{disk_name}'], # Text SAT with 16-byte commands ['smartctl', '-i', '-H', f'/dev/{disk_name}'], # Basic info + health only + ['smartctl', '-i', '-H', '-d', 'ata', f'/dev/{disk_name}'], # Basic with ATA + ['smartctl', '-i', '-H', '-d', 'sat', f'/dev/{disk_name}'], # Basic with SAT ] for cmd_index, cmd in enumerate(commands_to_try): @@ -746,6 +752,75 @@ def get_smart_data(disk_name): print(f"[v0] ===== Final SMART data for /dev/{disk_name}: {smart_data} =====") return smart_data +def get_proxmox_storage(): + """Get Proxmox storage information using pvesm status""" + try: + print("[v0] Getting Proxmox storage with pvesm status...") + result = subprocess.run(['pvesm', 'status'], capture_output=True, text=True, timeout=10) + + if result.returncode != 0: + print(f"[v0] pvesm status failed with return code {result.returncode}") + print(f"[v0] stderr: {result.stderr}") + return { + 'error': 'pvesm command not available or failed', + 'storage': [] + } + + storage_list = [] + lines = result.stdout.strip().split('\n') + + # Skip header line + if len(lines) < 2: + print("[v0] No storage found in pvesm output") + return {'storage': []} + + # Parse each storage line + for line in lines[1:]: # Skip header + parts = line.split() + if len(parts) >= 6: + name = parts[0] + storage_type = parts[1] + status = parts[2] + total = int(parts[3]) + used = int(parts[4]) + available = int(parts[5]) + percent = float(parts[6].rstrip('%')) if len(parts) > 6 else 0.0 + + # Convert bytes to GB + total_gb = round(total / (1024**2), 2) + used_gb = round(used / (1024**2), 2) + available_gb = round(available / (1024**2), 2) + + storage_info = { + 'name': name, + 'type': storage_type, + 'status': status, + 'total': total_gb, + 'used': used_gb, + 'available': available_gb, + 'percent': round(percent, 2) + } + + print(f"[v0] Found storage: {name} ({storage_type}) - {used_gb}/{total_gb} GB ({percent}%)") + storage_list.append(storage_info) + + return {'storage': storage_list} + + except FileNotFoundError: + print("[v0] pvesm command not found - Proxmox not installed or not in PATH") + return { + 'error': 'pvesm command not found - Proxmox not installed', + 'storage': [] + } + except Exception as e: + print(f"[v0] Error getting Proxmox storage: {type(e).__name__}: {e}") + import traceback + traceback.print_exc() + return { + 'error': f'Unable to get Proxmox storage: {str(e)}', + 'storage': [] + } + def get_network_info(): """Get network interface information""" try: @@ -833,6 +908,11 @@ def api_storage(): """Get storage information""" return jsonify(get_storage_info()) +@app.route('/api/proxmox-storage', methods=['GET']) +def api_proxmox_storage(): + """Get Proxmox storage information""" + return jsonify(get_proxmox_storage()) + @app.route('/api/network', methods=['GET']) def api_network(): """Get network information""" @@ -949,6 +1029,7 @@ def api_info(): '/api/system', '/api/system-info', '/api/storage', + '/api/proxmox-storage', # Added new endpoint '/api/network', '/api/vms', '/api/logs',