fix(auth/admin): atomic user+customer signup; Rule::in for country validation (comma-safe)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/portal-design
nexxo 2026-07-25 18:33:09 +02:00
parent 6ca1e3fba5
commit 22cbe9ac39
3 changed files with 23 additions and 18 deletions

View File

@ -4,6 +4,7 @@ namespace App\Actions\Fortify;
use App\Models\Customer; use App\Models\Customer;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
@ -39,23 +40,25 @@ class CreateNewUser implements CreatesNewUsers
'password' => $this->passwordRules(), 'password' => $this->passwordRules(),
])->validate(); ])->validate();
$user = User::create([ // Create the user and its linked customer atomically — a public signup is
'name' => $input['name'], // a customer, and the portal (Billing::purchase, Settings, …) needs one.
'email' => $input['email'], // Either both exist or neither, so a failure never orphans a user.
'password' => Hash::make($input['password']), return DB::transaction(function () use ($input) {
]); $user = User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
// A public signup is a customer — create and link the record now so the Customer::query()->create([
// portal (Billing::purchase, Settings, …) has a customer to work with 'user_id' => $user->id,
// before they buy a package. 'name' => $input['name'],
Customer::query()->create([ 'email' => $input['email'],
'user_id' => $user->id, 'locale' => app()->getLocale(),
'name' => $input['name'], 'status' => 'active',
'email' => $input['email'], ]);
'locale' => app()->getLocale(),
'status' => 'active',
]);
return $user; return $user;
});
} }
} }

View File

@ -3,6 +3,7 @@
namespace App\Livewire\Admin; namespace App\Livewire\Admin;
use App\Models\Datacenter; use App\Models\Datacenter;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout; use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate; use Livewire\Attributes\Validate;
use Livewire\Component; use Livewire\Component;
@ -32,7 +33,7 @@ class Datacenters extends Component
$data = $this->validate([ $data = $this->validate([
'code' => 'required|string|max:8|alpha_dash|unique:datacenters,code', 'code' => 'required|string|max:8|alpha_dash|unique:datacenters,code',
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'location' => 'nullable|in:'.implode(',', array_keys((array) config('countries'))), 'location' => ['nullable', Rule::in(array_keys((array) config('countries')))],
]); ]);
Datacenter::create([ Datacenter::create([

View File

@ -3,6 +3,7 @@
namespace App\Livewire\Admin; namespace App\Livewire\Admin;
use App\Models\Datacenter; use App\Models\Datacenter;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Validate; use Livewire\Attributes\Validate;
use LivewireUI\Modal\ModalComponent; use LivewireUI\Modal\ModalComponent;
@ -51,7 +52,7 @@ class EditDatacenter extends ModalComponent
} }
$data = $this->validate([ $data = $this->validate([
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'location' => 'nullable|in:'.implode(',', $allowed), 'location' => ['nullable', Rule::in($allowed)], // array form — a legacy value may contain commas
]); ]);
Datacenter::query()->where('uuid', $this->uuid)->update([ Datacenter::query()->where('uuid', $this->uuid)->update([