123 lines
7.1 KiB
PHP
123 lines
7.1 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">
|
|
<span class="font-medium text-ink">{{ $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">↓ {{ \App\Support\Bytes::human($peer->rx_bytes) }} ↑ {{ \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>
|