mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-12 15:53:46 -04:00
fix(api): validate delete_from_provider before deleting a server
Reject delete_from_provider values that are not booleans, and reject the request when the team has no token for a linked cloud provider. Before, both cases removed the server from Coolify but kept the cloud VM running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f885e1eeeb
commit
4145261b6e
@@ -11,6 +11,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Jobs\DeleteResourceJob;
|
||||
use App\Jobs\ValidateAndInstallServerJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\CloudProviderToken;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server as ModelsServer;
|
||||
@@ -900,9 +901,24 @@ class ServersController extends Controller
|
||||
return response()->json(['message' => 'Local server cannot be deleted.'], 400);
|
||||
}
|
||||
|
||||
$deleteFromProvider = filter_var($request->query('delete_from_provider', false), FILTER_VALIDATE_BOOLEAN);
|
||||
if ($deleteFromProvider && ! $server->hetzner_server_id && ! $server->vultr_instance_id && ! $server->digitalocean_droplet_id) {
|
||||
return response()->json(['message' => 'Server is not linked to a cloud provider.'], 422);
|
||||
$deleteFromProvider = filter_var($request->query('delete_from_provider', false), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
if (is_null($deleteFromProvider)) {
|
||||
return response()->json(['message' => 'delete_from_provider must be a boolean.'], 422);
|
||||
}
|
||||
if ($deleteFromProvider) {
|
||||
$linkedProviders = array_keys(array_filter([
|
||||
'hetzner' => $server->hetzner_server_id,
|
||||
'vultr' => $server->vultr_instance_id,
|
||||
'digitalocean' => $server->digitalocean_droplet_id,
|
||||
]));
|
||||
if (empty($linkedProviders)) {
|
||||
return response()->json(['message' => 'Server is not linked to a cloud provider.'], 422);
|
||||
}
|
||||
foreach ($linkedProviders as $provider) {
|
||||
if (! CloudProviderToken::whereTeamId($server->team_id)->whereProvider($provider)->exists()) {
|
||||
return response()->json(['message' => "No {$provider} token found for this team. Add one before deleting the server from the cloud provider."], 422);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($force) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Actions\Server\DeleteServer;
|
||||
use App\Models\CloudProviderToken;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
@@ -41,7 +42,8 @@ it('does not delete from the cloud provider by default', function () {
|
||||
&& $params[7] === false);
|
||||
});
|
||||
|
||||
it('deletes from the linked cloud provider when delete_from_provider is true', function (array $attributes) {
|
||||
it('deletes from the linked cloud provider when delete_from_provider is true', function (string $provider, array $attributes) {
|
||||
CloudProviderToken::factory()->create(['team_id' => $this->team->id, 'provider' => $provider]);
|
||||
$server = Server::factory()->create(['team_id' => $this->team->id, ...$attributes]);
|
||||
|
||||
deleteServerViaApi($this->token, $server->uuid, '?delete_from_provider=true')->assertOk();
|
||||
@@ -51,9 +53,25 @@ it('deletes from the linked cloud provider when delete_from_provider is true', f
|
||||
&& $params[5] === true
|
||||
&& $params[7] === true);
|
||||
})->with([
|
||||
'hetzner' => [['hetzner_server_id' => 123]],
|
||||
'vultr' => [['vultr_instance_id' => 'vultr-instance-id']],
|
||||
'digitalocean' => [['digitalocean_droplet_id' => 456]],
|
||||
'hetzner' => ['hetzner', ['hetzner_server_id' => 123]],
|
||||
'vultr' => ['vultr', ['vultr_instance_id' => 'vultr-instance-id']],
|
||||
'digitalocean' => ['digitalocean', ['digitalocean_droplet_id' => 456]],
|
||||
]);
|
||||
|
||||
it('keeps the server when the team has no token for the linked cloud provider', function (string $provider, array $attributes) {
|
||||
CloudProviderToken::factory()->create(['provider' => $provider]);
|
||||
$server = Server::factory()->create(['team_id' => $this->team->id, ...$attributes]);
|
||||
|
||||
deleteServerViaApi($this->token, $server->uuid, '?delete_from_provider=true')
|
||||
->assertStatus(422)
|
||||
->assertJson(['message' => "No {$provider} token found for this team. Add one before deleting the server from the cloud provider."]);
|
||||
|
||||
expect(Server::find($server->id))->not->toBeNull();
|
||||
DeleteServer::assertNotPushed();
|
||||
})->with([
|
||||
'hetzner' => ['hetzner', ['hetzner_server_id' => 123]],
|
||||
'vultr' => ['vultr', ['vultr_instance_id' => 'vultr-instance-id']],
|
||||
'digitalocean' => ['digitalocean', ['digitalocean_droplet_id' => 456]],
|
||||
]);
|
||||
|
||||
it('rejects delete_from_provider when the server has no cloud provider link', function () {
|
||||
@@ -78,3 +96,23 @@ it('does not delete a server of another team from the cloud provider', function
|
||||
expect(Server::find($otherServer->id))->not->toBeNull();
|
||||
DeleteServer::assertNotPushed();
|
||||
});
|
||||
|
||||
it('rejects an invalid delete_from_provider value without deleting the server', function (string $value) {
|
||||
CloudProviderToken::factory()->create(['team_id' => $this->team->id, 'provider' => 'hetzner']);
|
||||
$server = Server::factory()->create(['team_id' => $this->team->id, 'hetzner_server_id' => 123]);
|
||||
|
||||
deleteServerViaApi($this->token, $server->uuid, "?delete_from_provider={$value}")
|
||||
->assertStatus(422)
|
||||
->assertJson(['message' => 'delete_from_provider must be a boolean.']);
|
||||
|
||||
expect(Server::find($server->id))->not->toBeNull();
|
||||
DeleteServer::assertNotPushed();
|
||||
})->with(['ture', 'yes-please', '2']);
|
||||
|
||||
it('deletes only from Coolify when delete_from_provider is false', function () {
|
||||
$server = Server::factory()->create(['team_id' => $this->team->id, 'hetzner_server_id' => 123]);
|
||||
|
||||
deleteServerViaApi($this->token, $server->uuid, '?delete_from_provider=false')->assertOk();
|
||||
|
||||
DeleteServer::assertPushed(fn ($action, array $params) => $params[0] === $server->id && $params[1] === false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user