+
+
+
+
+
+ @if ($generatedPrivate)
+
+
+ Privater Schlüssel — erscheint nur jetzt, sicher speichern.
+
+
+
+ @endif
+
@if ($error)
{{ $error }}
diff --git a/resources/views/livewire/servers/show.blade.php b/resources/views/livewire/servers/show.blade.php
index 4415b9d..d123616 100644
--- a/resources/views/livewire/servers/show.blade.php
+++ b/resources/views/livewire/servers/show.blade.php
@@ -1,170 +1,199 @@
@php
// Threshold tone for gauges/bars: >=90 offline, >=75 warning, else online.
$tone = fn (int $v): string => $v >= 90 ? 'offline' : ($v >= 75 ? 'warning' : 'online');
-
$barColor = ['online' => 'bg-online', 'warning' => 'bg-warning', 'offline' => 'bg-offline'];
-
$statusLabel = ['online' => 'Online', 'warning' => 'Warnung', 'offline' => 'Offline'][$server->status] ?? ucfirst($server->status);
$specs = $server->specs ?? [];
+ $cores = $specs['cores'] ?? null;
+ $ramGb = $specs['ram_gb'] ?? null;
+ $diskGb = $specs['disk_gb'] ?? null;
+ $ramUsed = $ramGb !== null ? round((float) $ramGb * $server->mem / 100, 1) : null;
+ $diskUsed = $diskGb !== null ? (int) round((float) $diskGb * $server->disk / 100) : null;
+
$specRows = [
- ['Kerne', $specs['cores'] ?? '—'],
- ['RAM', isset($specs['ram_gb']) ? $specs['ram_gb'].' GB' : '—'],
- ['Disk', isset($specs['disk_gb']) ? $specs['disk_gb'].' GB' : '—'],
- ['Architektur', $specs['arch'] ?? '—'],
- ['Kernel', $specs['kernel'] ?? '—'],
- ['Virtualisierung', $specs['virt'] ?? '—'],
+ ['Architektur', $specs['arch'] ?? '—'],
+ ['Kernel', $specs['kernel'] ?? '—'],
+ ['Virtualisierung', $specs['virt'] ?? '—'],
+ ['Kerne', $cores ?? '—'],
+ ['RAM', $ramGb !== null ? $ramGb.' GB' : '—'],
+ ['Disk', $diskGb !== null ? $diskGb.' GB' : '—'],
+ ];
+
+ $vitals = [
+ ['label' => 'CPU', 'icon' => 'cpu', 'val' => (int) $server->cpu, 'sub' => ($loadAvg > 0 ? 'Last '.number_format($loadAvg, 2).' · ' : '').($cores ? $cores.' Kerne' : '—')],
+ ['label' => 'RAM', 'icon' => 'activity', 'val' => (int) $server->mem, 'sub' => $ramUsed !== null ? $ramUsed.' / '.$ramGb.' GB' : '—'],
+ ['label' => 'Disk', 'icon' => 'server', 'val' => (int) $server->disk, 'sub' => $diskUsed !== null ? $diskUsed.' / '.$diskGb.' GB' : '—'],
];
@endphp
-
- {{-- Header --}}
-
-
-
- Zurück zur Flotte
-
+
+
+
+ Zurück zur Flotte
+
-
+ {{-- Hero header --}}
+
+
+
$server->status === 'online',
+ 'border-warning/30 bg-warning/10 text-warning' => $server->status === 'warning',
+ 'border-offline/30 bg-offline/10 text-offline' => $server->status === 'offline',
+ ])>
+
+
-
Server
-
{{ $server->name }}
-
+
+
{{ $server->name }}
+ {{ $statusLabel }}
+
+
{{ $server->hostname }}
·
{{ $server->ip }}
·
{{ $server->os }}
·
- Uptime {{ $server->uptime }}
+ Uptime {{ $server->uptime ?: '—' }}
·
- zuletzt gesehen {{ optional($server->last_seen_at)->diffForHumans() ?? '—' }}
+ zuletzt {{ optional($server->last_seen_at)->diffForHumans() ?? '—' }}
-
-
- Zugang
-
-
- Dateien
-
- {{ $statusLabel }}
-
+
+
+
+ Zugang
+
+
+ Dateien
+
@if ($ready && ! $connected)
-
+
-
Keine Live-Verbindung — gezeigt werden die zuletzt gespeicherten Werte. Credential/Erreichbarkeit prüfen.
+
Keine Live-Verbindung — gezeigt werden die zuletzt gespeicherten Werte. Zugang/Erreichbarkeit prüfen.
@endif
- {{-- Auslastung + Spezifikationen --}}
-
-
-
-
-
-
+ {{-- Vitals: live gauges, full width, dense --}}
+
+ @foreach ($vitals as $v)
+
+
+
+
+ {{ $v['label'] }}
+
+
{{ $v['sub'] }}
+
-
-
-
-
- @foreach ($specRows as [$key, $val])
-
-
- {{ $key }}
- - {{ $val }}
-
- @endforeach
-
-
+ @endforeach
@if ($ready)
- {{-- Volumes + Netzwerk-Interfaces --}}
-
-
-
- @foreach ($volumes as $vol)
- @php $vt = $tone((int) $vol['used']); @endphp
-
-
-
{{ $vol['mount'] }}
-
- {{ $vol['fs'] }} · {{ $vol['size'] }}
-
+ {{-- Spezifikationen + Sicherheit --}}
+
+
+
+ @foreach ($specRows as [$key, $val])
+
+
- {{ $key }}
+ - {{ $val }}
-
-
-
+ @endforeach
+
+
+
+
+
+ @foreach ($hardening as $check)
+
$check['status'] === 'online',
+ 'border-warning' => $check['status'] === 'warning',
+ 'border-offline' => $check['status'] === 'offline',
+ ])>
+
+
{{ $check['label'] }}
+
{{ $check['detail'] }}
-
{{ $vol['used'] }}%
+
+ {{ $check['status'] === 'online' ? 'OK' : 'Fehlt' }}
+
-
- @endforeach
-
-
+ @endforeach
+
+
+
-
- {{-- Dense table on desktop; horizontal scroll keeps columns aligned on small screens (R7) --}}
-
-
-
-
- | Name |
- IP |
- MAC |
- RX |
- TX |
-
-
-
- @foreach ($interfaces as $if)
-
- | {{ $if['name'] }} |
- {{ $if['ip'] }} |
- {{ $if['mac'] }} |
- {{ $if['rx'] }} |
- {{ $if['tx'] }} |
+ {{-- Volumes + Netzwerk-Interfaces --}}
+
+
+
+ @forelse ($volumes as $vol)
+ @php $vt = $tone((int) $vol['used']); @endphp
+
+
+
{{ $vol['mount'] }}
+
+ {{ $vol['fs'] }} · {{ $vol['size'] }}
+
+
+
+
+
{{ $vol['used'] }}%
+
+
+ @empty
+
Keine Volumes gelesen.
+ @endforelse
+
+
+
+
+
+
+
+
+ | Name |
+ IP |
+ MAC |
+ RX |
+ TX |
- @endforeach
-
-
-
-
-
-
- {{-- Sicherheit (Hardening) + SSH-Schlüssel --}}
-
-
-
- @foreach ($hardening as $check)
-
-
-
{{ $check['label'] }}
-
{{ $check['detail'] }}
-
-
- {{ $check['status'] === 'online' ? 'OK' : 'Fehlt' }}
-
-
- @endforeach
-
-
+
+
+ @foreach ($interfaces as $if)
+
+ | {{ $if['name'] }} |
+ {{ $if['ip'] }} |
+ {{ $if['mac'] }} |
+ {{ $if['rx'] }} |
+ {{ $if['tx'] }} |
+
+ @endforeach
+
+
+
+
+
+ {{-- SSH-Schlüssel --}}
- {{-- R5: wire to wire-elements/modal --}}
-
- Schlüssel hinzufügen
+ Schlüssel hinzufügen
- @foreach ($sshKeys as $key)
+ @forelse ($sshKeys as $key)
@@ -176,31 +205,37 @@
{{ $key['fingerprint'] }}
- {{-- R5: destructive → wire-elements/modal confirm + audit --}}
- @endforeach
+ @empty
+
Keine autorisierten Schlüssel.
+ @endforelse
-
@else
+ {{-- skeletons for the SSH-loaded sections --}}
-
-
-
-
-
-
+ @foreach (['Spezifikationen', 'Sicherheit'] as $t)
+
+
+
+
+
+ @endforeach
-
-
-
-
-
-
+ @foreach (['Volumes', 'Netzwerk-Interfaces'] as $t)
+
+
+
+
+
+ @endforeach
+
+
+
@endif
diff --git a/resources/views/livewire/services/index.blade.php b/resources/views/livewire/services/index.blade.php
index 80d4389..c3e7cc3 100644
--- a/resources/views/livewire/services/index.blade.php
+++ b/resources/views/livewire/services/index.blade.php
@@ -36,7 +36,7 @@
-
+
@if (! $ready)
@for ($i = 0; $i < 7; $i++)
diff --git a/resources/views/livewire/settings/index.blade.php b/resources/views/livewire/settings/index.blade.php
new file mode 100644
index 0000000..84e72ed
--- /dev/null
+++ b/resources/views/livewire/settings/index.blade.php
@@ -0,0 +1,83 @@
+@php
+ $field = 'h-9 w-full rounded-md border border-line bg-inset px-3 font-sans text-sm text-ink placeholder:text-ink-4 focus:border-accent/40 focus:outline-none';
+ $label = 'mb-1 block font-mono text-[11px] uppercase tracking-wider text-ink-3';
+ $err = 'mt-1 flex items-center gap-1.5 font-mono text-[11px] text-offline';
+@endphp
+
+
+ {{-- Header --}}
+
+
Konto
+
Einstellungen
+
+
+ {{-- Profil --}}
+
+
+
+
+ {{-- Passwort --}}
+
+
+
+
+ {{-- 2FA --}}
+
+
+
+
+
+
{{ $twoFactorEnabled ? '2FA ist aktiv' : '2FA ist nicht eingerichtet' }}
+
{{ $twoFactorEnabled ? 'Der Login erfordert einen TOTP-Code.' : 'Empfohlen — schützt den Login mit einem zweiten Faktor.' }}
+
+
+ @if ($twoFactorEnabled)
+
Deaktivieren
+ @else
+
+ Einrichten
+
+ @endif
+
+
+
diff --git a/routes/web.php b/routes/web.php
index 332a131..7a5b371 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -7,6 +7,7 @@ use App\Livewire\Dashboard;
use App\Livewire\Files;
use App\Livewire\Servers;
use App\Livewire\Services;
+use App\Livewire\Settings;
use Illuminate\Support\Facades\Auth as AuthFacade;
use Illuminate\Support\Facades\Route;
@@ -38,5 +39,7 @@ Route::middleware('auth')->group(function () {
Route::get('/services', Services\Index::class)->name('services.index');
Route::get('/files', Files\Index::class)->name('files.index');
Route::get('/audit', Audit\Index::class)->name('audit.index');
+
+ Route::get('/einstellungen', Settings\Index::class)->name('settings');
});
});