diff --git a/app/Livewire/NavbarDeleteTeam.php b/app/Livewire/NavbarDeleteTeam.php index a8c932912..8e5478b5e 100644 --- a/app/Livewire/NavbarDeleteTeam.php +++ b/app/Livewire/NavbarDeleteTeam.php @@ -15,10 +15,10 @@ public function mount() $this->team = currentTeam()->name; } - public function delete($password) + public function delete($password, $selectedActions = []) { if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } $currentTeam = currentTeam(); diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php index 35262d7b0..c24e2a3f1 100644 --- a/app/Livewire/Project/Database/BackupEdit.php +++ b/app/Livewire/Project/Database/BackupEdit.php @@ -146,12 +146,12 @@ public function syncData(bool $toModel = false) } } - public function delete($password) + public function delete($password, $selectedActions = []) { $this->authorize('manageBackups', $this->backup->database); if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } try { diff --git a/app/Livewire/Project/Database/BackupExecutions.php b/app/Livewire/Project/Database/BackupExecutions.php index 44f903fcc..1dd93781d 100644 --- a/app/Livewire/Project/Database/BackupExecutions.php +++ b/app/Livewire/Project/Database/BackupExecutions.php @@ -65,10 +65,10 @@ public function cleanupDeleted() } } - public function deleteBackup($executionId, $password) + public function deleteBackup($executionId, $password, $selectedActions = []) { if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } $execution = $this->backup->executions()->where('id', $executionId)->first(); @@ -96,7 +96,11 @@ public function deleteBackup($executionId, $password) $this->refreshBackupExecutions(); } catch (\Exception $e) { $this->dispatch('error', 'Failed to delete backup: '.$e->getMessage()); + + return true; } + + return true; } public function download_file($exeuctionId) diff --git a/app/Livewire/Project/Service/FileStorage.php b/app/Livewire/Project/Service/FileStorage.php index 079115bb6..5d948bffd 100644 --- a/app/Livewire/Project/Service/FileStorage.php +++ b/app/Livewire/Project/Service/FileStorage.php @@ -134,12 +134,12 @@ public function convertToFile() } } - public function delete($password) + public function delete($password, $selectedActions = []) { $this->authorize('update', $this->resource); if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } try { @@ -158,6 +158,8 @@ public function delete($password) } finally { $this->dispatch('refreshStorages'); } + + return true; } public function submit() diff --git a/app/Livewire/Project/Service/Index.php b/app/Livewire/Project/Service/Index.php index b735d7e71..c77a3a516 100644 --- a/app/Livewire/Project/Service/Index.php +++ b/app/Livewire/Project/Service/Index.php @@ -194,13 +194,13 @@ public function refreshFileStorages() } } - public function deleteDatabase($password) + public function deleteDatabase($password, $selectedActions = []) { try { $this->authorize('delete', $this->serviceDatabase); if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } $this->serviceDatabase->delete(); @@ -398,13 +398,13 @@ public function instantSaveApplicationAdvanced() } } - public function deleteApplication($password) + public function deleteApplication($password, $selectedActions = []) { try { $this->authorize('delete', $this->serviceApplication); if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } $this->serviceApplication->delete(); diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index e9c18cc8d..caaabc494 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -88,16 +88,21 @@ public function mount() } } - public function delete($password) + public function delete($password, $selectedActions = []) { if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } if (! $this->resource) { - $this->addError('resource', 'Resource not found.'); + return 'Resource not found.'; + } - return; + if (! empty($selectedActions)) { + $this->delete_volumes = in_array('delete_volumes', $selectedActions); + $this->delete_connected_networks = in_array('delete_connected_networks', $selectedActions); + $this->delete_configurations = in_array('delete_configurations', $selectedActions); + $this->docker_cleanup = in_array('docker_cleanup', $selectedActions); } try { diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php index 7ab81b7d1..363471760 100644 --- a/app/Livewire/Project/Shared/Destination.php +++ b/app/Livewire/Project/Shared/Destination.php @@ -134,11 +134,11 @@ public function addServer(int $network_id, int $server_id) $this->dispatch('refresh'); } - public function removeServer(int $network_id, int $server_id, $password) + public function removeServer(int $network_id, int $server_id, $password, $selectedActions = []) { try { if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) { @@ -152,6 +152,8 @@ public function removeServer(int $network_id, int $server_id, $password) $this->loadData(); $this->dispatch('refresh'); ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id')); + + return true; } catch (\Exception $e) { return handleError($e, $this); } diff --git a/app/Livewire/Project/Shared/Storages/Show.php b/app/Livewire/Project/Shared/Storages/Show.php index 2091eca14..69395a591 100644 --- a/app/Livewire/Project/Shared/Storages/Show.php +++ b/app/Livewire/Project/Shared/Storages/Show.php @@ -77,15 +77,17 @@ public function submit() $this->dispatch('success', 'Storage updated successfully'); } - public function delete($password) + public function delete($password, $selectedActions = []) { $this->authorize('update', $this->resource); if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } $this->storage->delete(); $this->dispatch('refreshStorages'); + + return true; } } diff --git a/app/Livewire/Server/Delete.php b/app/Livewire/Server/Delete.php index e7b64b805..beb8c0a12 100644 --- a/app/Livewire/Server/Delete.php +++ b/app/Livewire/Server/Delete.php @@ -24,10 +24,14 @@ public function mount(string $server_uuid) } } - public function delete($password) + public function delete($password, $selectedActions = []) { if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; + } + + if (! empty($selectedActions)) { + $this->delete_from_hetzner = in_array('delete_from_hetzner', $selectedActions); } try { $this->authorize('delete', $this->server); diff --git a/app/Livewire/Server/Security/TerminalAccess.php b/app/Livewire/Server/Security/TerminalAccess.php index 310edcfe4..b4b99a3e7 100644 --- a/app/Livewire/Server/Security/TerminalAccess.php +++ b/app/Livewire/Server/Security/TerminalAccess.php @@ -31,7 +31,7 @@ public function mount(string $server_uuid) } } - public function toggleTerminal($password) + public function toggleTerminal($password, $selectedActions = []) { try { $this->authorize('update', $this->server); @@ -43,7 +43,7 @@ public function toggleTerminal($password) // Verify password if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } // Toggle the terminal setting @@ -55,6 +55,8 @@ public function toggleTerminal($password) $status = $this->isTerminalEnabled ? 'enabled' : 'disabled'; $this->dispatch('success', "Terminal access has been {$status}."); + + return true; } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Livewire/Team/AdminView.php b/app/Livewire/Team/AdminView.php index c8d44d42b..09878f27b 100644 --- a/app/Livewire/Team/AdminView.php +++ b/app/Livewire/Team/AdminView.php @@ -49,14 +49,14 @@ public function getUsers() } } - public function delete($id, $password) + public function delete($id, $password, $selectedActions = []) { if (! isInstanceAdmin()) { return redirect()->route('dashboard'); } if (! verifyPasswordConfirmation($password, $this)) { - return; + return 'The provided password is incorrect.'; } if (! auth()->user()->isInstanceAdmin()) { @@ -71,6 +71,8 @@ public function delete($id, $password) try { $user->delete(); $this->getUsers(); + + return true; } catch (\Exception $e) { return $this->dispatch('error', $e->getMessage()); } diff --git a/tests/v4/Feature/DangerDeleteResourceTest.php b/tests/v4/Feature/DangerDeleteResourceTest.php new file mode 100644 index 000000000..7a73f5979 --- /dev/null +++ b/tests/v4/Feature/DangerDeleteResourceTest.php @@ -0,0 +1,81 @@ + 0]); + Queue::fake(); + + $this->user = User::factory()->create([ + 'password' => Hash::make('test-password'), + ]); + $this->team = Team::factory()->create(); + $this->user->teams()->attach($this->team, ['role' => 'owner']); + + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + $this->destination = StandaloneDocker::factory()->create([ + 'server_id' => $this->server->id, + 'network' => 'test-network-'.fake()->unique()->word(), + ]); + $this->project = Project::factory()->create(['team_id' => $this->team->id]); + $this->environment = Environment::factory()->create(['project_id' => $this->project->id]); + + $this->application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + $this->actingAs($this->user); + session(['currentTeam' => $this->team]); + + // Bind route parameters so get_route_parameters() works in the Danger component + $route = Route::get('/test/{project_uuid}/{environment_uuid}', fn () => '')->name('test.danger'); + $request = Request::create("/test/{$this->project->uuid}/{$this->environment->uuid}"); + $route->bind($request); + app('router')->setRoutes(app('router')->getRoutes()); + Route::dispatch($request); +}); + +test('delete returns error string when password is incorrect', function () { + Livewire::test(Danger::class, ['resource' => $this->application]) + ->call('delete', 'wrong-password') + ->assertReturned('The provided password is incorrect.'); + + // Resource should NOT be deleted + expect(Application::find($this->application->id))->not->toBeNull(); +}); + +test('delete succeeds with correct password and redirects', function () { + Livewire::test(Danger::class, ['resource' => $this->application]) + ->call('delete', 'test-password') + ->assertHasNoErrors(); + + // Resource should be soft-deleted + expect(Application::find($this->application->id))->toBeNull(); +}); + +test('delete applies selectedActions from checkbox state', function () { + $component = Livewire::test(Danger::class, ['resource' => $this->application]) + ->call('delete', 'test-password', ['delete_configurations', 'docker_cleanup']); + + expect($component->get('delete_volumes'))->toBeFalse(); + expect($component->get('delete_connected_networks'))->toBeFalse(); + expect($component->get('delete_configurations'))->toBeTrue(); + expect($component->get('docker_cleanup'))->toBeTrue(); +});