CluPilotCloud/resources/views/components/admin/vpn-peer-row.blade.php

129 lines
7.6 KiB
PHP

@props(['peer'])
{{--
Eine Zeile der Zugangsliste.
Ausgelagert, als die Liste in zwei Gruppen zerfiel (Personen und Hosts).
Sie zweimal hinzuschreiben hätte geheißen, sie ab dem nächsten Knopf an
zwei Stellen zu pflegen und die zweite fällt erst auf, wenn jemand einen
Host-Zugang sucht und ihn anders aussehen sieht als seinen eigenen.
Ursprünglicher Kopfkommentar der Liste, weil er die Form dieser Zeile
erklärt: Ein Register von Zugängen, keine Tabelle. Sieben Spalten ließen
jede davon zu schmal Namen brachen um, „Letzter Kontakt" brach in seiner
eigenen Überschrift um, und die Zeile passte trotzdem nicht. Das sind
Eigenschaften EINES Zugangs, keine Größen, die jemand über eine Spalte
hinweg vergleicht: also je Zugang eine eigene Zeile, Identität oben,
Messwerte darunter in Mono, wo sie sich von selbst untereinander stellen.
--}}
@php
$status = $peer->status();
$tone = [
'online' => 'border-success-border bg-success-bg text-success',
'idle' => 'border-line-strong bg-surface-2 text-muted',
'blocked' => 'border-danger-border bg-danger-bg text-danger',
'pending' => 'border-warning-border bg-warning-bg text-warning',
][$status];
// Der Strich trägt den Zustand mit, damit ein gesperrter Zugang beim
// Überfliegen auffällt, ohne dass jemand eine Plakette liest.
$rule = [
'online' => 'bg-success-bright',
'idle' => 'bg-line-strong',
'blocked' => 'bg-danger',
'pending' => 'bg-warning',
][$status];
@endphp
<li wire:key="peer-{{ $peer->uuid }}" class="relative flex flex-wrap items-start gap-x-4 gap-y-3 py-4 pl-6 pr-4 transition hover:bg-surface-hover">
<span class="absolute left-0 top-4 bottom-4 w-0.5 rounded-pill {{ $rule }}" aria-hidden="true"></span>
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-center gap-x-2.5 gap-y-1">
{{-- Bei einem Host-Zugang der Name des HOSTS, nicht der gespeicherte.
Der Zugang bekommt seinen Namen einmal beim Anlegen; wird der
Host später umbenannt, zeigt die Liste weiter den alten. Genau
so stand hier „pve-fns-1", während der Host längst „fsn-01"
hieß — zwei Namen für dieselbe Maschine, und man muss erst
wissen, dass es dieselbe ist. --}}
<span class="font-medium text-ink">{{ $peer->host?->name ?? $peer->name }}</span>
{{-- Die Plakette bleibt, obwohl die Gruppe daneben dasselbe sagt:
eine Zeile wandert beim Suchen aus ihrer Überschrift heraus,
und dann steht sie ohne sie da. --}}
<span class="inline-flex items-center gap-1 rounded-pill border border-line px-2 py-0.5 text-[11px] text-muted">
<x-ui.icon :name="$peer->host ? 'server' : 'users'" class="size-3" />
{{ $peer->host ? __('vpn.host_peer') : __('vpn.operator_peer') }}
</span>
@if ($peer->owner)
<span class="text-sm text-muted">{{ $peer->owner->name }}</span>
@if ($peer->user_id === auth()->id())
<span class="rounded-pill border border-accent-border bg-accent-subtle px-1.5 py-0.5 text-[10px] font-medium text-accent-text">{{ __('vpn.you') }}</span>
@endif
@elseif (! $peer->host)
<span class="text-sm text-muted">{{ __('vpn.no_owner') }}</span>
@endif
</div>
{{-- Die Messwerte, jeder mit eigener Beschriftung: ohne Kopfzeile
müssen sie sich selbst benennen. --}}
<div class="mt-1.5 flex flex-wrap items-center gap-x-5 gap-y-1 font-mono text-xs text-muted">
<span class="text-body">{{ $peer->allowed_ip }}</span>
@if ($peer->endpoint)
<span class="text-muted">{{ $peer->endpoint }}</span>
@endif
<span class="whitespace-nowrap">&darr;&nbsp;{{ \App\Support\Bytes::human($peer->rx_bytes) }}&nbsp;&nbsp;&uarr;&nbsp;{{ \App\Support\Bytes::human($peer->tx_bytes) }}</span>
<span class="whitespace-nowrap">{{ __('vpn.handshake') }}: {{ $peer->last_handshake_at?->diffForHumans() ?? __('vpn.never') }}</span>
</div>
</div>
<div class="flex shrink-0 items-center gap-2">
<span class="inline-flex items-center gap-1.5 whitespace-nowrap rounded-pill border px-2.5 py-0.5 text-xs font-medium {{ $tone }}">
<span class="size-1.5 rounded-pill bg-current" aria-hidden="true"></span>
{{ __('vpn.status_'.$status) }}
</span>
<div class="inline-flex gap-1">
{{-- Eine hinterlegte Konfiguration lässt sich erneut abholen; ohne
eine gibt es nichts auszugeben, und das zu sagen ist besser als
ein fehlender Knopf. --}}
@if (! $peer->hasStoredConfig() && $peer->kind === \App\Models\VpnPeer::KIND_STAFF && $peer->user_id === auth()->id())
<span title="{{ __('vpn.no_stored_config') }}"
class="grid size-8 cursor-help place-items-center rounded-md border border-dashed border-line text-muted">
<x-ui.icon name="download" class="size-4" />
</span>
@endif
@can('downloadConfig', $peer)
<button type="button" aria-label="{{ __('vpn.get_config') }}" title="{{ __('vpn.get_config') }}"
x-on:click="$dispatch('openModal', { component: 'admin.vpn-config-access', arguments: { uuid: '{{ $peer->uuid }}' } })"
class="grid size-8 place-items-center rounded-md border border-line text-muted transition hover:border-accent-border hover:text-accent-text">
<x-ui.icon name="download" class="size-4" />
</button>
@endcan
@can('block', $peer)
<button type="button" wire:click="toggle('{{ $peer->uuid }}')"
aria-label="{{ $peer->enabled ? __('vpn.block') : __('vpn.unblock') }}" title="{{ $peer->enabled ? __('vpn.block') : __('vpn.unblock') }}"
class="grid size-8 place-items-center rounded-md border border-line text-muted transition hover:border-warning-border hover:text-warning">
<x-ui.icon :name="$peer->enabled ? 'lock' : 'unlock'" class="size-4" />
</button>
@endcan
@can('update', $peer)
@if ($peer->kind === \App\Models\VpnPeer::KIND_STAFF)
<button type="button"
x-on:click="$dispatch('openModal', { component: 'admin.confirm-reissue-vpn-peer', arguments: { uuid: '{{ $peer->uuid }}' } })"
title="{{ __('vpn.reissue') }}" aria-label="{{ __('vpn.reissue') }}"
class="grid size-8 place-items-center rounded-md border border-line text-muted transition hover:border-accent-border hover:text-accent-text">
<x-ui.icon name="refresh" class="size-4" />
</button>
@endif
@endcan
@can('delete', $peer)
<button type="button" aria-label="{{ __('vpn.delete') }}" title="{{ __('vpn.delete') }}"
x-on:click="$dispatch('openModal', { component: 'admin.confirm-delete-vpn-peer', arguments: { uuid: '{{ $peer->uuid }}' } })"
class="grid size-8 place-items-center rounded-md border border-line text-muted transition hover:border-danger hover:text-danger">
<x-ui.icon name="trash-2" class="size-4" />
</button>
@endcan
</div>
</div>
</li>