diff --git a/app/Filament/Resources/InfrastructureActionLogs/InfrastructureActionLogResource.php b/app/Filament/Resources/InfrastructureActionLogs/InfrastructureActionLogResource.php index 5a212bf..9c2e8d9 100644 --- a/app/Filament/Resources/InfrastructureActionLogs/InfrastructureActionLogResource.php +++ b/app/Filament/Resources/InfrastructureActionLogs/InfrastructureActionLogResource.php @@ -5,6 +5,7 @@ namespace App\Filament\Resources\InfrastructureActionLogs; use App\Filament\Resources\InfrastructureActionLogs\Pages\ManageInfrastructureActionLogs; use App\Models\InfrastructureActionLog; use BackedEnum; +use Filament\Actions\ViewAction; use Filament\Resources\Resource; use Filament\Support\Icons\Heroicon; use Filament\Tables; @@ -55,7 +56,7 @@ class InfrastructureActionLogResource extends Resource // ]) ->recordActions([ - Tables\Actions\ViewAction::make(), + ViewAction::make(), ]) ->toolbarActions([ // diff --git a/app/Filament/SuperAdmin/Pages/LogViewerPage.php b/app/Filament/SuperAdmin/Pages/LogViewerPage.php new file mode 100644 index 0000000..ed59dc8 --- /dev/null +++ b/app/Filament/SuperAdmin/Pages/LogViewerPage.php @@ -0,0 +1,157 @@ +logFiles = $this->discoverLogFiles(); + $this->selectedFile = $this->logFiles[0]['name'] ?? ''; + $this->refreshLines(); + } + + public function updatedSelectedFile(): void + { + $this->refreshLines(); + } + + public function updatedFilter(): void + { + $this->refreshLines(); + } + + public function updatedLimit(): void + { + $this->limit = max(50, min(1000, $this->limit)); + $this->refreshLines(); + } + + protected function discoverLogFiles(): array + { + $paths = File::glob(storage_path('logs/*.log')) ?: []; + + $files = collect($paths) + ->map(fn (string $path) => [ + 'name' => basename($path), + 'path' => $path, + 'modified' => File::lastModified($path) ?: 0, + 'size' => File::size($path) ?: 0, + ]) + ->sortByDesc('modified') + ->values() + ->all(); + + return $files; + } + + protected function refreshLines(): void + { + $path = storage_path('logs/'.$this->selectedFile); + + if (! File::exists($path)) { + $this->entries = []; + + return; + } + + $rawLines = $this->tailFile($path, $this->limit * 2); + + if ($this->filter !== '') { + $needle = mb_strtolower($this->filter); + $rawLines = array_values(array_filter( + $rawLines, + fn (string $line) => str_contains(mb_strtolower($line), $needle) + )); + } + + $rawLines = array_slice($rawLines, -$this->limit * 2); + $rawLines = $this->mergeStackTraces($rawLines); + $rawLines = array_slice(array_reverse($rawLines), 0, $this->limit); + + $this->entries = array_map([$this, 'formatLine'], $rawLines); + } + + /** + * Return last $lines lines of file. Simpler approach is fine for small logs in admin view. + */ + protected function tailFile(string $path, int $lines = 200): array + { + try { + $contents = File::get($path); + } catch (\Throwable) { + return []; + } + + $all = preg_split('/\r\n|\r|\n/', (string) $contents) ?: []; + + return array_slice($all, -$lines); + } + + protected function mergeStackTraces(array $lines): array + { + $merged = []; + + foreach ($lines as $line) { + $trimmed = ltrim($line); + $isStackLine = str_starts_with($trimmed, '#') + || str_starts_with($trimmed, 'Stack trace:') + || str_starts_with($line, ' '); + + if ($isStackLine && ! empty($merged)) { + $merged[array_key_last($merged)] .= PHP_EOL.$line; + continue; + } + + if ($line === '') { + continue; + } + + $merged[] = $line; + } + + return $merged; + } + + protected function formatLine(string $line): array + { + $level = 'info'; + $timestamp = null; + + if (preg_match('/\\[(?[^\\]]+)\\]/', $line, $matches)) { + $timestamp = $matches['ts'] ?? null; + } + + if (preg_match('/\\.(EMERGENCY|ALERT|CRITICAL|ERROR|WARNING|NOTICE|INFO|DEBUG)/i', $line, $matches)) { + $level = strtolower($matches[1]); + } + + return [ + 'text' => $line, + 'level' => $level, + 'timestamp' => $timestamp, + ]; + } +} diff --git a/app/Filament/Widgets/DokployPlatformHealth.php b/app/Filament/Widgets/DokployPlatformHealth.php index b039ed3..b88cb97 100644 --- a/app/Filament/Widgets/DokployPlatformHealth.php +++ b/app/Filament/Widgets/DokployPlatformHealth.php @@ -47,8 +47,11 @@ class DokployPlatformHealth extends Widget $results[] = [ 'label' => ucfirst($label), 'compose_id' => $composeId, + 'name' => $composeId, 'status' => 'unreachable', 'error' => $exception->getMessage(), + 'services' => [], + 'last_deploy' => null, ]; } } diff --git a/resources/views/filament/super-admin/pages/log-viewer.blade.php b/resources/views/filament/super-admin/pages/log-viewer.blade.php new file mode 100644 index 0000000..4777f76 --- /dev/null +++ b/resources/views/filament/super-admin/pages/log-viewer.blade.php @@ -0,0 +1,63 @@ + + @php($selectedMeta = collect($logFiles)->firstWhere('name', $selectedFile)) + +
+
+
+ + +

Zuletzt aktualisiert: {{ $selectedMeta ? \Carbon\Carbon::createFromTimestamp($selectedMeta['modified'])->diffForHumans() : '—' }}

+
+
+ + +

Einfache Teilstring-Suche. Kein Regex.

+
+
+ + +

Letzte Zeilen (nach Filter).

+
+
+ +
+
+ {{ $selectedFile ?: 'Keine Datei' }} + {{ $filter ? 'Gefiltert' : 'Ungefiltert' }} · {{ count($entries) }} Zeilen +
+
+
    + @forelse($entries as $entry) +
  • +
    + $entry['level'] === 'error' || $entry['level'] === 'critical', + 'bg-amber-100 text-amber-900 dark:bg-amber-500/20 dark:text-amber-100' => $entry['level'] === 'warning', + 'bg-blue-100 text-blue-800 dark:bg-blue-500/20 dark:text-blue-100' => $entry['level'] === 'info', + 'bg-slate-200 text-slate-800 dark:bg-slate-700 dark:text-slate-200' => ! in_array($entry['level'], ['error','critical','warning','info']), + ])> + {{ strtoupper($entry['level']) }} + +
    + @if($entry['timestamp']) +

    {{ $entry['timestamp'] }}

    + @endif +

    {{ $entry['text'] }}

    +
    +
    +
  • + @empty +
  • Keine Logeinträge gefunden.
  • + @endforelse +
+
+
+
+
diff --git a/resources/views/filament/widgets/dokploy-platform-health.blade.php b/resources/views/filament/widgets/dokploy-platform-health.blade.php index 3a66c93..7f0858d 100644 --- a/resources/views/filament/widgets/dokploy-platform-health.blade.php +++ b/resources/views/filament/widgets/dokploy-platform-health.blade.php @@ -6,7 +6,7 @@

{{ $compose['label'] }}

-

{{ $compose['name'] }}

+

{{ $compose['name'] ?? '–' }}

{{ $compose['compose_id'] }}