diff --git a/app/Livewire/Admin/ConfirmDeleteDatacenter.php b/app/Livewire/Admin/ConfirmDeleteDatacenter.php index e88e8cf..cb52618 100644 --- a/app/Livewire/Admin/ConfirmDeleteDatacenter.php +++ b/app/Livewire/Admin/ConfirmDeleteDatacenter.php @@ -4,7 +4,7 @@ namespace App\Livewire\Admin; use App\Models\Datacenter; use App\Models\Host; -use Illuminate\Support\Facades\DB; +use Illuminate\Database\QueryException; use LivewireUI\Modal\ModalComponent; /** @@ -30,24 +30,23 @@ class ConfirmDeleteDatacenter extends ModalComponent public function delete() { - // Serialize the guard with the delete: lock the datacenter row and - // re-check host membership inside the same transaction so a host created - // concurrently can't be orphaned (hosts.datacenter is a bare code). - DB::transaction(function () { - $dc = Datacenter::query()->where('uuid', $this->uuid)->lockForUpdate()->first(); - if ($dc === null) { - return; - } - // Deactivate first so HostCreate's `exists:…,active,1` rule blocks any - // new host for this code while we finish deleting. - $dc->update(['active' => false]); + $dc = Datacenter::query()->where('uuid', $this->uuid)->first(); + if ($dc === null) { + return $this->redirectRoute('admin.datacenters', navigate: true); + } - if (Host::query()->where('datacenter', $dc->code)->exists()) { - return; // still has hosts — leave it (deactivated) - } + // The hosts.datacenter → datacenters.code foreign key (restrictOnDelete) + // is the source of truth: the DB refuses to orphan a host. We pre-check + // for a friendly message and catch the constraint as the race backstop. + if (Host::query()->where('datacenter', $dc->code)->exists()) { + return $this->redirectRoute('admin.datacenters', navigate: true); + } + try { $dc->delete(); - }); + } catch (QueryException) { + // A host was created for this code concurrently — leave it intact. + } return $this->redirectRoute('admin.datacenters', navigate: true); } diff --git a/database/migrations/2026_07_25_090008_add_datacenter_fk_to_hosts.php b/database/migrations/2026_07_25_090008_add_datacenter_fk_to_hosts.php new file mode 100644 index 0000000..4cb5d9b --- /dev/null +++ b/database/migrations/2026_07_25_090008_add_datacenter_fk_to_hosts.php @@ -0,0 +1,43 @@ +whereNotNull('datacenter') + ->whereNotIn('datacenter', fn ($q) => $q->from('datacenters')->select('code')) + ->distinct()->pluck('datacenter'); + foreach ($missing as $code) { + DB::table('datacenters')->insert([ + 'uuid' => (string) \Illuminate\Support\Str::uuid(), + 'code' => $code, 'name' => \Illuminate\Support\Str::upper($code), + 'active' => true, 'created_at' => $now, 'updated_at' => $now, + ]); + } + + Schema::table('hosts', function (Blueprint $table) { + $table->foreign('datacenter')->references('code')->on('datacenters')->restrictOnDelete(); + }); + } + + public function down(): void + { + Schema::table('hosts', function (Blueprint $table) { + $table->dropForeign(['datacenter']); + }); + } +};