From 920dfb02bf9f261d74651a18d592d04818d78481 Mon Sep 17 00:00:00 2001 From: boban Date: Sun, 21 Jun 2026 22:25:41 +0200 Subject: [PATCH] feat(wg): compact QR + meaningful download filename (v0.9.44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QR: 0.9.42 over-enlarged it; bring the show-once peer QR back to a balanced ~240px (still crispEdges on a white quiet-zone frame) so it scans on a phone without dominating the modal. - Download filename now becomes a useful tunnel name on import: build it from the endpoint host + peer name (e.g. "10.10.90.165-laptop.conf") instead of the generic "clusev-wireguard.conf" — WireGuard apps name a tunnel after the file. A QR scan can't carry a name (the app prompts for one); the import hint now explains both paths. Tests: download-filename cases (host+peer, fallback); 349 pass, Pint clean, /wireguard loads 200, lang parity 83/83. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 12 +++++++++- app/Livewire/Wireguard/Index.php | 23 +++++++++++++++++-- config/clusev.php | 2 +- lang/de/wireguard.php | 2 +- lang/en/wireguard.php | 2 +- .../views/livewire/wireguard/index.blade.php | 6 ++--- tests/Feature/WireguardPageTest.php | 14 +++++++++-- 7 files changed, 50 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37cc8d5..7834d97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,17 @@ getaggte Releases (Kanal `stable`, optional `beta`) — niemals Entwicklungs-Bui _Keine offenen Änderungen — der nächste Stand wird hier gesammelt und als `vX.Y.Z` getaggt._ -## [0.9.43] - 2026-06-21 +## [0.9.44] - 2026-06-21 + +### Geändert +- **Peer-QR-Code kompakter.** Nach der Vergrößerung in 0.9.42 war der QR zu groß; er ist jetzt auf + ein ausgewogenes Maß (~240 px) reduziert — weiterhin pixelgenau (`crispEdges`) auf weißem Rahmen, + damit das Scannen am Handy zuverlässig bleibt. +- **Download-Dateiname als Tunnel-Name.** Die heruntergeladene Client-Config heißt jetzt + `-.conf` (z. B. `10.10.90.165-laptop.conf`) statt `clusev-wireguard.conf`. + WireGuard-Apps benennen einen Tunnel nach der Datei, sodass der Tunnel sinnvoll vorbenannt ist. + (Beim QR-Scan fragt die WireGuard-App selbst nach einem Namen — das gibt der QR-Standard nicht her; + ein Hinweis im Einmal-Dialog erklärt beide Wege.) ### Behoben - **Endpoint ohne Port machte jede Client-Config ungültig.** Wurde beim Einrichten/Ändern nur ein diff --git a/app/Livewire/Wireguard/Index.php b/app/Livewire/Wireguard/Index.php index cbf1f89..3e0f41d 100644 --- a/app/Livewire/Wireguard/Index.php +++ b/app/Livewire/Wireguard/Index.php @@ -59,6 +59,11 @@ class Index extends Component public ?string $resultQr = null; + /** Peer name of the in-flight request, carried to the result so the download gets a meaningful filename. */ + public ?string $pendingName = null; + + public ?string $resultName = null; + public function setWindow(int $seconds): void { $this->window = $this->clampWindow($seconds); @@ -76,6 +81,7 @@ class Index extends Component $name = $this->newPeer; $this->pendingId = $bridge->request('add-peer', ['name' => $name]); $this->pendingAction = 'add-peer'; + $this->pendingName = $name; $this->newPeer = ''; $this->audit('wg.add-peer', $name); } @@ -106,6 +112,7 @@ class Index extends Component 'endpoint' => $this->setupEndpoint, 'name' => $this->setupPeer, ]); $this->pendingAction = 'setup'; + $this->pendingName = $this->setupPeer; $this->audit('wg.setup', $this->setupSubnet); } @@ -324,6 +331,7 @@ class Index extends Component if ($res['ok'] && in_array($this->pendingAction, ['add-peer', 'setup'], true) && $res['config'] !== null) { $this->resultConfig = $res['config']; $this->resultQr = $res['qr']; + $this->resultName = $this->pendingName; } elseif (! $res['ok']) { $msg = ($res['message'] ?? '') !== '' ? $res['message'] : __('wireguard.action_failed'); $this->dispatch('notify', message: $msg, level: 'error'); @@ -331,15 +339,21 @@ class Index extends Component $this->dispatch('notify', message: __('wireguard.action_done')); } $this->pendingAction = null; + $this->pendingName = null; } public function dismissResult(): void { $this->resultConfig = null; $this->resultQr = null; + $this->resultName = null; } - /** Download the show-once client config as a .conf file (for importing on a PC). */ + /** + * Download the show-once client config as a .conf file. The filename becomes the tunnel name when + * imported (WireGuard apps name a tunnel after the file), so build it from the endpoint host + peer + * name — e.g. "10.10.90.165-laptop.conf". (A QR scan can't carry a name; the app prompts for one.) + */ public function downloadConfig(): ?StreamedResponse { if ($this->resultConfig === null) { @@ -347,9 +361,14 @@ class Index extends Component } $cfg = $this->resultConfig; + $host = preg_match('/^Endpoint\s*=\s*([^:\s]+)/m', $cfg, $m) === 1 ? $m[1] : 'clusev'; + $parts = array_filter([$host, (string) $this->resultName]); + $base = preg_replace('/[^A-Za-z0-9._-]+/', '-', implode('-', $parts)); + $base = trim((string) $base, '-.') ?: 'clusev-wireguard'; + return response()->streamDownload(function () use ($cfg) { echo $cfg; - }, 'clusev-wireguard.conf', ['Content-Type' => 'text/plain; charset=UTF-8']); + }, $base.'.conf', ['Content-Type' => 'text/plain; charset=UTF-8']); } /** Clamp a traffic window to one of the allowed values, falling back to the shortest. */ diff --git a/config/clusev.php b/config/clusev.php index 7b35d1f..df0f8a5 100644 --- a/config/clusev.php +++ b/config/clusev.php @@ -2,7 +2,7 @@ return [ // First tagged release is v0.1.0 (semantic, not -dev). - 'version' => '0.9.43', + 'version' => '0.9.44', // Deployed commit + branch. install.sh bakes these into .env from the host's .git (the prod // image ships no .git); the Versions page prefers them and falls back to a live .git read in diff --git a/lang/de/wireguard.php b/lang/de/wireguard.php index 46ea970..b3990f7 100644 --- a/lang/de/wireguard.php +++ b/lang/de/wireguard.php @@ -45,7 +45,7 @@ return [ 'new_peer_once' => 'Diese Konfiguration wird NUR EINMAL angezeigt und nicht gespeichert. Jetzt importieren (QR scannen oder Text kopieren).', 'close' => 'Schließen', 'download' => 'Herunterladen', - 'import_hint' => 'QR-Code mit der WireGuard-App scannen (Handy) oder Konfiguration herunterladen und importieren (PC).', + 'import_hint' => 'QR mit der WireGuard-App scannen (Handy; die App fragt nach einem Tunnel-Namen) oder herunterladen und importieren (PC) — der Dateiname wird zum Tunnel-Namen.', 'settings_title' => 'Einstellungen', 'apply' => 'Anwenden', 'gate_toggle' => 'Gate', diff --git a/lang/en/wireguard.php b/lang/en/wireguard.php index 985f574..9a6d8e1 100644 --- a/lang/en/wireguard.php +++ b/lang/en/wireguard.php @@ -45,7 +45,7 @@ return [ 'new_peer_once' => 'This configuration is shown ONCE and never stored. Import it now (scan the QR or copy the text).', 'close' => 'Close', 'download' => 'Download', - 'import_hint' => 'Scan the QR code with the WireGuard app (phone) or download the configuration and import it (PC).', + 'import_hint' => 'Scan the QR with the WireGuard app (phone; it asks for a tunnel name) or download and import it (PC) — the filename becomes the tunnel name.', 'settings_title' => 'Settings', 'apply' => 'Apply', 'gate_toggle' => 'Gate', diff --git a/resources/views/livewire/wireguard/index.blade.php b/resources/views/livewire/wireguard/index.blade.php index e91835d..7ecd318 100644 --- a/resources/views/livewire/wireguard/index.blade.php +++ b/resources/views/livewire/wireguard/index.blade.php @@ -273,9 +273,9 @@

{{ __('wireguard.new_peer_once') }}

{{ __('wireguard.import_hint') }}

@if ($resultQr) - {{-- White frame + generous size + crispEdges: a WireGuard config QR is dense (~57 - modules); too small or anti-aliased and a phone camera misreads it. --}} -
+ {{-- White frame + crispEdges: a WireGuard config QR is dense (~57 modules); too + small or anti-aliased and a phone camera misreads it. --}} +
{!! $resultQr !!}
@endif diff --git a/tests/Feature/WireguardPageTest.php b/tests/Feature/WireguardPageTest.php index 5b89f9d..30c2dd6 100644 --- a/tests/Feature/WireguardPageTest.php +++ b/tests/Feature/WireguardPageTest.php @@ -205,12 +205,22 @@ class WireguardPageTest extends TestCase ->assertHasErrors('setupSubnet'); } - public function test_download_config_streams_a_conf_file(): void + public function test_download_config_filename_uses_endpoint_host_and_peer_name(): void + { + // The .conf filename becomes the tunnel name on import — build it from endpoint host + peer. + Livewire::test(Index::class) + ->set('resultConfig', "[Interface]\nPrivateKey = x\n\n[Peer]\nEndpoint = 10.10.90.165:51820") + ->set('resultName', 'laptop') + ->call('downloadConfig') + ->assertFileDownloaded('10.10.90.165-laptop.conf'); + } + + public function test_download_config_falls_back_to_a_default_filename(): void { Livewire::test(Index::class) ->set('resultConfig', "[Interface]\nPrivateKey = x") ->call('downloadConfig') - ->assertFileDownloaded('clusev-wireguard.conf'); + ->assertFileDownloaded('clusev.conf'); } public function test_poll_result_times_out_when_the_host_does_not_respond(): void