Infrastructure
Forge Disk Filesystems code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/forge-disk-filesystems.blade.php
@props([
'output' => null,
'rows' => [],
'title' => 'Disk filesystems',
'description' => null,
'command' => 'df -h',
'capturedAt' => null,
'host' => null,
'limit' => null,
'emptyTitle' => 'No filesystem output',
'emptyDescription' => 'Capture df output and pass it to the component to inspect mounted filesystems.',
])
@php
/*
| Filesystem usage viewer for `df -h` output. Accepts either the raw command
| output or normalized row arrays. Shell execution stays with the caller.
|
| Usage:
| <x-ui.forge-disk-filesystems :output="$dfOutput" host="web-01.production" captured-at="14:28 UTC" />
*/
$parseDfOutput = function (?string $rawOutput): array {
if (! is_string($rawOutput) || trim($rawOutput) === '') {
return [];
}
$filesystems = [];
foreach (preg_split('/\R/', trim($rawOutput)) ?: [] as $index => $line) {
$line = trim($line);
if ($line === '' || ($index === 0 && str_starts_with($line, 'Filesystem'))) {
continue;
}
if (! preg_match('/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)%\s+(.+)$/', $line, $matches)) {
continue;
}
$filesystems[] = [
'filesystem' => $matches[1],
'size' => $matches[2],
'used' => $matches[3],
'avail' => $matches[4],
'percent' => (int) $matches[5],
'mount' => $matches[6],
];
}
return $filesystems;
};
$normalizeFilesystem = function (array $filesystem): array {
$percent = $filesystem['percent'] ?? $filesystem['usePercent'] ?? $filesystem['use_percent'] ?? null;
if (is_string($percent)) {
$percent = (int) rtrim($percent, '%');
}
return [
'filesystem' => (string) ($filesystem['filesystem'] ?? $filesystem['Filesystem'] ?? ''),
'size' => (string) ($filesystem['size'] ?? $filesystem['Size'] ?? ''),
'used' => (string) ($filesystem['used'] ?? $filesystem['Used'] ?? ''),
'avail' => (string) ($filesystem['avail'] ?? $filesystem['available'] ?? $filesystem['Avail'] ?? ''),
'percent' => is_numeric($percent) ? max(0, min(100, (int) $percent)) : null,
'mount' => (string) ($filesystem['mount'] ?? $filesystem['mountedOn'] ?? $filesystem['Mounted on'] ?? ''),
];
};
$filesystems = collect(! empty($rows) ? $rows : $parseDfOutput($output))
->filter(fn ($filesystem) => is_array($filesystem))
->map($normalizeFilesystem)
->filter(fn (array $filesystem): bool => filled($filesystem['mount']) || filled($filesystem['filesystem']))
->when(is_numeric($limit) && (int) $limit > 0, fn ($items) => $items->take((int) $limit))
->values();
$usageBadgeVariant = function (?int $percent): string {
return match (true) {
$percent === null => 'gray',
$percent >= 90 => 'red',
$percent >= 75 => 'amber',
$percent === 0 => 'gray',
default => 'green',
};
};
$usageBarClass = function (?int $percent): string {
return match (true) {
$percent === null => 'bg-brand-500 dark:bg-brand-400',
$percent >= 90 => 'bg-red-500 dark:bg-red-400',
$percent >= 75 => 'bg-amber-500 dark:bg-amber-400',
default => 'bg-brand-500 dark:bg-brand-400',
};
};
$visibleFilesystems = $filesystems->count();
$highestUsage = $filesystems->sortByDesc('percent')->first();
$warningCount = $filesystems->filter(fn (array $filesystem): bool => ($filesystem['percent'] ?? 0) >= 75)->count();
@endphp
<section {{ $attributes->merge(['class' => 'min-w-0 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-900']) }}>
<div class="flex flex-col gap-4 px-5 py-4 sm:flex-row sm:items-start sm:justify-between sm:px-6">
<div class="min-w-0">
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">{{ $title }}</h3>
@if ($description)
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ $description }}</p>
@endif
</div>
<div class="flex shrink-0 flex-wrap items-center gap-2">
<x-ui.badge variant="gray" :pill="false" icon="terminal-window" icon-weight="regular">
<span class="font-mono">{{ $command }}</span>
</x-ui.badge>
@if ($capturedAt)
<x-ui.badge variant="gray" :pill="false" icon="clock" icon-weight="regular">{{ $capturedAt }}</x-ui.badge>
@endif
</div>
</div>
@if ($visibleFilesystems > 0)
<div class="grid gap-4 border-b border-gray-200 px-5 py-4 dark:border-gray-800 sm:grid-cols-2 lg:grid-cols-4 sm:px-6">
<div class="min-w-0 rounded-lg bg-gray-50 p-3 dark:bg-gray-950/40">
<p class="text-xs text-gray-500 dark:text-gray-400">Filesystems</p>
<p class="mt-1 text-lg font-semibold tabular-nums text-gray-900 dark:text-white">{{ number_format($visibleFilesystems) }}</p>
</div>
<div class="min-w-0 rounded-lg bg-gray-50 p-3 dark:bg-gray-950/40">
<p class="text-xs text-gray-500 dark:text-gray-400">Highest usage</p>
<p class="mt-1 text-lg font-semibold tabular-nums text-gray-900 dark:text-white">
{{ $highestUsage['percent'] ?? '—' }}@if (isset($highestUsage['percent']))%@endif
</p>
</div>
<div class="min-w-0 rounded-lg bg-gray-50 p-3 dark:bg-gray-950/40">
<p class="text-xs text-gray-500 dark:text-gray-400">Over 75% full</p>
<p class="mt-1 text-lg font-semibold tabular-nums text-gray-900 dark:text-white">{{ number_format($warningCount) }}</p>
</div>
<div class="min-w-0 rounded-lg bg-gray-50 p-3 dark:bg-gray-950/40">
<p class="text-xs text-gray-500 dark:text-gray-400">{{ $host ? 'Host' : 'Busiest mount' }}</p>
<p class="mt-1 truncate font-mono text-sm font-semibold text-gray-900 dark:text-white" title="{{ $host ?: ($highestUsage['mount'] ?? '') }}">
{{ $host ?: ($highestUsage['mount'] ?? 'Unknown') }}
</p>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full min-w-[48rem] table-fixed divide-y divide-gray-200 text-sm dark:divide-gray-800">
<caption class="sr-only">{{ $title }} from {{ $command }}</caption>
<thead class="bg-gray-50 text-xs text-gray-500 dark:bg-gray-950/40 dark:text-gray-400">
<tr>
<th scope="col" class="w-full px-5 py-3 text-left font-medium sm:px-6">Mounted on</th>
<th scope="col" class="w-28 px-5 py-3 text-right font-medium whitespace-nowrap sm:px-6">Size</th>
<th scope="col" class="w-28 px-5 py-3 text-right font-medium whitespace-nowrap sm:px-6">Used</th>
<th scope="col" class="hidden w-28 px-5 py-3 text-right font-medium whitespace-nowrap md:table-cell sm:px-6">Avail</th>
<th scope="col" class="w-40 px-5 py-3 text-right font-medium whitespace-nowrap sm:px-6">Use%</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-800">
@foreach ($filesystems as $filesystem)
@php
$percent = $filesystem['percent'];
$barWidth = $percent === null ? null : max(1, min(100, $percent));
@endphp
<tr class="transition-colors hover:bg-gray-50 dark:hover:bg-gray-800/40">
<td class="max-w-0 px-5 py-3 sm:px-6">
<p class="truncate font-mono text-sm font-medium text-gray-900 dark:text-white" title="{{ $filesystem['mount'] }}">
{{ $filesystem['mount'] }}
</p>
@if ($filesystem['filesystem'])
<p class="mt-0.5 truncate font-mono text-xs text-gray-500 dark:text-gray-400" title="{{ $filesystem['filesystem'] }}">
{{ $filesystem['filesystem'] }}
</p>
@endif
</td>
<td class="px-5 py-3 text-right font-medium tabular-nums text-gray-900 dark:text-white sm:px-6">{{ $filesystem['size'] ?: '—' }}</td>
<td class="px-5 py-3 text-right font-medium tabular-nums text-gray-900 dark:text-white sm:px-6">{{ $filesystem['used'] ?: '—' }}</td>
<td class="hidden px-5 py-3 text-right tabular-nums text-gray-500 dark:text-gray-400 md:table-cell sm:px-6">{{ $filesystem['avail'] ?: '—' }}</td>
<td class="px-5 py-3 sm:px-6">
<div class="ml-auto flex w-full max-w-36 items-center justify-end gap-2">
@if ($barWidth !== null)
<div class="hidden h-1.5 w-14 overflow-hidden rounded-full bg-gray-100 dark:bg-gray-800 sm:block" role="presentation">
<div class="h-full rounded-full {{ $usageBarClass($percent) }}" style="width: {{ $barWidth }}%"></div>
</div>
<x-ui.badge :variant="$usageBadgeVariant($percent)" size="sm" :pill="false" class="tabular-nums" aria-label="{{ $percent }} percent disk used">{{ $percent }}%</x-ui.badge>
@else
<span class="text-gray-500 dark:text-gray-400">—</span>
@endif
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<x-ui.empty-state :title="$emptyTitle" :description="$emptyDescription" icon="hard-drive" class="rounded-none border-0 shadow-none" />
@endif
</section>