163 lines
5.1 KiB
PHP
163 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Servers;
|
|
|
|
use App\Models\Server;
|
|
use App\Services\FleetService;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\On;
|
|
use Livewire\Attributes\Title;
|
|
use Livewire\Component;
|
|
use Throwable;
|
|
|
|
#[Layout('layouts.app')]
|
|
#[Title('Server-Details — Clusev')]
|
|
class Show extends Component
|
|
{
|
|
/** Route-model-bound by uuid (R11). */
|
|
public Server $server;
|
|
|
|
public bool $connected = false;
|
|
|
|
public bool $ready = false;
|
|
|
|
public array $volumes = [];
|
|
|
|
public array $interfaces = [];
|
|
|
|
public array $hardening = [];
|
|
|
|
public array $sshKeys = [];
|
|
|
|
/**
|
|
* Pull a live snapshot over SSH and persist identity/metrics onto the row.
|
|
* On any failure (no credential, unreachable) the page renders an offline
|
|
* state from the last persisted values instead of crashing.
|
|
*/
|
|
public function mount(): void
|
|
{
|
|
// identity + gauges render from the persisted row immediately;
|
|
// the live snapshot loads lazily via wire:init -> 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->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(string $fingerprint, string $comment): void
|
|
{
|
|
$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');
|
|
}
|
|
}
|