load(). } /** Lazy: pull the full SSH snapshot after the shell renders, behind skeletons. */ public function load(FleetService $fleet): void { try { $snap = $fleet->snapshot($this->server); $id = $snap['identity']; $rootUsed = collect($snap['volumes'])->firstWhere('mount', '/')['used'] ?? $this->server->disk; $this->server->forceFill([ 'cpu' => $snap['metrics']['cpu'], 'mem' => $snap['metrics']['mem'], 'disk' => $rootUsed, 'os' => $id['os'], 'hostname' => $id['hostname'], 'uptime' => $id['uptime'], 'status' => 'online', 'last_seen_at' => now(), 'specs' => [ 'cores' => $id['cores'], 'ram_gb' => $id['ram_gb'], 'disk_gb' => $id['disk_gb'], 'arch' => $id['arch'], 'kernel' => $id['kernel'], 'virt' => $id['virt'], ], ])->save(); $this->volumes = $snap['volumes']; $this->interfaces = $snap['interfaces']; $this->hardening = $snap['hardening']; $this->sshKeys = $snap['sshKeys']; $this->loadAvg = (float) ($snap['metrics']['load'] ?? 0); $this->connected = true; } catch (Throwable) { $this->connected = false; if ($this->server->status !== 'offline') { $this->server->forceFill(['status' => 'offline'])->save(); } } $this->ready = true; } /** Refresh the live gauges from the poller-updated row (wire:poll). */ public function pollMetrics(): void { $this->server->refresh(); } /** * Revoke an SSH key (R5): opens the confirm modal, which writes the * AuditEvent and re-dispatches `keyRemoved`. SSH layer removal lands later. */ public function confirmKeyRemoval(int $index): void { $key = $this->sshKeys[$index] ?? null; if ($key === null) { return; } $fingerprint = $key['fingerprint']; $comment = $key['comment']; $this->dispatch('openModal', component: 'modals.confirm-action', arguments: [ 'heading' => 'SSH-Schlüssel entfernen', 'body' => "Der Schlüssel „{$comment}“ verliert den Zugang zu {$this->server->name}.", 'confirmLabel' => 'Entfernen', 'danger' => true, 'icon' => 'trash', 'auditAction' => 'ssh_key.remove', 'auditTarget' => "{$comment} · {$this->server->name}", 'serverId' => $this->server->id, 'event' => 'keyRemoved', 'params' => ['fingerprint' => $fingerprint], 'notify' => "Schlüssel „{$comment}“ entfernt.", ], ); } /** Applies the confirmed key removal over SSH, then reloads the list. */ #[On('keyRemoved')] public function removeKey(string $fingerprint, FleetService $fleet): void { try { $fleet->removeAuthorizedKey($this->server, $fingerprint); } catch (Throwable $e) { $this->dispatch('notify', message: 'Entfernen fehlgeschlagen: '.$e->getMessage()); return; } $this->reloadKeys($fleet); } /** Re-read the authorized keys after an add/remove. */ #[On('keyChanged')] public function reloadKeys(FleetService $fleet): void { try { $this->sshKeys = $fleet->sshKeys($this->server); } catch (Throwable) { // keep the current list on failure } } /** Credential changed -> reconnect with the new login and re-pull the snapshot. */ #[On('credentialChanged')] public function reloadAfterCredential(FleetService $fleet): void { $this->server->refresh(); $this->ready = false; $this->load($fleet); } /** Open the file manager scoped to this server. */ public function openFiles() { session(['active_server_id' => $this->server->id]); return $this->redirect(route('files.index'), navigate: true); } public function render() { return view('livewire.servers.show'); } }