mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-14 08:42:38 -04:00
Import: - Check every uuid in a bundle against a strict id format before any write, and quote the SSH mux socket and key paths in local shell commands. A bundle uuid could run commands in the Coolify container. - Validate bundle fqdns, git source URLs, paths, networks, cron expressions, and database ports; cap the number of items. - Do not confirm that another team has a uuid. - Refuse a project icon path with a uuid that is not a plain id. Claim, finish, and migrate: - Pass checklist step keys to wire:click with Js::from (stored XSS). - Link only to https target server URLs. - Validate target_instance_url in /transfer/complete; never prefill an unverified URL for a transfer back; scope the transfer-back token to write+deploy for 1 hour. - Release on an owner marker only when it is newer than the own claim. - Do not touch apps that only use the server as an additional destination when an archive is released or deleted. - Give Sentinel a new token on claim. - Add a checklist step to remove the previous instance's SSH key. - Block a transfer while backups or scheduled tasks run. - Authorize refreshMigration and PrivateKey checkConnection, clear tokens from Livewire state, hide migration log/error from members. Archives: - Refuse ip, user, port, private key, and Cloudflare tunnel changes. - Throttle Enable management and require a free server slot for it and for a transfer back. - Skip cloud provider calls. - Re-read force_disabled before each SSH command, so running jobs stop. - Probe the import check target at the resolved address only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
160 lines
6.1 KiB
PHP
160 lines
6.1 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;
|
|
}
|
|
if ($this->server->hasDefinedResources() && ! $this->force_delete_resources) {
|
|
$this->dispatch('error', 'Server has defined resources. Please delete them first or select "Delete all resources".');
|
|
|
|
return;
|
|
}
|
|
|
|
$resources = $this->server->definedResources();
|
|
if ($isArchive) {
|
|
$claimer = app(ServerTransferClaimer::class);
|
|
$claimer->forgetDnsRecords($this->server);
|
|
// Applications that use the server only as an additional destination stay; they lose this server.
|
|
$claimer->detachAdditionalApplications($this->server);
|
|
$resources = $claimer->archiveResources($this->server);
|
|
}
|
|
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 = [];
|
|
|
|
if ($this->server->hasDefinedResources() && ! $this->server->isTransferredAway()) {
|
|
$resourceCount = $this->server->definedResources()->count();
|
|
$checkboxes[] = [
|
|
'id' => 'force_delete_resources',
|
|
'label' => "Delete all resources ({$resourceCount} total)",
|
|
'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,
|
|
]);
|
|
}
|
|
}
|