mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-17 19:16:25 +00:00
Update AppImage
This commit is contained in:
@@ -63,9 +63,14 @@ export function ProxmoxDashboard() {
|
|||||||
const [lastScrollY, setLastScrollY] = useState(0)
|
const [lastScrollY, setLastScrollY] = useState(0)
|
||||||
|
|
||||||
const fetchSystemData = useCallback(async () => {
|
const fetchSystemData = useCallback(async () => {
|
||||||
|
console.log("[v0] Fetching system data from Flask server...")
|
||||||
|
console.log("[v0] Current window location:", window.location.href)
|
||||||
|
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
||||||
const apiUrl = `${baseUrl}/api/system`
|
const apiUrl = `${baseUrl}/api/system`
|
||||||
|
|
||||||
|
console.log("[v0] API URL:", apiUrl)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -74,12 +79,14 @@ export function ProxmoxDashboard() {
|
|||||||
},
|
},
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
})
|
})
|
||||||
|
console.log("[v0] Response status:", response.status)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Server responded with status: ${response.status}`)
|
throw new Error(`Server responded with status: ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: FlaskSystemData = await response.json()
|
const data: FlaskSystemData = await response.json()
|
||||||
|
console.log("[v0] System data received:", data)
|
||||||
|
|
||||||
let status: "healthy" | "warning" | "critical" = "healthy"
|
let status: "healthy" | "warning" | "critical" = "healthy"
|
||||||
if (data.cpu_usage > 90 || data.memory_usage > 90) {
|
if (data.cpu_usage > 90 || data.memory_usage > 90) {
|
||||||
@@ -97,6 +104,13 @@ export function ProxmoxDashboard() {
|
|||||||
})
|
})
|
||||||
setIsServerConnected(true)
|
setIsServerConnected(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("[v0] Failed to fetch system data from Flask server:", error)
|
||||||
|
console.error("[v0] Error details:", {
|
||||||
|
message: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
apiUrl,
|
||||||
|
windowLocation: window.location.href,
|
||||||
|
})
|
||||||
|
|
||||||
setIsServerConnected(false)
|
setIsServerConnected(false)
|
||||||
setSystemStatus((prev) => ({
|
setSystemStatus((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -234,7 +248,9 @@ export function ProxmoxDashboard() {
|
|||||||
|
|
||||||
<header className="border-b border-border bg-card sticky top-0 z-50 shadow-sm">
|
<header className="border-b border-border bg-card sticky top-0 z-50 shadow-sm">
|
||||||
<div className="container mx-auto px-4 md:px-6 py-4 md:py-4">
|
<div className="container mx-auto px-4 md:px-6 py-4 md:py-4">
|
||||||
|
{/* Logo and Title */}
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex items-start justify-between gap-3">
|
||||||
|
{/* Logo and Title */}
|
||||||
<div className="flex items-center space-x-2 md:space-x-3 min-w-0">
|
<div className="flex items-center space-x-2 md:space-x-3 min-w-0">
|
||||||
<div className="w-16 h-16 md:w-10 md:h-10 relative flex items-center justify-center bg-primary/10 flex-shrink-0">
|
<div className="w-16 h-16 md:w-10 md:h-10 relative flex items-center justify-center bg-primary/10 flex-shrink-0">
|
||||||
<Image
|
<Image
|
||||||
@@ -245,6 +261,7 @@ export function ProxmoxDashboard() {
|
|||||||
className="object-contain md:w-10 md:h-10"
|
className="object-contain md:w-10 md:h-10"
|
||||||
priority
|
priority
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
|
console.log("[v0] Logo failed to load, using fallback icon")
|
||||||
const target = e.target as HTMLImageElement
|
const target = e.target as HTMLImageElement
|
||||||
target.style.display = "none"
|
target.style.display = "none"
|
||||||
const fallback = target.parentElement?.querySelector(".fallback-icon")
|
const fallback = target.parentElement?.querySelector(".fallback-icon")
|
||||||
@@ -265,6 +282,7 @@ export function ProxmoxDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Desktop Actions */}
|
||||||
<div className="hidden lg:flex items-center space-x-4">
|
<div className="hidden lg:flex items-center space-x-4">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Server className="h-4 w-4 text-muted-foreground" />
|
<Server className="h-4 w-4 text-muted-foreground" />
|
||||||
@@ -294,6 +312,7 @@ export function ProxmoxDashboard() {
|
|||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Actions */}
|
||||||
<div className="flex lg:hidden items-center gap-2">
|
<div className="flex lg:hidden items-center gap-2">
|
||||||
<Badge variant="outline" className={`${statusColor} text-xs px-2`}>
|
<Badge variant="outline" className={`${statusColor} text-xs px-2`}>
|
||||||
{statusIcon}
|
{statusIcon}
|
||||||
@@ -308,6 +327,7 @@ export function ProxmoxDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Server Info */}
|
||||||
<div className="lg:hidden mt-2 flex items-center justify-end text-xs text-muted-foreground">
|
<div className="lg:hidden mt-2 flex items-center justify-end text-xs text-muted-foreground">
|
||||||
<span className="whitespace-nowrap">Uptime: {systemStatus.uptime}</span>
|
<span className="whitespace-nowrap">Uptime: {systemStatus.uptime}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -109,12 +109,13 @@ const fetchSystemData = async (): Promise<SystemData | null> => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
throw new Error(`Flask server responded with status: ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
return data
|
return data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("[v0] Failed to fetch system data:", error)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,12 +134,13 @@ const fetchVMData = async (): Promise<VMData[]> => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
throw new Error(`Flask server responded with status: ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
return data.vms || []
|
return Array.isArray(data) ? data : data.vms || []
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("[v0] Failed to fetch VM data:", error)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,12 +159,40 @@ const fetchStorageData = async (): Promise<StorageData | null> => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
console.log("[v0] Storage API not available (this is normal if not configured)")
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
return data
|
return data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log("[v0] Storage data unavailable:", error instanceof Error ? error.message : "Unknown error")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchNetworkData = async (): Promise<NetworkData | null> => {
|
||||||
|
try {
|
||||||
|
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
||||||
|
const apiUrl = `${baseUrl}/api/network`
|
||||||
|
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
cache: "no-store",
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.log("[v0] Network API not available (this is normal if not configured)")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
return data
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[v0] Network data unavailable:", error instanceof Error ? error.message : "Unknown error")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,30 +223,6 @@ const fetchProxmoxStorageData = async (): Promise<ProxmoxStorageData | null> =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchNetworkData = async (): Promise<NetworkData | null> => {
|
|
||||||
try {
|
|
||||||
const baseUrl = typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
|
||||||
const apiUrl = `${baseUrl}/api/network`
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
cache: "no-store",
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json()
|
|
||||||
return data
|
|
||||||
} catch (error) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SystemOverview() {
|
export function SystemOverview() {
|
||||||
const [systemData, setSystemData] = useState<SystemData | null>(null)
|
const [systemData, setSystemData] = useState<SystemData | null>(null)
|
||||||
const [vmData, setVmData] = useState<VMData[]>([])
|
const [vmData, setVmData] = useState<VMData[]>([])
|
||||||
|
|||||||
@@ -1087,7 +1087,7 @@ def get_smart_data(disk_name):
|
|||||||
|
|
||||||
smart_data['media_wearout_indicator'] = wear_used
|
smart_data['media_wearout_indicator'] = wear_used
|
||||||
smart_data['ssd_life_left'] = max(0, 100 - wear_used)
|
smart_data['ssd_life_left'] = max(0, 100 - wear_used)
|
||||||
print(f"[v0] Media Wearout Indicator (ID 230): {smart_data['media_wearout_indicator']}% used, {smart_data['ssd_life_left']}% life left")
|
print(f"[v0] Media Wearout Indicator (ID 230): {wear_used}% used, {smart_data['ssd_life_left']}% life left")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[v0] Error parsing Media_Wearout_Indicator (ID 230): {e}")
|
print(f"[v0] Error parsing Media_Wearout_Indicator (ID 230): {e}")
|
||||||
elif attr_id == '233': # Media_Wearout_Indicator (Intel/Samsung SSD)
|
elif attr_id == '233': # Media_Wearout_Indicator (Intel/Samsung SSD)
|
||||||
@@ -2806,6 +2806,26 @@ def get_detailed_gpu_info(gpu):
|
|||||||
detailed_info['utilization_memory'] = round(mem_util, 1)
|
detailed_info['utilization_memory'] = round(mem_util, 1)
|
||||||
print(f"[v0] Memory Utilization: {detailed_info['utilization_memory']}%", flush=True)
|
print(f"[v0] Memory Utilization: {detailed_info['utilization_memory']}%", flush=True)
|
||||||
|
|
||||||
|
# Parse GRBM (Graphics Register Bus Manager) for engine utilization
|
||||||
|
if 'GRBM' in device:
|
||||||
|
grbm = device['GRBM']
|
||||||
|
|
||||||
|
# Graphics Pipe (similar to Render/3D)
|
||||||
|
if 'Graphics Pipe' in grbm:
|
||||||
|
gfx_pipe = grbm['Graphics Pipe']
|
||||||
|
if 'value' in gfx_pipe:
|
||||||
|
detailed_info['engine_render'] = f"{gfx_pipe['value']:.1f}%"
|
||||||
|
|
||||||
|
# Parse GRBM2 for additional engine info
|
||||||
|
if 'GRBM2' in device:
|
||||||
|
grbm2 = device['GRBM2']
|
||||||
|
|
||||||
|
# Texture Cache (similar to Blitter)
|
||||||
|
if 'Texture Cache' in grbm2:
|
||||||
|
tex_cache = grbm2['Texture Cache']
|
||||||
|
if 'value' in tex_cache:
|
||||||
|
detailed_info['engine_blitter'] = f"{tex_cache['value']:.1f}%"
|
||||||
|
|
||||||
# Parse processes (fdinfo)
|
# Parse processes (fdinfo)
|
||||||
if 'fdinfo' in device:
|
if 'fdinfo' in device:
|
||||||
fdinfo = device['fdinfo']
|
fdinfo = device['fdinfo']
|
||||||
@@ -3871,11 +3891,14 @@ def api_network_interface_metrics(interface_name):
|
|||||||
print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST EXCEPTION =====")
|
print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST EXCEPTION =====")
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
# ... existing code ...
|
||||||
|
|
||||||
@app.route('/api/vms', methods=['GET'])
|
@app.route('/api/vms', methods=['GET'])
|
||||||
def api_vms():
|
def api_vms():
|
||||||
"""Get virtual machine information"""
|
"""Get virtual machine information"""
|
||||||
return jsonify(get_proxmox_vms())
|
return jsonify(get_proxmox_vms())
|
||||||
|
|
||||||
|
# Add the new api_vm_metrics endpoint here
|
||||||
@app.route('/api/vms/<int:vmid>/metrics', methods=['GET'])
|
@app.route('/api/vms/<int:vmid>/metrics', methods=['GET'])
|
||||||
def api_vm_metrics(vmid):
|
def api_vm_metrics(vmid):
|
||||||
"""Get historical metrics (RRD data) for a specific VM/LXC"""
|
"""Get historical metrics (RRD data) for a specific VM/LXC"""
|
||||||
|
|||||||
Reference in New Issue
Block a user