mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-14 08:42:38 -04:00
Deleting a server with "Delete all resources" (UI) or ?force=true (API) deleted every application that used the server, also applications that use it only as an additional destination, for example after Promote. Server delete now deletes only resources whose main destination is on the server. Applications that use it only as an additional destination lose their additional_destinations row for the server, and their containers on it are stopped (best effort). The check for resources and the transfer archive path use the same Server::primaryResources(). The delete confirmation lists the resources to delete and the applications that keep running on another server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
163 lines
6.3 KiB
PHP
163 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Server;
|
|
|
|
use App\Actions\Server\DeleteServer;
|
|
use App\Jobs\DeleteResourceJob;
|
|
use App\Models\Server;
|
|
use App\Services\ServerTransfer\ServerTransferClaimer;
|
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
use Livewire\Component;
|
|
|
|
class Delete extends Component
|
|
{
|
|
use AuthorizesRequests;
|
|
|
|
public Server $server;
|
|
|
|
public bool $delete_from_hetzner = false;
|
|
|
|
public bool $delete_from_vultr = false;
|
|
|
|
public bool $delete_from_digitalocean = false;
|
|
|
|
public bool $delete_from_hostinger = false;
|
|
|
|
public bool $force_delete_resources = false;
|
|
|
|
public function mount(string $server_uuid)
|
|
{
|
|
try {
|
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function delete($password, $selectedActions = [])
|
|
{
|
|
if (! verifyPasswordConfirmation($password, $this)) {
|
|
return 'The provided password is incorrect.';
|
|
}
|
|
|
|
if (! empty($selectedActions)) {
|
|
$this->delete_from_hetzner = in_array('delete_from_hetzner', $selectedActions);
|
|
$this->delete_from_vultr = in_array('delete_from_vultr', $selectedActions);
|
|
$this->delete_from_digitalocean = in_array('delete_from_digitalocean', $selectedActions);
|
|
$this->delete_from_hostinger = in_array('delete_from_hostinger', $selectedActions);
|
|
$this->force_delete_resources = in_array('force_delete_resources', $selectedActions);
|
|
}
|
|
// Another Coolify instance manages a transferred server. This instance deletes only its own records:
|
|
// never the VM, and never the containers that the other instance runs.
|
|
$isArchive = $this->server->isTransferredAway();
|
|
if ($isArchive) {
|
|
$this->delete_from_hetzner = false;
|
|
$this->delete_from_vultr = false;
|
|
$this->delete_from_digitalocean = false;
|
|
$this->delete_from_hostinger = false;
|
|
$this->force_delete_resources = true;
|
|
}
|
|
try {
|
|
$this->authorize('delete', $this->server);
|
|
if ($this->server->is_coolify_host) {
|
|
$this->dispatch('error', 'The Coolify host server cannot be deleted.');
|
|
|
|
return;
|
|
}
|
|
$resources = $this->server->primaryResources();
|
|
if ($resources->isNotEmpty() && ! $this->force_delete_resources) {
|
|
$this->dispatch('error', 'Server has defined resources. Please delete them first or select "Delete all resources".');
|
|
|
|
return;
|
|
}
|
|
|
|
if ($isArchive) {
|
|
app(ServerTransferClaimer::class)->forgetDnsRecords($this->server);
|
|
}
|
|
// Applications that use the server only as an additional destination stay; they lose this server.
|
|
// Another instance manages a transferred server, so the containers stay as they are there.
|
|
$this->server->detachAdditionalApplications(stopContainers: ! $isArchive);
|
|
if ($this->force_delete_resources) {
|
|
foreach ($resources as $resource) {
|
|
DeleteResourceJob::dispatch($resource, deleteFromCoolifyOnly: $isArchive);
|
|
}
|
|
}
|
|
|
|
$this->server->delete();
|
|
DeleteServer::dispatch(
|
|
$this->server->id,
|
|
$this->delete_from_hetzner,
|
|
$this->server->hetzner_server_id,
|
|
$this->server->cloud_provider_token_id,
|
|
$this->server->team_id,
|
|
$this->delete_from_vultr,
|
|
$this->server->vultr_instance_id,
|
|
$this->delete_from_digitalocean,
|
|
$this->server->digitalocean_droplet_id,
|
|
$this->delete_from_hostinger,
|
|
$this->server->hostinger_virtual_machine_id
|
|
);
|
|
|
|
return redirectRoute($this, 'server.index');
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
$checkboxes = [];
|
|
|
|
$resourcesToDelete = $this->server->primaryResources();
|
|
$detachedApplications = $this->server->additionalApplications();
|
|
|
|
if ($resourcesToDelete->isNotEmpty() && ! $this->server->isTransferredAway()) {
|
|
$checkboxes[] = [
|
|
'id' => 'force_delete_resources',
|
|
'label' => "Delete all resources ({$resourcesToDelete->count()} total): ".$resourcesToDelete->pluck('name')->implode(', '),
|
|
'default_warning' => 'Server cannot be deleted while it has resources.',
|
|
];
|
|
}
|
|
|
|
$canDeleteFromProvider = ! $this->server->isTransferredAway();
|
|
|
|
if ($canDeleteFromProvider && $this->server->hetzner_server_id) {
|
|
$checkboxes[] = [
|
|
'id' => 'delete_from_hetzner',
|
|
'label' => 'Also delete server from Hetzner Cloud',
|
|
'default_warning' => 'The actual server on Hetzner Cloud will NOT be deleted.',
|
|
];
|
|
}
|
|
|
|
if ($canDeleteFromProvider && $this->server->vultr_instance_id) {
|
|
$checkboxes[] = [
|
|
'id' => 'delete_from_vultr',
|
|
'label' => 'Also delete server from Vultr',
|
|
'default_warning' => 'The actual server on Vultr will NOT be deleted.',
|
|
];
|
|
}
|
|
|
|
if ($canDeleteFromProvider && $this->server->digitalocean_droplet_id) {
|
|
$checkboxes[] = [
|
|
'id' => 'delete_from_digitalocean',
|
|
'label' => 'Also delete droplet from DigitalOcean',
|
|
'default_warning' => 'The actual droplet on DigitalOcean will NOT be deleted.',
|
|
];
|
|
}
|
|
|
|
if ($canDeleteFromProvider && $this->server->hostinger_virtual_machine_id) {
|
|
$checkboxes[] = [
|
|
'id' => 'delete_from_hostinger',
|
|
'label' => 'Also delete VPS from Hostinger (at the end of the paid period)',
|
|
'default_warning' => 'The VPS on Hostinger will NOT be deleted, and Hostinger will keep billing for it.',
|
|
];
|
|
}
|
|
|
|
return view('livewire.server.delete', [
|
|
'checkboxes' => $checkboxes,
|
|
'resourcesToDelete' => $resourcesToDelete,
|
|
'detachedApplications' => $detachedApplications,
|
|
]);
|
|
}
|
|
}
|