From 6e0ec724ca8c2bf3f20a8fbb998033162b783042 Mon Sep 17 00:00:00 2001 From: boban Date: Sun, 14 Jun 2026 19:29:24 +0200 Subject: [PATCH] fix(ui): re-sync active server on bfcache restore of a details page wire:navigate restores Back/Forward pages from its client cache without re-running Show::mount(), which could leave active_server_id pointing at a different server than the details page on screen. Add a servers.activate endpoint + a global livewire:navigated hook that re-asserts the on-screen server on every navigation (including cache restores). Co-Authored-By: Claude Opus 4.8 (1M context) --- resources/js/app.js | 14 ++++++++++++++ resources/views/livewire/servers/show.blade.php | 2 +- routes/web.php | 9 +++++++++ tests/Feature/ServerContextSyncTest.php | 10 ++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/resources/js/app.js b/resources/js/app.js index dade033..9b5ef1a 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -198,3 +198,17 @@ document.addEventListener('alpine:init', () => { }, })); }); + +// Keep the active-server session in sync with the server-details page on screen, even +// when wire:navigate restores a page from its bfcache (Back/Forward) without re-running +// Show::mount(). The details page tags its root with data-clusev-activate=. +document.addEventListener('livewire:navigated', () => { + const el = document.querySelector('[data-clusev-activate]'); + if (!el) return; + const token = document.querySelector('meta[name="csrf-token"]')?.content; + fetch(el.dataset.clusevActivate, { + method: 'POST', + headers: { 'X-CSRF-TOKEN': token ?? '', 'X-Requested-With': 'XMLHttpRequest' }, + keepalive: true, + }).catch(() => {}); +}); diff --git a/resources/views/livewire/servers/show.blade.php b/resources/views/livewire/servers/show.blade.php index 02f3d29..0d6bd73 100644 --- a/resources/views/livewire/servers/show.blade.php +++ b/resources/views/livewire/servers/show.blade.php @@ -36,7 +36,7 @@ ]; @endphp -
+
diff --git a/routes/web.php b/routes/web.php index 5ecadfa..4876493 100644 --- a/routes/web.php +++ b/routes/web.php @@ -11,6 +11,7 @@ use App\Livewire\Services; use App\Livewire\Settings; use App\Livewire\System; use App\Livewire\Versions; +use App\Models\Server; use App\Services\DeploymentService; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth as AuthFacade; @@ -80,6 +81,14 @@ Route::middleware('auth')->group(function () { Route::get('/servers', Servers\Index::class)->name('servers.index'); Route::get('/servers/{server}', Servers\Show::class)->name('servers.show'); + // Re-assert the active server when a details page is shown — incl. a wire:navigate + // bfcache restore (Back/Forward), which never re-runs Show::mount(). A tiny JS hook + // on `livewire:navigated` posts here so the session context never drifts. + Route::post('/servers/{server}/activate', function (Server $server) { + session(['active_server_id' => $server->id]); + + return response()->noContent(); + })->name('servers.activate'); Route::get('/services', Services\Index::class)->name('services.index'); Route::get('/files', Files\Index::class)->name('files.index'); diff --git a/tests/Feature/ServerContextSyncTest.php b/tests/Feature/ServerContextSyncTest.php index cd4610b..dfc6963 100644 --- a/tests/Feature/ServerContextSyncTest.php +++ b/tests/Feature/ServerContextSyncTest.php @@ -19,6 +19,16 @@ class ServerContextSyncTest extends TestCase return Server::create(['name' => $name, 'ip' => '10.0.0.'.rand(2, 250), 'ssh_port' => 22, 'status' => 'online']); } + public function test_activate_endpoint_sets_active_server(): void + { + $this->actingAs(User::factory()->create()); + $b = $this->server('B'); + + $this->post(route('servers.activate', $b))->assertNoContent(); + + $this->assertSame($b->id, session('active_server_id')); + } + public function test_viewing_server_details_sets_it_active(): void { $this->actingAs(User::factory()->create());