Files
fotospiel-app/app/Filament/Widgets/StorageCapacityWidget.php
Codex Agent 5817270c35 Admin Menü neu geordnet.
Introduced a two-tier media pipeline with dynamic disks, asset tracking, admin controls, and alerting around
  upload/archival workflows.
  - Added storage metadata + asset tables and models so every photo/variant knows where it lives
 (database/migrations/2025_10_20_090000_create_media_storage_targets_table.php, database/  migrations/2025_10_20_090200_create_event_media_assets_table.php, app/Models/MediaStorageTarget.php:1, app/
    Models/EventMediaAsset.php:1, app/Models/EventStorageAssignment.php:1, app/Models/Event.php:27).
  - Rewired guest and tenant uploads to pick the event’s hot disk, persist EventMediaAsset records, compute
    checksums, and clean up on delete (app/Http/Controllers/Api/EventPublicController.php:243, app/Http/
Controllers/Api/Tenant/PhotoController.php:25, app/Models/Photo.php:25).
  - Implemented storage services, archival job scaffolding, monitoring config, and queue-failure notifications for upload issues (app/Services/Storage/EventStorageManager.php:16, app/Services/Storage/
    StorageHealthService.php:9, app/Jobs/ArchiveEventMediaAssets.php:16, app/Providers/AppServiceProvider.php:39, app/Notifications/UploadPipelineFailed.php:8, config/storage-monitor.php:1).
  - Seeded default hot/cold targets and exposed super-admin tooling via a Filament resource and capacity widget (database/seeders/MediaStorageTargetSeeder.php:13, database/seeders/DatabaseSeeder.php:17, app/Filament/Resources/MediaStorageTargetResource.php:1, app/Filament/Widgets/StorageCapacityWidget.php:12, app/Providers/Filament/SuperAdminPanelProvider.php:47).
- Dropped cron skeletons and artisan placeholders to schedule storage monitoring, archival dispatch, and upload queue health checks (cron/storage_monitor.sh, cron/archive_dispatcher.sh, cron/upload_queue_health.sh, routes/console.php:9).
2025-10-17 22:26:13 +02:00

77 lines
2.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Filament\Widgets;
use App\Models\MediaStorageTarget;
use App\Services\Storage\StorageHealthService;
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\StatsOverviewWidget\Card;
class StorageCapacityWidget extends StatsOverviewWidget
{
protected static ?int $sort = 1;
protected function getCards(): array
{
$health = app(StorageHealthService::class);
return MediaStorageTarget::all()
->map(function (MediaStorageTarget $target) use ($health) {
$stats = $health->getCapacity($target);
if ($stats['status'] !== 'ok') {
return Card::make($target->name, 'Kapazität unbekannt')
->description(match ($stats['status']) {
'unavailable' => 'Monitoring nicht verfügbar',
'unknown' => 'Monitor-Pfad nicht gesetzt',
'error' => $stats['message'] ?? 'Fehler beim Auslesen',
default => 'Status unbekannt',
})
->descriptionIcon('heroicon-m-question-mark-circle')
->color('warning');
}
$used = $this->formatBytes($stats['used']);
$total = $this->formatBytes($stats['total']);
$free = $this->formatBytes($stats['free']);
$percentageValue = $stats['percentage'];
$percent = $percentageValue !== null ? $percentageValue.' %' : '';
$color = 'success';
if ($percentageValue === null) {
$color = 'warning';
} elseif ($percentageValue >= 80) {
$color = 'danger';
} elseif ($percentageValue >= 60) {
$color = 'warning';
}
return Card::make($target->name, "$used / $total")
->description("Frei: $free · Auslastung: $percent")
->color($color)
->extraAttributes([
'data-storage-disk' => $target->key,
]);
})
->toArray();
}
private function formatBytes(?int $bytes): string
{
if ($bytes === null) {
return '';
}
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$index = 0;
$value = (float) $bytes;
while ($value >= 1024 && $index < count($units) - 1) {
$value /= 1024;
$index++;
}
return round($value, 1).' '.$units[$index];
}
}