mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-18 03:26:17 +00:00
Update AppImage
This commit is contained in:
@@ -215,15 +215,15 @@ export function NetworkMetrics() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-muted-foreground hidden lg:inline">Received:</span>
|
<span className="text-sm text-muted-foreground">Received:</span>
|
||||||
<span className="text-base lg:text-xl font-bold text-green-500">↓ {trafficInFormatted}</span>
|
<span className="text-base lg:text-xl font-bold text-green-500">↓ {trafficInFormatted}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-muted-foreground hidden lg:inline">Sent:</span>
|
<span className="text-sm text-muted-foreground">Sent:</span>
|
||||||
<span className="text-base lg:text-xl font-bold text-blue-500">↑ {trafficOutFormatted}</span>
|
<span className="text-base lg:text-xl font-bold text-blue-500">↑ {trafficOutFormatted}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-2 hidden lg:block">Total data transferred</p>
|
<p className="text-xs text-muted-foreground mt-2">Total data transferred</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@@ -250,91 +250,130 @@ export function NetworkMetrics() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Firewall Status card */}
|
||||||
<Card className="bg-card border-border">
|
<Card className="bg-card border-border">
|
||||||
<CardHeader>
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-foreground flex items-center">
|
<CardTitle className="text-sm font-medium text-muted-foreground">Firewall Status</CardTitle>
|
||||||
<Router className="h-5 w-5 mr-2" />
|
<Router className="h-4 w-4 text-muted-foreground" />
|
||||||
Physical Interfaces
|
|
||||||
<Badge variant="outline" className="ml-3 bg-blue-500/10 text-blue-500 border-blue-500/20">
|
|
||||||
{networkData.physical_active_count ?? 0}/{networkData.physical_total_count ?? 0} Active
|
|
||||||
</Badge>
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-4">
|
<div className="text-xl lg:text-2xl font-bold text-green-500">Active</div>
|
||||||
{networkData.physical_interfaces.map((interface_, index) => {
|
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20 mt-2">
|
||||||
const typeBadge = getInterfaceTypeBadge(interface_.type)
|
Protected
|
||||||
|
</Badge>
|
||||||
|
<p className="text-xs text-muted-foreground mt-2">System protected</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
return (
|
{/* Network Health card */}
|
||||||
<div
|
<Card className="bg-card border-border">
|
||||||
key={index}
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
className="flex flex-col gap-3 p-4 rounded-lg border border-white/10 bg-white/5 sm:bg-card sm:hover:bg-white/5 transition-colors cursor-pointer"
|
<CardTitle className="text-sm font-medium text-muted-foreground">Network Health</CardTitle>
|
||||||
onClick={() => setSelectedInterface(interface_)}
|
<Activity className="h-4 w-4 text-muted-foreground" />
|
||||||
>
|
</CardHeader>
|
||||||
{/* First row: Icon, Name, Type Badge, Status */}
|
<CardContent>
|
||||||
<div className="flex items-center gap-3 flex-wrap">
|
<Badge variant="outline" className={healthColor}>
|
||||||
<Wifi className="h-5 w-5 text-muted-foreground flex-shrink-0" />
|
{healthStatus}
|
||||||
<div className="flex items-center gap-2 min-w-0 flex-1 flex-wrap">
|
</Badge>
|
||||||
<div className="font-medium text-foreground">{interface_.name}</div>
|
<div className="flex flex-col gap-1 mt-2 text-xs">
|
||||||
<Badge variant="outline" className={typeBadge.color}>
|
<div className="flex items-center justify-between">
|
||||||
{typeBadge.label}
|
<span className="text-muted-foreground">Packet Loss:</span>
|
||||||
</Badge>
|
<span className="font-medium text-foreground">{avgPacketLoss}%</span>
|
||||||
</div>
|
</div>
|
||||||
<Badge
|
<div className="flex items-center justify-between">
|
||||||
variant="outline"
|
<span className="text-muted-foreground">Errors:</span>
|
||||||
className={
|
<span className="font-medium text-foreground">{totalErrors}</span>
|
||||||
interface_.status === "up"
|
</div>
|
||||||
? "bg-green-500/10 text-green-500 border-green-500/20"
|
|
||||||
: "bg-red-500/10 text-red-500 border-red-500/20"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{interface_.status.toUpperCase()}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Second row: Details - Responsive layout */}
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
|
||||||
<div>
|
|
||||||
<div className="text-muted-foreground text-xs">IP Address</div>
|
|
||||||
<div className="font-medium text-foreground font-mono text-sm truncate">
|
|
||||||
{interface_.addresses.length > 0 ? interface_.addresses[0].ip : "N/A"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className="text-muted-foreground text-xs">Speed</div>
|
|
||||||
<div className="font-medium text-foreground flex items-center gap-1 text-xs">
|
|
||||||
<Zap className="h-3 w-3" />
|
|
||||||
{formatSpeed(interface_.speed)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2 md:col-span-1">
|
|
||||||
<div className="text-muted-foreground text-xs">Traffic</div>
|
|
||||||
<div className="font-medium text-foreground text-xs">
|
|
||||||
<span className="text-green-500">↓ {formatBytes(interface_.bytes_recv)}</span>
|
|
||||||
{" / "}
|
|
||||||
<span className="text-blue-500">↑ {formatBytes(interface_.bytes_sent)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{interface_.mac_address && (
|
|
||||||
<div className="col-span-2 md:col-span-1">
|
|
||||||
<div className="text-muted-foreground text-xs">MAC</div>
|
|
||||||
<div className="font-medium text-foreground font-mono text-xs truncate">
|
|
||||||
{interface_.mac_address}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Physical Interfaces section */}
|
||||||
|
<Card className="bg-card border-border">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-foreground flex items-center">
|
||||||
|
<Router className="h-5 w-5 mr-2" />
|
||||||
|
Physical Interfaces
|
||||||
|
<Badge variant="outline" className="ml-3 bg-blue-500/10 text-blue-500 border-blue-500/20">
|
||||||
|
{networkData.physical_active_count ?? 0}/{networkData.physical_total_count ?? 0} Active
|
||||||
|
</Badge>
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{networkData.physical_interfaces.map((interface_, index) => {
|
||||||
|
const typeBadge = getInterfaceTypeBadge(interface_.type)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex flex-col gap-3 p-4 rounded-lg border border-white/10 bg-white/5 sm:bg-card sm:hover:bg-white/5 transition-colors cursor-pointer"
|
||||||
|
onClick={() => setSelectedInterface(interface_)}
|
||||||
|
>
|
||||||
|
{/* First row: Icon, Name, Type Badge, Status */}
|
||||||
|
<div className="flex items-center gap-3 flex-wrap">
|
||||||
|
<Wifi className="h-5 w-5 text-muted-foreground flex-shrink-0" />
|
||||||
|
<div className="flex items-center gap-2 min-w-0 flex-1 flex-wrap">
|
||||||
|
<div className="font-medium text-foreground">{interface_.name}</div>
|
||||||
|
<Badge variant="outline" className={typeBadge.color}>
|
||||||
|
{typeBadge.label}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className={
|
||||||
|
interface_.status === "up"
|
||||||
|
? "bg-green-500/10 text-green-500 border-green-500/20"
|
||||||
|
: "bg-red-500/10 text-red-500 border-red-500/20"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{interface_.status.toUpperCase()}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Second row: Details - Responsive layout */}
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<div className="text-muted-foreground text-xs">IP Address</div>
|
||||||
|
<div className="font-medium text-foreground font-mono text-sm truncate">
|
||||||
|
{interface_.addresses.length > 0 ? interface_.addresses[0].ip : "N/A"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="text-muted-foreground text-xs">Speed</div>
|
||||||
|
<div className="font-medium text-foreground flex items-center gap-1 text-xs">
|
||||||
|
<Zap className="h-3 w-3" />
|
||||||
|
{formatSpeed(interface_.speed)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-span-2 md:col-span-1">
|
||||||
|
<div className="text-muted-foreground text-xs">Traffic</div>
|
||||||
|
<div className="font-medium text-foreground text-xs">
|
||||||
|
<span className="text-green-500">↓ {formatBytes(interface_.bytes_recv)}</span>
|
||||||
|
{" / "}
|
||||||
|
<span className="text-blue-500">↑ {formatBytes(interface_.bytes_sent)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{interface_.mac_address && (
|
||||||
|
<div className="col-span-2 md:col-span-1">
|
||||||
|
<div className="text-muted-foreground text-xs">MAC</div>
|
||||||
|
<div className="font-medium text-foreground font-mono text-xs truncate">
|
||||||
|
{interface_.mac_address}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{networkData.bridge_interfaces && networkData.bridge_interfaces.length > 0 && (
|
{networkData.bridge_interfaces && networkData.bridge_interfaces.length > 0 && (
|
||||||
<Card className="bg-card border-border">
|
<Card className="bg-card border-border">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
@@ -43,14 +43,22 @@ export function NodeMetricsCharts() {
|
|||||||
typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
||||||
const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}`
|
const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}`
|
||||||
|
|
||||||
|
console.log("[v0] Fetching node metrics from:", apiUrl)
|
||||||
|
|
||||||
const response = await fetch(apiUrl)
|
const response = await fetch(apiUrl)
|
||||||
|
|
||||||
|
console.log("[v0] Response status:", response.status)
|
||||||
|
console.log("[v0] Response ok:", response.ok)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json()
|
const errorData = await response.json()
|
||||||
|
console.log("[v0] Error response data:", errorData)
|
||||||
throw new Error(errorData.error || "Failed to fetch node metrics")
|
throw new Error(errorData.error || "Failed to fetch node metrics")
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
|
console.log("[v0] Node metrics result:", result)
|
||||||
|
console.log("[v0] Data points received:", result.data?.length || 0)
|
||||||
|
|
||||||
const transformedData = result.data.map((item: any) => {
|
const transformedData = result.data.map((item: any) => {
|
||||||
const date = new Date(item.time * 1000)
|
const date = new Date(item.time * 1000)
|
||||||
@@ -94,9 +102,14 @@ export function NodeMetricsCharts() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log("[v0] Transformed data sample:", transformedData[0])
|
||||||
|
console.log("[v0] Total transformed data points:", transformedData.length)
|
||||||
|
|
||||||
setData(transformedData)
|
setData(transformedData)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("[v0] Error fetching node metrics:", err)
|
console.error("[v0] Error fetching node metrics:", err)
|
||||||
|
console.error("[v0] Error message:", err.message)
|
||||||
|
console.error("[v0] Error stack:", err.stack)
|
||||||
setError(err.message || "Error loading metrics")
|
setError(err.message || "Error loading metrics")
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|||||||
@@ -427,14 +427,14 @@ export function StorageOverview() {
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Storage Summary */}
|
{/* Storage Summary */}
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 lg:gap-6">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium">Total Storage</CardTitle>
|
<CardTitle className="text-sm font-medium">Total Storage</CardTitle>
|
||||||
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">{storageData.total.toFixed(1)} TB</div>
|
<div className="text-xl lg:text-2xl font-bold">{storageData.total.toFixed(1)} TB</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">{storageData.disk_count} physical disks</p>
|
<p className="text-xs text-muted-foreground mt-1">{storageData.disk_count} physical disks</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -445,7 +445,7 @@ export function StorageOverview() {
|
|||||||
<Database className="h-4 w-4 text-muted-foreground" />
|
<Database className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">{formatStorage(totalProxmoxUsed)}</div>
|
<div className="text-xl lg:text-2xl font-bold">{formatStorage(totalProxmoxUsed)}</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">{usagePercent}% used</p>
|
<p className="text-xs text-muted-foreground mt-1">{usagePercent}% used</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -457,7 +457,7 @@ export function StorageOverview() {
|
|||||||
<CheckCircle2 className="h-4 w-4 text-muted-foreground" />
|
<CheckCircle2 className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">{storageData.disk_count} disks</div>
|
<div className="text-xl lg:text-2xl font-bold">{storageData.disk_count} disks</div>
|
||||||
<p className="text-xs mt-1">
|
<p className="text-xs mt-1">
|
||||||
<span className="text-green-500">{diskHealthBreakdown.normal} normal</span>
|
<span className="text-green-500">{diskHealthBreakdown.normal} normal</span>
|
||||||
{diskHealthBreakdown.warning > 0 && (
|
{diskHealthBreakdown.warning > 0 && (
|
||||||
@@ -483,7 +483,7 @@ export function StorageOverview() {
|
|||||||
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">{storageData.disk_count} disks</div>
|
<div className="text-xl lg:text-2xl font-bold">{storageData.disk_count} disks</div>
|
||||||
<p className="text-xs mt-1">
|
<p className="text-xs mt-1">
|
||||||
{diskTypesBreakdown.nvme > 0 && <span className="text-purple-500">{diskTypesBreakdown.nvme} NVMe</span>}
|
{diskTypesBreakdown.nvme > 0 && <span className="text-purple-500">{diskTypesBreakdown.nvme} NVMe</span>}
|
||||||
{diskTypesBreakdown.ssd > 0 && (
|
{diskTypesBreakdown.ssd > 0 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user