@php
$fmtBytes = function (int $n): string {
$u = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
$v = (float) $n;
while ($v >= 1024 && $i < count($u) - 1) { $v /= 1024; $i++; }
return ($i === 0 ? (string) $n : number_format($v, 1)).' '.$u[$i];
};
$ago = function (int $ts): string {
if ($ts <= 0) return __('wireguard.never');
$d = max(0, time() - $ts);
if ($d < 60) return $d.'s';
if ($d < 3600) return intdiv($d, 60).'m';
if ($d < 86400) return intdiv($d, 3600).'h';
return intdiv($d, 86400).'d';
};
@endphp
{{ __('wireguard.eyebrow') }}
{{ __('wireguard.heading') }}
{{ __('wireguard.subtitle') }}
@if (! $status['configured'])
{{ __('wireguard.setup_intro') }}
@else
@if ($status['stale'])
{{ __('wireguard.stale') }}
@endif
@if ($pendingId)
{{ __('wireguard.pending') }}
@endif
{{-- Tabs: Peers (list + traffic) and Server (independent server configuration) --}}
@foreach ([['peers', __('wireguard.tab_peers')], ['server', __('wireguard.tab_server')]] as [$tabKey, $tabLabel])
$tab === $tabKey,
'border-transparent text-ink-3 hover:text-ink' => $tab !== $tabKey,
])>{{ $tabLabel }}
@endforeach
@if ($tab === 'server')
{{-- ── Server configuration tab ──────────────────────────────────────────────── --}}
{{-- Gate toggle --}}
{{ __('wireguard.gate_toggle') }}
@if ($status['gate']['marker'] && $status['gate']['chain'])
{{ __('wireguard.gate_turn_off') }}
@else
{{ __('wireguard.gate_turn_on') }}
@endif
{{-- SSH lock (port 22) — lock-out risk, warning shown inline (not just in the modal) --}}
{{ __('wireguard.ssh_lock') }}
@if ($status['gate']['ssh'])
{{ __('wireguard.ssh_lock_on_state') }}
{{ __('wireguard.ssh_lock_turn_off') }}
@else
{{ __('wireguard.ssh_lock_off_state') }}
{{ __('wireguard.ssh_lock_turn_on') }}
@endif
{{ __('wireguard.ssh_lock_hint') }}
{{-- Endpoint --}}
{{-- DNS --}}
{{-- Port --}}
{{ __('wireguard.port_label') }}
{{ __('wireguard.apply') }}
{{ __('wireguard.port_hint') }}
@error('newPort')
{{ $message }} @enderror
{{-- Subnet --}}
{{ __('wireguard.subnet_label') }}
{{ __('wireguard.apply') }}
{{ __('wireguard.subnet_hint') }}
@error('newSubnet')
{{ $message }} @enderror
{{ __('wireguard.subnet') }} {{ $status['server']['subnet'] ?: '—' }}
{{ __('wireguard.server_ip') }} {{ $status['server']['server_ip'] ?: '—' }}
{{ __('wireguard.port') }} {{ $status['server']['port'] ?: '—' }}
{{ __('wireguard.endpoint') }} {{ $status['server']['endpoint'] ?: '—' }}
{{ __('wireguard.dns') }} {{ $status['server']['dns'] ?: '1.1.1.1' }}
{{ __('wireguard.service') }} {{ $status['wg_quick'] }}
@if ($status['gate']['marker'] && $status['gate']['chain'])
{{ __('wireguard.gate_on') }}
@else
{{ __('wireguard.gate_off') }}
@endif
{{ __('wireguard.gate_escape') }}
@else
{{-- ── Peers tab (traffic + peer management) ──────────────────────────────────── --}}
@php
$win = collect($windows)->mapWithKeys(fn ($s) => [$s => match ($s) {
3600 => __('wireguard.window_1h'), 86400 => __('wireguard.window_24h'), default => __('wireguard.window_7d'),
}])->all();
$pts = $traffic['points'];
$n = max(1, count($pts) - 1);
$peak = max(1, $traffic['peak_down'], $traffic['peak_up']);
$line = function (string $key) use ($pts, $n, $peak) {
$out = [];
foreach ($pts as $i => $p) {
$x = round($i / $n * 600, 1);
$y = round(140 - ($p[$key] / $peak) * 132, 1);
$out[] = $x.','.$y;
}
return implode(' ', $out);
};
$hasTraffic = $traffic['total_down'] > 0 || $traffic['total_up'] > 0;
// Header = LIVE cumulative totals (sum of the peer counters), so it always matches the
// per-peer rows. The chart below is windowed THROUGHPUT history (needs the sampler).
$liveDown = array_sum(array_map(fn ($p) => $p['rx'], $status['peers']));
$liveUp = array_sum(array_map(fn ($p) => $p['tx'], $status['peers']));
@endphp
{{ __('wireguard.total_down') }}: {{ $fmtBytes($liveDown) }}
{{ __('wireguard.total_up') }}: {{ $fmtBytes($liveUp) }}
@foreach ($windows as $w)
$window === $w,
'border-line bg-raised text-ink-3 hover:border-accent/30 hover:text-ink' => $window !== $w,
])>{{ $win[$w] }}
@endforeach
@if ($hasTraffic)
@else
{{ __('wireguard.no_traffic') }}
@endif
@forelse ($status['peers'] as $peer)
{{ $peer['name'] !== '' ? $peer['name'] : \Illuminate\Support\Str::limit($peer['pubkey'], 10, '…') }}
{{ $peer['online'] ? __('wireguard.online') : __('wireguard.offline') }}
{{ __('wireguard.last_handshake') }}: {{ $ago($peer['latest_handshake']) }}
{{ $peer['endpoint'] !== '' ? $peer['endpoint'] : '—' }}
{{ __('wireguard.down_up', ['rx' => $fmtBytes($peer['rx']), 'tx' => $fmtBytes($peer['tx'])]) }}
{{ __('wireguard.remove') }}
@empty
{{ __('wireguard.no_peers') }}
@endforelse
@endif
@endif
@if ($resultConfig)
{{ __('wireguard.new_peer_title') }}
{{ __('wireguard.new_peer_once') }}
{{ __('wireguard.import_hint') }}
@if ($resultQr)
{{-- White frame + crispEdges: a WireGuard config QR is dense (~57 modules); too
small or anti-aliased and a phone camera misreads it. --}}
@endif
{{ $resultConfig }}
{{ __('wireguard.download') }}
{{ __('wireguard.close') }}
@endif