From 01383e7e5e32b2a24ac1c4101038b737d5ce9e6a Mon Sep 17 00:00:00 2001 From: nexxo Date: Sat, 25 Jul 2026 14:55:09 +0200 Subject: [PATCH] fix(portal): race-safe owner-seat initialization (firstOrCreate + catch) Co-Authored-By: Claude Opus 4.8 --- app/Livewire/Users.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/Livewire/Users.php b/app/Livewire/Users.php index 77ced0b..dc1bea3 100644 --- a/app/Livewire/Users.php +++ b/app/Livewire/Users.php @@ -4,6 +4,7 @@ namespace App\Livewire; use App\Models\Customer; use App\Models\Seat; +use Illuminate\Database\UniqueConstraintViolationException; use Illuminate\Support\Facades\DB; use Livewire\Attributes\Layout; use Livewire\Attributes\Validate; @@ -23,16 +24,19 @@ class Users extends Component public function mount(): void { - // Every customer starts with themselves as the owner seat. + // Every customer starts with themselves as the owner seat. firstOrCreate + // keyed on (customer_id, email) is idempotent; the catch covers the + // concurrent-first-visit race against the unique index. $customer = $this->customer(); if ($customer !== null && $customer->seats()->count() === 0) { - $customer->seats()->create([ - 'email' => $customer->email, - 'name' => $customer->name, - 'role' => 'owner', - 'status' => 'active', - 'invited_at' => now(), - ]); + try { + $customer->seats()->firstOrCreate( + ['email' => $customer->email], + ['name' => $customer->name, 'role' => 'owner', 'status' => 'active', 'invited_at' => now()], + ); + } catch (UniqueConstraintViolationException) { + // Another concurrent first visit created it — fine. + } } }