Feature: Disk Usage via df im Dashboard (LXC-kompatibel)
- get_disk_usage() in system_info.py via /usr/bin/df -P - GET /api/system/disk-usage Endpoint - getDiskUsage() im API-Client - Dashboard zeigt Disk Usage Karten mit Balken + Total/Used/Free (sichtbar auf LXC wo /proc/diskstats keine Blockgeräte liefert) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+50
-1
@@ -24,6 +24,7 @@ export default function Dashboard() {
|
||||
const [networkInfo, setNetworkInfo] = useState<any>(null)
|
||||
const [networkTraffic, setNetworkTraffic] = useState<any>(null)
|
||||
const [diskIO, setDiskIO] = useState<any>(null)
|
||||
const [diskUsage, setDiskUsage] = useState<any>(null)
|
||||
|
||||
// History buffers for sparklines (rolling window of 30 points, ~2.5 minutes at 5s intervals)
|
||||
const cpuHistoryRef = useRef<number[]>([])
|
||||
@@ -100,7 +101,7 @@ export default function Dashboard() {
|
||||
|
||||
const loadSystemStats = async () => {
|
||||
try {
|
||||
const [sysInfo, memInfo, cpuData, uptime, network, traffic, diskio] = await Promise.all([
|
||||
const [sysInfo, memInfo, cpuData, uptime, network, traffic, diskio, diskusage] = await Promise.all([
|
||||
api.getSystemInfo().catch(() => null),
|
||||
api.getMemory().catch(() => null),
|
||||
api.getCpuInfo().catch(() => null),
|
||||
@@ -108,6 +109,7 @@ export default function Dashboard() {
|
||||
api.getNetwork().catch(() => null),
|
||||
api.getNetworkTraffic().catch(() => null),
|
||||
api.getDiskIO().catch(() => null),
|
||||
api.getDiskUsage().catch(() => null),
|
||||
])
|
||||
setSystemInfo(sysInfo)
|
||||
setMemoryInfo(memInfo)
|
||||
@@ -116,6 +118,7 @@ export default function Dashboard() {
|
||||
setNetworkInfo(network)
|
||||
setNetworkTraffic(traffic)
|
||||
setDiskIO(diskio)
|
||||
setDiskUsage(diskusage)
|
||||
|
||||
// Add to history
|
||||
if (cpuData?.percent !== undefined) {
|
||||
@@ -524,6 +527,52 @@ export default function Dashboard() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Disk Usage (df-based, immer sichtbar wenn Daten vorhanden) */}
|
||||
{diskUsage?.filesystems && diskUsage.filesystems.length > 0 && (
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl font-semibold mb-4">Disk Usage</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{diskUsage.filesystems.map((fs: any) => {
|
||||
const pct = fs.capacity
|
||||
const barColor = pct > 85 ? "bg-red-500" : pct > 70 ? "bg-yellow-500" : "bg-blue-500"
|
||||
return (
|
||||
<Card key={fs.mountpoint}>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground flex items-center gap-2">
|
||||
<HardDrive className="w-4 h-4" />
|
||||
{fs.mountpoint}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1 h-2 bg-muted rounded-full overflow-hidden">
|
||||
<div className={`h-full ${barColor} rounded-full`} style={{ width: `${pct}%` }} />
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground w-9 text-right">{pct}%</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2 text-sm">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Total</p>
|
||||
<p className="font-medium">{formatBytes(fs.total)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Used</p>
|
||||
<p className="font-medium">{formatBytes(fs.used)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Free</p>
|
||||
<p className="font-medium">{formatBytes(fs.available)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground truncate">{fs.filesystem}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Disk I/O */}
|
||||
{diskIO?.disks && diskIO.disks.length > 0 && (
|
||||
<div className="mb-8">
|
||||
|
||||
Reference in New Issue
Block a user