fix(ui): cancel stale activate requests + onboarded user in test

Abort any in-flight active-server POST on each navigation so a slower stale
request can't overwrite the latest selection. Fix the activate-endpoint test to
use an onboarded user (the route is behind EnsureSecurityOnboarded).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat/v1-foundation
boban 2026-06-14 19:32:16 +02:00
parent 6e0ec724ca
commit f4c24d51a6
2 changed files with 8 additions and 2 deletions

View File

@ -202,13 +202,18 @@ document.addEventListener('alpine:init', () => {
// Keep the active-server session in sync with the server-details page on screen, even // 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 // 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=<activate URL>. // Show::mount(). The details page tags its root with data-clusev-activate=<activate URL>.
let clusevActivateAbort;
document.addEventListener('livewire:navigated', () => { document.addEventListener('livewire:navigated', () => {
// Abort any prior in-flight activation so only the LATEST navigation can win — a
// stale request must never overwrite a newer server selection.
clusevActivateAbort?.abort();
const el = document.querySelector('[data-clusev-activate]'); const el = document.querySelector('[data-clusev-activate]');
if (!el) return; if (!el) return;
clusevActivateAbort = new AbortController();
const token = document.querySelector('meta[name="csrf-token"]')?.content; const token = document.querySelector('meta[name="csrf-token"]')?.content;
fetch(el.dataset.clusevActivate, { fetch(el.dataset.clusevActivate, {
method: 'POST', method: 'POST',
headers: { 'X-CSRF-TOKEN': token ?? '', 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-CSRF-TOKEN': token ?? '', 'X-Requested-With': 'XMLHttpRequest' },
keepalive: true, signal: clusevActivateAbort.signal,
}).catch(() => {}); }).catch(() => {});
}); });

View File

@ -21,7 +21,8 @@ class ServerContextSyncTest extends TestCase
public function test_activate_endpoint_sets_active_server(): void public function test_activate_endpoint_sets_active_server(): void
{ {
$this->actingAs(User::factory()->create()); // onboarded user — the route sits behind EnsureSecurityOnboarded
$this->actingAs(User::factory()->create(['two_factor_secret' => 'x', 'two_factor_confirmed_at' => now()]));
$b = $this->server('B'); $b = $this->server('B');
$this->post(route('servers.activate', $b))->assertNoContent(); $this->post(route('servers.activate', $b))->assertNoContent();