From 7c60ccda0dcbe84f6394924bef10a800fcc890f8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 28 Sep 2026 09:35:32 +0200 Subject: [PATCH] fix: restore config diff, error text, delete handler, webhook id and danger zone fixes - Configuration diff: report all settings when an app has no earlier snapshot, so older apps show "configuration changed" again (#11430). - DigitalOcean/Vultr: do not repeat the "API error:" prefix. - HasSecretManager: the deleting listener returns nothing, so later deleting listeners still run. - GitLab webhook: use new_public_id() like the other webhooks. - Team danger zone: show the link and open the page only for the team owner. Co-Authored-By: Claude Opus 5.5 --- app/Http/Controllers/Webhook/Gitlab.php | 7 +++--- app/Livewire/Server/New/ByDigitalOcean.php | 4 ++- app/Livewire/Server/New/ByVultr.php | 4 ++- app/Livewire/Team/DangerZone.php | 1 + .../ConfigurationDiffer.php | 4 ++- app/Traits/HasSecretManager.php | 5 +++- .../components/team/settings-layout.blade.php | 4 +-- .../Authorization/TeamAuthorizationTest.php | 25 ++++++++++++++++--- 8 files changed, 40 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php index 27138135ef..62199ba6ec 100644 --- a/app/Http/Controllers/Webhook/Gitlab.php +++ b/app/Http/Controllers/Webhook/Gitlab.php @@ -17,7 +17,6 @@ use Exception; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; -use Visus\Cuid2\Cuid2; class Gitlab extends Controller { @@ -185,7 +184,7 @@ class Gitlab extends Controller continue; } - $deployment_uuid = new Cuid2; + $deployment_uuid = new_public_id(); $result = queue_application_deployment( application: $application, deployment_uuid: $deployment_uuid, @@ -203,7 +202,7 @@ class Gitlab extends Controller 'mode' => 'app', 'application_uuid' => $application->uuid, 'application_name' => $application->name, - 'deployment_uuid' => $deployment_uuid->toString(), + 'deployment_uuid' => $deployment_uuid, 'commit' => $commit, ]); @@ -275,7 +274,7 @@ class Gitlab extends Controller continue; } - $deployment_uuid = new Cuid2; + $deployment_uuid = new_public_id(); $found = ApplicationPreview::where('application_id', $application->id) ->where('pull_request_id', $pull_request_id) ->first(); diff --git a/app/Livewire/Server/New/ByDigitalOcean.php b/app/Livewire/Server/New/ByDigitalOcean.php index d05532776e..1cfcc56634 100644 --- a/app/Livewire/Server/New/ByDigitalOcean.php +++ b/app/Livewire/Server/New/ByDigitalOcean.php @@ -296,7 +296,9 @@ class ByDigitalOcean extends Component $details = data_get($e->response->json(), $jsonMessageKey) ?: $e->response->body() ?: $details; } - return "{$providerName} API error: {$details}"; + $prefix = "{$providerName} API error: "; + + return str_starts_with($details, $prefix) ? $details : $prefix.$details; } public function getAvailableSizesProperty(): array diff --git a/app/Livewire/Server/New/ByVultr.php b/app/Livewire/Server/New/ByVultr.php index 3a6ce532df..321d8f6588 100644 --- a/app/Livewire/Server/New/ByVultr.php +++ b/app/Livewire/Server/New/ByVultr.php @@ -375,7 +375,9 @@ class ByVultr extends Component $details = data_get($e->response->json(), $jsonMessageKey) ?: $e->response->body() ?: $details; } - return "{$providerName} API error: {$details}"; + $prefix = "{$providerName} API error: "; + + return str_starts_with($details, $prefix) ? $details : $prefix.$details; } private function createVultrServer(VultrService $vultrService): array diff --git a/app/Livewire/Team/DangerZone.php b/app/Livewire/Team/DangerZone.php index 74cf3d7ef8..643477e24d 100644 --- a/app/Livewire/Team/DangerZone.php +++ b/app/Livewire/Team/DangerZone.php @@ -16,6 +16,7 @@ class DangerZone extends Component public function mount(): void { $this->team = currentTeam(); + $this->authorize('delete', $this->team); } public function delete(): mixed diff --git a/app/Services/DeploymentConfiguration/ConfigurationDiffer.php b/app/Services/DeploymentConfiguration/ConfigurationDiffer.php index ace4888338..688db3f0a4 100644 --- a/app/Services/DeploymentConfiguration/ConfigurationDiffer.php +++ b/app/Services/DeploymentConfiguration/ConfigurationDiffer.php @@ -39,7 +39,9 @@ class ConfigurationDiffer $previous = $previousItems[$key] ?? null; $current = $currentItems[$key] ?? null; - if ($previous === null && ! in_array(data_get($current, 'section'), self::DYNAMIC_SECTIONS, true)) { + // Static settings missing from an older snapshot were newly tracked, not changed. + // Without any previous snapshot (legacy fallback), every setting is reported. + if ($previous === null && $previousItems !== [] && ! in_array(data_get($current, 'section'), self::DYNAMIC_SECTIONS, true)) { continue; } diff --git a/app/Traits/HasSecretManager.php b/app/Traits/HasSecretManager.php index 8b3e50b7bd..a788ae823e 100644 --- a/app/Traits/HasSecretManager.php +++ b/app/Traits/HasSecretManager.php @@ -15,7 +15,10 @@ trait HasSecretManager public static function bootHasSecretManager(): void { - static::deleting(fn ($resource) => $resource->secretManagerLink()->delete()); + // Return nothing: a non-null result halts the "deleting" event and skips later listeners. + static::deleting(function ($resource): void { + $resource->secretManagerLink()->delete(); + }); } public function secretManagerLink(): MorphOne diff --git a/resources/views/components/team/settings-layout.blade.php b/resources/views/components/team/settings-layout.blade.php index 3b6db03c3a..be3ea573e1 100644 --- a/resources/views/components/team/settings-layout.blade.php +++ b/resources/views/components/team/settings-layout.blade.php @@ -24,13 +24,13 @@ 'active' => request()->routeIs('team.admin-view'), 'icon' => 'admin', ] : null, - [ + auth()->user()->can('delete', currentTeam()) ? [ 'label' => 'Danger Zone', 'route' => 'team.danger-zone', 'active' => request()->routeIs('team.danger-zone'), 'icon' => 'shield-alert', 'sectionStart' => true, - ], + ] : null, ])->filter(); @endphp diff --git a/tests/Feature/Authorization/TeamAuthorizationTest.php b/tests/Feature/Authorization/TeamAuthorizationTest.php index 0637a48db5..7d437c823f 100644 --- a/tests/Feature/Authorization/TeamAuthorizationTest.php +++ b/tests/Feature/Authorization/TeamAuthorizationTest.php @@ -171,17 +171,34 @@ test('team index mounts when is_mcp_server_enabled is null on the session team', // --- Team Danger Zone Livewire: delete --- -test('member cannot delete team via danger zone', function () { +test('member cannot open the team danger zone', function () { $this->actingAs($this->member); session(['currentTeam' => $this->team]); - Livewire::test(DangerZone::class) - ->call('delete') - ->assertDispatched('error'); + Livewire::test(DangerZone::class)->assertForbidden(); expect(Team::find($this->team->id))->not->toBeNull(); }); +test('owner can open the team danger zone', function () { + $this->actingAs($this->owner); + session(['currentTeam' => $this->team]); + + Livewire::test(DangerZone::class)->assertSuccessful(); +}); + +test('team settings show the danger zone link only to the team owner', function () { + session(['currentTeam' => $this->team]); + + $this->actingAs($this->owner); + Livewire::test(TeamIndex::class)->assertSee('Danger Zone'); + + foreach ([$this->admin, $this->member] as $user) { + $this->actingAs($user); + Livewire::test(TeamIndex::class)->assertDontSee('Danger Zone'); + } +}); + test('admin cannot delete team via policy', function () { $this->actingAs($this->admin); session(['currentTeam' => $this->team]);