From df796dffa2598354b7edcf3965dd00d45787eebf Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Thu, 8 Aug 2024 01:02:48 +0200 Subject: [PATCH 001/101] fix delte networks and unused images of services when deleted --- app/Actions/Service/DeleteService.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 194cf4db9..b043082ac 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -3,6 +3,7 @@ namespace App\Actions\Service; use App\Models\Service; +use App\Actions\Server\CleanupDocker; use Lorisleiva\Actions\Concerns\AsAction; class DeleteService @@ -33,6 +34,11 @@ class DeleteService foreach ($storagesToDelete as $storage) { $commands[] = "docker volume rm -f $storage->name"; } + + $uuid = $service->uuid; + instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); + instant_remote_process(["docker network rm {$uuid}"], $server, false); + $commands[] = "docker rm -f $service->uuid"; instant_remote_process($commands, $server, false); @@ -50,6 +56,9 @@ class DeleteService $task->delete(); } $service->tags()->detach(); + $service->forceDelete(); + + CleanupDocker::run($server, true); } } } From 070daee28e8f5c91401c37c778b919f1270042a0 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Thu, 8 Aug 2024 01:19:17 +0200 Subject: [PATCH 002/101] remove networks and cleanup unused images when stoping dockercompose build pack containers --- app/Actions/Application/StopApplication.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Actions/Application/StopApplication.php b/app/Actions/Application/StopApplication.php index 1f05e29ac..70575c821 100644 --- a/app/Actions/Application/StopApplication.php +++ b/app/Actions/Application/StopApplication.php @@ -3,6 +3,7 @@ namespace App\Actions\Application; use App\Models\Application; +use App\Actions\Server\CleanupDocker; use Lorisleiva\Actions\Concerns\AsAction; class StopApplication @@ -13,7 +14,6 @@ class StopApplication { if ($application->destination->server->isSwarm()) { instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server); - return; } @@ -23,7 +23,7 @@ class StopApplication $servers->push($server); }); foreach ($servers as $server) { - if (! $server->isFunctional()) { + if (!$server->isFunctional()) { return 'Server is not functional'; } if ($previewDeployments) { @@ -44,10 +44,11 @@ class StopApplication } } if ($application->build_pack === 'dockercompose') { - // remove network $uuid = $application->uuid; instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); instant_remote_process(["docker network rm {$uuid}"], $server, false); + + CleanupDocker::run($server, true); } } } From 4d0acee95cf32173411523429598bb21774e1ca8 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:31:37 +0200 Subject: [PATCH 003/101] UI options for deletion WIP --- app/Actions/Service/DeleteService.php | 4 +-- app/Jobs/DeleteResourceJob.php | 31 +++++++++++++++---- .../livewire/project/shared/danger.blade.php | 4 ++- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index b043082ac..7c8eaf75d 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -10,7 +10,7 @@ class DeleteService { use AsAction; - public function handle(Service $service) + public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $deleteImages, bool $deleteNetworks) { try { $server = data_get($service, 'server'); @@ -61,4 +61,4 @@ class DeleteService CleanupDocker::run($server, true); } } -} +} \ No newline at end of file diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index dbf44dd5d..36f673986 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -6,6 +6,7 @@ use App\Actions\Application\StopApplication; use App\Actions\Database\StopDatabase; use App\Actions\Service\DeleteService; use App\Actions\Service\StopService; +use App\Actions\Server\CleanupDocker; use App\Models\Application; use App\Models\Service; use App\Models\StandaloneClickhouse; @@ -31,7 +32,11 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue public function __construct( public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false, - public bool $deleteVolumes = false) {} + public bool $deleteVolumes = false, + public bool $deleteImages = false, + public bool $deleteNetworks = false + ) { + } public function handle() { @@ -59,19 +64,33 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue break; } - if ($this->deleteVolumes && $this->resource->type() !== 'service') { - $this->resource?->delete_volumes($persistentStorages); - } if ($this->deleteConfigurations) { $this->resource?->delete_configurations(); } + + if ($this->deleteVolumes && $this->resource->type() !== 'service') { + $this->resource?->delete_volumes($persistentStorages); + } + + if ($this->deleteImages) { + // Logic to delete images + } + + if ($this->deleteNetworks) { + // Logic to delete networks + } + + $server = data_get($this->resource, 'server'); + if ($server) { + CleanupDocker::run($server, true); + } } catch (\Throwable $e) { ray($e->getMessage()); - send_internal_notification('ContainerStoppingJob failed with: '.$e->getMessage()); + send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage()); throw $e; } finally { $this->resource->forceDelete(); Artisan::queue('cleanup:stucked-resources'); } } -} +} \ No newline at end of file diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index 276061a8e..20bd7310b 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -12,5 +12,7 @@ + + - + \ No newline at end of file From 0135e2b5c02095aa43e67c370c70747a96cf7f83 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 00:30:11 +0200 Subject: [PATCH 004/101] add logic --- app/Actions/Application/StopApplication.php | 3 +- app/Actions/Service/DeleteService.php | 34 +++++++--- app/Jobs/DeleteResourceJob.php | 28 ++++---- app/Livewire/Project/Shared/Danger.php | 12 +++- app/Models/Application.php | 68 +++++++++++-------- app/Models/ServiceApplication.php | 4 +- .../livewire/project/shared/danger.blade.php | 9 ++- 7 files changed, 94 insertions(+), 64 deletions(-) diff --git a/app/Actions/Application/StopApplication.php b/app/Actions/Application/StopApplication.php index c81e90518..73abeba7a 100644 --- a/app/Actions/Application/StopApplication.php +++ b/app/Actions/Application/StopApplication.php @@ -43,8 +43,7 @@ class StopApplication } if ($application->build_pack === 'dockercompose') { $uuid = $application->uuid; - instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); - instant_remote_process(["docker network rm {$uuid}"], $server, false); + $application->delete_connected_networks($uuid); CleanupDocker::run($server, true); } diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 7c8eaf75d..f32a44262 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -10,14 +10,16 @@ class DeleteService { use AsAction; - public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $deleteImages, bool $deleteNetworks) + public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $deleteImages, bool $deleteConnectedNetworks) { try { $server = data_get($service, 'server'); + if ($server->isFunctional()) { $storagesToDelete = collect([]); $service->environment_variables()->delete(); + $commands = []; foreach ($service->applications()->get() as $application) { $storages = $application->persistentStorages()->get(); @@ -31,21 +33,34 @@ class DeleteService $storagesToDelete->push($storage); } } - foreach ($storagesToDelete as $storage) { - $commands[] = "docker volume rm -f $storage->name"; + + // Delete volumes if the flag is set + if ($deleteVolumes) { + foreach ($service->applications()->get() as $application) { + $persistentStorages = $application->persistentStorages()->get(); + $application->delete_volumes($persistentStorages); + } } - $uuid = $service->uuid; - instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); - instant_remote_process(["docker network rm {$uuid}"], $server, false); + // Delete networks if the flag is set + if ($deleteConnectedNetworks) { + $uuid = $service->uuid; + $service->delete_connected_networks($uuid); + } + // Command to remove the service itself $commands[] = "docker rm -f $service->uuid"; + // Execute all commands instant_remote_process($commands, $server, false); } } catch (\Exception $e) { throw new \Exception($e->getMessage()); } finally { + // Delete configurations if the flag is set + if ($deleteConfigurations) { + $service->delete_configurations(); + } foreach ($service->applications()->get() as $application) { $application->forceDelete(); } @@ -58,7 +73,10 @@ class DeleteService $service->tags()->detach(); $service->forceDelete(); - CleanupDocker::run($server, true); + // Run cleanup if images need to be deleted + if ($deleteImages) { + CleanupDocker::run($server, true); + } } } -} \ No newline at end of file +} diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index 36f673986..68036ee4a 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -31,10 +31,10 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue public function __construct( public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, - public bool $deleteConfigurations = false, - public bool $deleteVolumes = false, - public bool $deleteImages = false, - public bool $deleteNetworks = false + public bool $deleteConfigurations, + public bool $deleteVolumes, + public bool $deleteImages, + public bool $deleteConnectedNetworks ) { } @@ -60,7 +60,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue break; case 'service': StopService::run($this->resource); - DeleteService::run($this->resource); + DeleteService::run($this->resource, $this->deleteConfigurations, $this->deleteVolumes, $this->deleteImages, $this->deleteConnectedNetworks); break; } @@ -72,20 +72,16 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue $this->resource?->delete_volumes($persistentStorages); } - if ($this->deleteImages) { - // Logic to delete images - } - - if ($this->deleteNetworks) { - // Logic to delete networks - } - $server = data_get($this->resource, 'server'); - if ($server) { + if ($this->deleteImages && $server) { CleanupDocker::run($server, true); } + + if ($this->deleteConnectedNetworks) { + $uuid = $this->resource->uuid; // Get the UUID from the resource + $this->resource?->delete_connected_networks($uuid); // Pass the UUID to the method + } } catch (\Throwable $e) { - ray($e->getMessage()); send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage()); throw $e; } finally { @@ -93,4 +89,4 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue Artisan::queue('cleanup:stucked-resources'); } } -} \ No newline at end of file +} diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index 5f0178be4..cff1d453a 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -18,6 +18,10 @@ class Danger extends Component public bool $delete_volumes = true; + public bool $delete_images = true; + + public bool $delete_connected_networks = true; + public ?string $modalId = null; public function mount() @@ -33,7 +37,13 @@ class Danger extends Component try { // $this->authorize('delete', $this->resource); $this->resource->delete(); - DeleteResourceJob::dispatch($this->resource, $this->delete_configurations, $this->delete_volumes); + DeleteResourceJob::dispatch( + $this->resource, + $this->delete_configurations, + $this->delete_volumes, + $this->delete_images, + $this->delete_connected_networks + ); return redirect()->route('project.resource.index', [ 'project_uuid' => $this->projectUuid, diff --git a/app/Models/Application.php b/app/Models/Application.php index e2871da4b..324713bbf 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -152,7 +152,7 @@ class Application extends BaseModel $workdir = $this->workdir(); if (str($workdir)->endsWith($this->uuid)) { ray('Deleting workdir'); - instant_remote_process(['rm -rf '.$this->workdir()], $server, false); + instant_remote_process(['rm -rf ' . $this->workdir()], $server, false); } } @@ -173,6 +173,15 @@ class Application extends BaseModel } } + public function delete_connected_networks($uuid) + { + $server = data_get($this, 'destination.server'); + ray($uuid); + instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); + instant_remote_process(["docker network rm {$uuid}"], $server, false); + } + + public function additional_servers() { return $this->belongsToMany(Server::class, 'additional_destinations') @@ -280,7 +289,7 @@ class Application extends BaseModel public function publishDirectory(): Attribute { return Attribute::make( - set: fn ($value) => $value ? '/'.ltrim($value, '/') : null, + set: fn ($value) => $value ? '/' . ltrim($value, '/') : null, ); } @@ -288,7 +297,7 @@ class Application extends BaseModel { return Attribute::make( get: function () { - if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) { + if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) { if (str($this->git_repository)->contains('bitbucket')) { return "{$this->source->html_url}/{$this->git_repository}/src/{$this->git_branch}"; } @@ -315,7 +324,7 @@ class Application extends BaseModel { return Attribute::make( get: function () { - if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) { + if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) { return "{$this->source->html_url}/{$this->git_repository}/settings/hooks"; } // Convert the SSH URL to HTTPS URL @@ -334,7 +343,7 @@ class Application extends BaseModel { return Attribute::make( get: function () { - if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) { + if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) { return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}"; } // Convert the SSH URL to HTTPS URL @@ -351,7 +360,7 @@ class Application extends BaseModel public function gitCommitLink($link): string { - if (! is_null(data_get($this, 'source.html_url')) && ! is_null(data_get($this, 'git_repository')) && ! is_null(data_get($this, 'git_branch'))) { + if (!is_null(data_get($this, 'source.html_url')) && !is_null(data_get($this, 'git_repository')) && !is_null(data_get($this, 'git_branch'))) { if (str($this->source->html_url)->contains('bitbucket')) { return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}"; } @@ -362,7 +371,7 @@ class Application extends BaseModel $git_repository = str_replace('.git', '', $this->git_repository); $url = Url::fromString($git_repository); $url = $url->withUserInfo(''); - $url = $url->withPath($url->getPath().'/commits/'.$link); + $url = $url->withPath($url->getPath() . '/commits/' . $link); return $url->__toString(); } @@ -432,7 +441,7 @@ class Application extends BaseModel public function baseDirectory(): Attribute { return Attribute::make( - set: fn ($value) => '/'.ltrim($value, '/'), + set: fn ($value) => '/' . ltrim($value, '/'), ); } @@ -775,7 +784,7 @@ class Application extends BaseModel public function workdir() { - return application_configuration_dir()."/{$this->uuid}"; + return application_configuration_dir() . "/{$this->uuid}"; } public function isLogDrainEnabled() @@ -785,7 +794,7 @@ class Application extends BaseModel public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->fqdn.$this->git_repository.$this->git_branch.$this->git_commit_sha.$this->build_pack.$this->static_image.$this->install_command.$this->build_command.$this->start_command.$this->ports_exposes.$this->ports_mappings.$this->base_directory.$this->publish_directory.$this->dockerfile.$this->dockerfile_location.$this->custom_labels.$this->custom_docker_run_options.$this->dockerfile_target_build.$this->redirect; + $newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->ports_exposes . $this->ports_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels . $this->custom_docker_run_options . $this->dockerfile_target_build . $this->redirect; if ($this->pull_request_id === 0 || $this->pull_request_id === null) { $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); } else { @@ -839,7 +848,7 @@ class Application extends BaseModel public function dirOnServer() { - return application_configuration_dir()."/{$this->uuid}"; + return application_configuration_dir() . "/{$this->uuid}"; } public function setGitImportSettings(string $deployment_uuid, string $git_clone_command, bool $public = false) @@ -885,7 +894,7 @@ class Application extends BaseModel if ($this->source->is_public) { $fullRepoUrl = "{$this->source->html_url}/{$customRepository}"; $git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$customRepository} {$baseDir}"; - if (! $only_checkout) { + if (!$only_checkout) { $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: true); } if ($exec_in_docker) { @@ -902,7 +911,7 @@ class Application extends BaseModel $git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository} {$baseDir}"; $fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}"; } - if (! $only_checkout) { + if (!$only_checkout) { $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: false); } if ($exec_in_docker) { @@ -963,7 +972,7 @@ class Application extends BaseModel } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); } elseif ($git_type === 'github' || $git_type === 'gitea') { $branch = "pull/{$pull_request_id}/head:$pr_branch_name"; if ($exec_in_docker) { @@ -971,14 +980,14 @@ class Application extends BaseModel } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); } elseif ($git_type === 'bitbucket') { if ($exec_in_docker) { $commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'")); } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" ".$this->buildGitCheckoutCommand($commit); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit); } } @@ -1007,7 +1016,7 @@ class Application extends BaseModel } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); } elseif ($git_type === 'github' || $git_type === 'gitea') { $branch = "pull/{$pull_request_id}/head:$pr_branch_name"; if ($exec_in_docker) { @@ -1015,14 +1024,14 @@ class Application extends BaseModel } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); } elseif ($git_type === 'bitbucket') { if ($exec_in_docker) { $commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'")); } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" ".$this->buildGitCheckoutCommand($commit); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit); } } @@ -1074,20 +1083,20 @@ class Application extends BaseModel } if ($source->startsWith('.')) { $source = $source->after('.'); - $source = $workdir.$source; + $source = $workdir . $source; } $commands->push("mkdir -p $source > /dev/null 2>&1 || true"); } } } $labels = collect(data_get($service, 'labels', [])); - if (! $labels->contains('coolify.managed')) { + if (!$labels->contains('coolify.managed')) { $labels->push('coolify.managed=true'); } - if (! $labels->contains('coolify.applicationId')) { - $labels->push('coolify.applicationId='.$this->id); + if (!$labels->contains('coolify.applicationId')) { + $labels->push('coolify.applicationId=' . $this->id); } - if (! $labels->contains('coolify.type')) { + if (!$labels->contains('coolify.type')) { $labels->push('coolify.type=application'); } data_set($service, 'labels', $labels->toArray()); @@ -1161,7 +1170,7 @@ class Application extends BaseModel $jsonNames = $json->keys()->toArray(); $diff = array_diff($jsonNames, $names); $json = $json->filter(function ($value, $key) use ($diff) { - return ! in_array($key, $diff); + return !in_array($key, $diff); }); if ($json) { $this->docker_compose_domains = json_encode($json); @@ -1178,13 +1187,12 @@ class Application extends BaseModel } else { throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile

Check if you used the right extension (.yaml or .yml) in the compose file name."); } - } public function parseContainerLabels(?ApplicationPreview $preview = null) { $customLabels = data_get($this, 'custom_labels'); - if (! $customLabels) { + if (!$customLabels) { return; } if (base64_encode(base64_decode($customLabels, true)) !== $customLabels) { @@ -1267,10 +1275,10 @@ class Application extends BaseModel continue; } if (isset($healthcheckCommand) && str_contains($trimmedLine, '\\')) { - $healthcheckCommand .= ' '.trim($trimmedLine, '\\ '); + $healthcheckCommand .= ' ' . trim($trimmedLine, '\\ '); } - if (isset($healthcheckCommand) && ! str_contains($trimmedLine, '\\') && ! empty($healthcheckCommand)) { - $healthcheckCommand .= ' '.$trimmedLine; + if (isset($healthcheckCommand) && !str_contains($trimmedLine, '\\') && !empty($healthcheckCommand)) { + $healthcheckCommand .= ' ' . $trimmedLine; break; } } diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index 6690f254e..9df825869 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -23,7 +23,7 @@ class ServiceApplication extends BaseModel public function restart() { - $container_id = $this->name.'-'.$this->service->uuid; + $container_id = $this->name . '-' . $this->service->uuid; instant_remote_process(["docker restart {$container_id}"], $this->service->server); } @@ -59,7 +59,7 @@ class ServiceApplication extends BaseModel public function workdir() { - return service_configuration_dir()."/{$this->service->uuid}"; + return service_configuration_dir() . "/{$this->service->uuid}"; } public function serviceType() diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index 20bd7310b..f9eaec30f 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -9,10 +9,9 @@
This resource will be deleted. It is not reversible. Please think again.

Actions

- - - - + + + + \ No newline at end of file From 70aa05bde96a7a9a26d40903fc3dc81d3ba6e074 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 00:42:23 +0200 Subject: [PATCH 005/101] order in importance --- resources/views/livewire/project/shared/danger.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index f9eaec30f..6d00f357c 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -9,9 +9,9 @@
This resource will be deleted. It is not reversible. Please think again.

Actions

- - + + \ No newline at end of file From 51071da7006c7b15cc8f6828a8586d6678a072a4 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 01:00:07 +0200 Subject: [PATCH 006/101] fix order --- app/Jobs/DeleteResourceJob.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index 68036ee4a..50e0e646e 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -64,13 +64,12 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue break; } - if ($this->deleteConfigurations) { - $this->resource?->delete_configurations(); - } - if ($this->deleteVolumes && $this->resource->type() !== 'service') { $this->resource?->delete_volumes($persistentStorages); } + if ($this->deleteConfigurations) { + $this->resource?->delete_configurations(); + } $server = data_get($this->resource, 'server'); if ($this->deleteImages && $server) { From 86a087056e289873b474dacf0dd063b2b5e285cd Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:11:42 +0200 Subject: [PATCH 007/101] fix volume deletion for services --- app/Actions/Service/DeleteService.php | 46 ++++++++++--------- .../livewire/project/shared/danger.blade.php | 2 +- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index f32a44262..13b5cc50b 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -14,12 +14,10 @@ class DeleteService { try { $server = data_get($service, 'server'); - - if ($server->isFunctional()) { + if ($deleteVolumes && $server->isFunctional()) { $storagesToDelete = collect([]); $service->environment_variables()->delete(); - $commands = []; foreach ($service->applications()->get() as $application) { $storages = $application->persistentStorages()->get(); @@ -33,27 +31,33 @@ class DeleteService $storagesToDelete->push($storage); } } - - // Delete volumes if the flag is set - if ($deleteVolumes) { - foreach ($service->applications()->get() as $application) { - $persistentStorages = $application->persistentStorages()->get(); - $application->delete_volumes($persistentStorages); - } + foreach ($storagesToDelete as $storage) { + $commands[] = "docker volume rm -f $storage->name"; } - // Delete networks if the flag is set - if ($deleteConnectedNetworks) { - $uuid = $service->uuid; - $service->delete_connected_networks($uuid); - } - - // Command to remove the service itself - $commands[] = "docker rm -f $service->uuid"; - // Execute all commands - instant_remote_process($commands, $server, false); + if (!empty($commands)) { + foreach ($commands as $command) { + $result = instant_remote_process([$command], $server, false); + if ($result !== 0) { + ray("Failed to execute: $command"); + } + } + } } + + // Delete networks if the flag is set + if ($deleteConnectedNetworks) { + $uuid = $service->uuid; + $service->delete_connected_networks($uuid); + } + + // Command to remove the service itself + $commands[] = "docker rm -f $service->uuid"; + + // Execute all commands + instant_remote_process($commands, $server, false); + } catch (\Exception $e) { throw new \Exception($e->getMessage()); } finally { @@ -79,4 +83,4 @@ class DeleteService } } } -} +} \ No newline at end of file diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index 6d00f357c..174731eea 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -10,7 +10,7 @@ again.

Actions

- + From e67e03f73f2c8405b9bb96d6c1adb3f27c1b7e48 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:15:40 +0200 Subject: [PATCH 008/101] added comments and removed temp ones --- app/Actions/Service/DeleteService.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 13b5cc50b..521a70e83 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -35,7 +35,7 @@ class DeleteService $commands[] = "docker volume rm -f $storage->name"; } - // Execute all commands + // Execute volume deletion first, this must be done first otherwise volumes will not be deleted. if (!empty($commands)) { foreach ($commands as $command) { $result = instant_remote_process([$command], $server, false); @@ -46,22 +46,18 @@ class DeleteService } } - // Delete networks if the flag is set if ($deleteConnectedNetworks) { $uuid = $service->uuid; $service->delete_connected_networks($uuid); } - // Command to remove the service itself $commands[] = "docker rm -f $service->uuid"; - // Execute all commands + // Executing remaining commands instant_remote_process($commands, $server, false); - } catch (\Exception $e) { throw new \Exception($e->getMessage()); } finally { - // Delete configurations if the flag is set if ($deleteConfigurations) { $service->delete_configurations(); } @@ -77,10 +73,9 @@ class DeleteService $service->tags()->detach(); $service->forceDelete(); - // Run cleanup if images need to be deleted if ($deleteImages) { CleanupDocker::run($server, true); } } } -} \ No newline at end of file +} From 7722809c52a04f052bfd1d2632daaf88ca0527a7 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:20:32 +0200 Subject: [PATCH 009/101] typo --- resources/views/livewire/project/shared/danger.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index 174731eea..1e1365749 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -10,7 +10,7 @@ again.

Actions

- + From 53dff4ca4f6f34f2d47de2db4e9c960275da1a34 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:58:59 +0200 Subject: [PATCH 010/101] simplify uuid variabel --- app/Actions/Service/DeleteService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 521a70e83..186200993 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -47,14 +47,14 @@ class DeleteService } if ($deleteConnectedNetworks) { - $uuid = $service->uuid; - $service->delete_connected_networks($uuid); + $service->delete_connected_networks($service->uuid); } $commands[] = "docker rm -f $service->uuid"; // Executing remaining commands instant_remote_process($commands, $server, false); + } catch (\Exception $e) { throw new \Exception($e->getMessage()); } finally { From d980c7a4254900d4c98fefd84f2cc257e2128356 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 02:59:41 +0200 Subject: [PATCH 011/101] only run network removal on stop service if it is not a deletion operation --- app/Actions/Service/StopService.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index 3dd91b4e2..e7d108528 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -9,14 +9,14 @@ class StopService { use AsAction; - public function handle(Service $service) + public function handle(Service $service, bool $isDeleteOperation = false) { try { $server = $service->destination->server; - if (! $server->isFunctional()) { + if (!$server->isFunctional()) { return 'Server is not functional'; } - ray('Stopping service: '.$service->name); + ray('Stopping service: ' . $service->name); $applications = $service->applications()->get(); foreach ($applications as $application) { instant_remote_process(command: ["docker stop --time=30 {$application->name}-{$service->uuid}"], server: $server, throwError: false); @@ -31,13 +31,15 @@ class StopService instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false); $db->update(['status' => 'exited']); } - instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy"], $service->server); - instant_remote_process(["docker network rm {$service->uuid}"], $service->server); + + if (!$isDeleteOperation) { + // Only run this if not a delete operation + $service->delete_connected_networks($service->uuid); + } } catch (\Exception $e) { ray($e->getMessage()); return $e->getMessage(); } - } } From 97c2bedda27f9078c794ac952332681f1b2e5024 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 03:00:30 +0200 Subject: [PATCH 012/101] add delete_connected_networks function to services.php --- app/Models/Application.php | 2 -- app/Models/Service.php | 15 +++++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/Models/Application.php b/app/Models/Application.php index 324713bbf..d88e94e19 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -151,7 +151,6 @@ class Application extends BaseModel $server = data_get($this, 'destination.server'); $workdir = $this->workdir(); if (str($workdir)->endsWith($this->uuid)) { - ray('Deleting workdir'); instant_remote_process(['rm -rf ' . $this->workdir()], $server, false); } } @@ -176,7 +175,6 @@ class Application extends BaseModel public function delete_connected_networks($uuid) { $server = data_get($this, 'destination.server'); - ray($uuid); instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); instant_remote_process(["docker network rm {$uuid}"], $server, false); } diff --git a/app/Models/Service.php b/app/Models/Service.php index 33238281e..f56d05af1 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -56,7 +56,7 @@ class Service extends BaseModel $databaseStorages = $this->databases()->get()->pluck('persistentStorages')->flatten()->sortBy('id'); $storages = $applicationStorages->merge($databaseStorages)->implode('updated_at'); - $newConfigHash = $images.$domains.$images.$storages; + $newConfigHash = $images . $domains . $images . $storages; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -121,13 +121,20 @@ class Service extends BaseModel public function delete_configurations() { - $server = data_get($this, 'server'); + $server = data_get($this, 'destination.server'); $workdir = $this->workdir(); if (str($workdir)->endsWith($this->uuid)) { - instant_remote_process(['rm -rf '.$this->workdir()], $server, false); + instant_remote_process(['rm -rf ' . $this->workdir()], $server, false); } } + public function delete_connected_networks($uuid) + { + $server = data_get($this, 'destination.server'); + instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); + instant_remote_process(["docker network rm {$uuid}"], $server, false); + } + public function status() { $applications = $this->applications; @@ -907,7 +914,7 @@ class Service extends BaseModel public function workdir() { - return service_configuration_dir()."/{$this->uuid}"; + return service_configuration_dir() . "/{$this->uuid}"; } public function saveComposeConfigs() From 5595853379307589cf0b50dc4c573028ded651df Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 03:03:40 +0200 Subject: [PATCH 013/101] WIP database network, image removal --- app/Jobs/DeleteResourceJob.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index 50e0e646e..6e102266a 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -57,9 +57,19 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue case 'standalone-clickhouse': $persistentStorages = $this->resource?->persistentStorages()?->get(); StopDatabase::run($this->resource); + // TODO + // DBs do not have a network normally? + //if ($this->deleteConnectedNetworks) { + // $this->resource?->delete_connected_networks($this->resource->uuid); + // } + // } + // $server = data_get($this->resource, 'server'); + // if ($this->deleteImages && $server) { + // CleanupDocker::run($server, true); + // } break; case 'service': - StopService::run($this->resource); + StopService::run($this->resource, true); DeleteService::run($this->resource, $this->deleteConfigurations, $this->deleteVolumes, $this->deleteImages, $this->deleteConnectedNetworks); break; } @@ -77,8 +87,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue } if ($this->deleteConnectedNetworks) { - $uuid = $this->resource->uuid; // Get the UUID from the resource - $this->resource?->delete_connected_networks($uuid); // Pass the UUID to the method + $this->resource?->delete_connected_networks($this->resource->uuid); } } catch (\Throwable $e) { send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage()); From d177e49e62abfee19f4da56f8c01b6b29ab3974f Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:43:13 +0200 Subject: [PATCH 014/101] updated warning message and formating --- .../livewire/project/service/navbar.blade.php | 208 +++++++++--------- 1 file changed, 100 insertions(+), 108 deletions(-) diff --git a/resources/views/livewire/project/service/navbar.blade.php b/resources/views/livewire/project/service/navbar.blade.php index 125f9121a..67d6a78ca 100644 --- a/resources/views/livewire/project/service/navbar.blade.php +++ b/resources/views/livewire/project/service/navbar.blade.php @@ -10,130 +10,122 @@ @script - + @endscript From 72bcf03cbb942b49891e4683b3479cbf5441382d Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:59:41 +0200 Subject: [PATCH 015/101] graceful service container stop --- app/Actions/Service/StopService.php | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index e7d108528..933eca7d4 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -3,6 +3,7 @@ namespace App\Actions\Service; use App\Models\Service; +use App\Actions\Server\CleanupDocker; use Lorisleiva\Actions\Concerns\AsAction; class StopService @@ -19,27 +20,38 @@ class StopService ray('Stopping service: ' . $service->name); $applications = $service->applications()->get(); foreach ($applications as $application) { - instant_remote_process(command: ["docker stop --time=30 {$application->name}-{$service->uuid}"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false); + $this->stopContainer("{$application->name}-{$service->uuid}", $server, 600); $application->update(['status' => 'exited']); } $dbs = $service->databases()->get(); foreach ($dbs as $db) { - instant_remote_process(command: ["docker stop --time=30 {$db->name}-{$service->uuid}"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false); + $this->stopContainer("{$db->name}-{$service->uuid}", $server, 600); $db->update(['status' => 'exited']); } if (!$isDeleteOperation) { - // Only run this if not a delete operation + // Only run if not a deletion operation as for deletion we can specify if we want to delete networks or not $service->delete_connected_networks($service->uuid); + CleanupDocker::run($server, true); } } catch (\Exception $e) { ray($e->getMessage()); - return $e->getMessage(); } } + + private function stopContainer(string $containerName, $server, int $timeout = 600) + { + try { + instant_remote_process(command: ["docker stop --time=$timeout $containerName"], server: $server, throwError: false); + $isRunning = instant_remote_process(command: ["docker inspect -f '{{.State.Running}}' $containerName"], server: $server, throwError: false); + + if (trim($isRunning) === 'true') { + instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false); + } + } catch (\Exception $error) { + } + + instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false); + } } From 1cfddfd529025e8623cd8ca9b2cdba334fbf8a3f Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:17:58 +0200 Subject: [PATCH 016/101] fix stop large amount of containers --- app/Actions/Service/StopService.php | 76 +++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index 933eca7d4..035781885 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -5,6 +5,8 @@ namespace App\Actions\Service; use App\Models\Service; use App\Actions\Server\CleanupDocker; use Lorisleiva\Actions\Concerns\AsAction; +use Illuminate\Support\Facades\Process; +use Illuminate\Process\InvokedProcess; class StopService { @@ -18,19 +20,12 @@ class StopService return 'Server is not functional'; } ray('Stopping service: ' . $service->name); - $applications = $service->applications()->get(); - foreach ($applications as $application) { - $this->stopContainer("{$application->name}-{$service->uuid}", $server, 600); - $application->update(['status' => 'exited']); - } - $dbs = $service->databases()->get(); - foreach ($dbs as $db) { - $this->stopContainer("{$db->name}-{$service->uuid}", $server, 600); - $db->update(['status' => 'exited']); - } + + $containersToStop = $this->getContainersToStop($service); + + $this->stopContainers($containersToStop, $server); if (!$isDeleteOperation) { - // Only run if not a deletion operation as for deletion we can specify if we want to delete networks or not $service->delete_connected_networks($service->uuid); CleanupDocker::run($server, true); } @@ -40,18 +35,61 @@ class StopService } } - private function stopContainer(string $containerName, $server, int $timeout = 600) + private function getContainersToStop(Service $service): array { - try { - instant_remote_process(command: ["docker stop --time=$timeout $containerName"], server: $server, throwError: false); - $isRunning = instant_remote_process(command: ["docker inspect -f '{{.State.Running}}' $containerName"], server: $server, throwError: false); + $containersToStop = []; + $applications = $service->applications()->get(); + foreach ($applications as $application) { + $containersToStop[] = "{$application->name}-{$service->uuid}"; + } + $dbs = $service->databases()->get(); + foreach ($dbs as $db) { + $containersToStop[] = "{$db->name}-{$service->uuid}"; + } + return $containersToStop; + } - if (trim($isRunning) === 'true') { - instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false); - } - } catch (\Exception $error) { + private function stopContainers(array $containerNames, $server, int $timeout = 300) + { + $processes = []; + foreach ($containerNames as $containerName) { + $processes[$containerName] = $this->stopContainer($containerName, $server, $timeout); } + $startTime = time(); + while (count($processes) > 0) { + $finishedProcesses = array_filter($processes, function ($process) { + return !$process->running(); + }); + foreach ($finishedProcesses as $containerName => $process) { + unset($processes[$containerName]); + $this->removeContainer($containerName, $server); + } + + if (time() - $startTime >= $timeout) { + $this->forceStopRemainingContainers(array_keys($processes), $server); + break; + } + + usleep(100000); + } + } + + private function stopContainer(string $containerName, $server, int $timeout): InvokedProcess + { + return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); + } + + private function removeContainer(string $containerName, $server) + { instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false); } + + private function forceStopRemainingContainers(array $containerNames, $server) + { + foreach ($containerNames as $containerName) { + instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false); + $this->removeContainer($containerName, $server); + } + } } From a4bb87d13b5ac132c634feec986537aab93b3ad1 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:22:14 +0200 Subject: [PATCH 017/101] simplify DeleteService.php --- app/Actions/Service/DeleteService.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 186200993..238e6b954 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -50,11 +50,7 @@ class DeleteService $service->delete_connected_networks($service->uuid); } - $commands[] = "docker rm -f $service->uuid"; - - // Executing remaining commands - instant_remote_process($commands, $server, false); - + instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false); } catch (\Exception $e) { throw new \Exception($e->getMessage()); } finally { From 450351921eeae0f98f6b2ba157a16b8cf83e561b Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:39:21 +0200 Subject: [PATCH 018/101] added public functions --- app/Models/Application.php | 55 ++++++++++++++++++++++++++++++++++++++ app/Models/Service.php | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/app/Models/Application.php b/app/Models/Application.php index d88e94e19..2873ee7da 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -8,6 +8,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; use Illuminate\Support\Str; +use Illuminate\Support\Facades\Process; +use Illuminate\Process\InvokedProcess; use OpenApi\Attributes as OA; use RuntimeException; use Spatie\Activitylog\Models\Activity; @@ -146,6 +148,59 @@ class Application extends BaseModel return Application::whereRelation('environment.project.team', 'id', $teamId)->orderBy('name'); } + public function getContainersToStop(bool $previewDeployments = false): array + { + $containers = $previewDeployments + ? getCurrentApplicationContainerStatus($this->destination->server, $this->id, includePullrequests: true) + : getCurrentApplicationContainerStatus($this->destination->server, $this->id, 0); + + return $containers->pluck('Names')->toArray(); + } + + public function stopContainers(array $containerNames, $server, int $timeout = 600) + { + $processes = []; + foreach ($containerNames as $containerName) { + $processes[$containerName] = $this->stopContainer($containerName, $server, $timeout); + } + + $startTime = time(); + while (count($processes) > 0) { + $finishedProcesses = array_filter($processes, function ($process) { + return !$process->running(); + }); + foreach ($finishedProcesses as $containerName => $process) { + unset($processes[$containerName]); + $this->removeContainer($containerName, $server); + } + + if (time() - $startTime >= $timeout) { + $this->forceStopRemainingContainers(array_keys($processes), $server); + break; + } + + usleep(100000); + } + } + + public function stopContainer(string $containerName, $server, int $timeout): InvokedProcess + { + return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); + } + + public function removeContainer(string $containerName, $server) + { + instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false); + } + + public function forceStopRemainingContainers(array $containerNames, $server) + { + foreach ($containerNames as $containerName) { + instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false); + $this->removeContainer($containerName, $server); + } + } + public function delete_configurations() { $server = data_get($this, 'destination.server'); diff --git a/app/Models/Service.php b/app/Models/Service.php index f56d05af1..14213ee9a 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -7,6 +7,8 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Process; +use Illuminate\Process\InvokedProcess; use OpenApi\Attributes as OA; use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; @@ -119,6 +121,59 @@ class Service extends BaseModel return $this->morphToMany(Tag::class, 'taggable'); } + public function getContainersToStop(bool $previewDeployments = false): array + { + $containers = $previewDeployments + ? getCurrentApplicationContainerStatus($this->destination->server, $this->id, includePullrequests: true) + : getCurrentApplicationContainerStatus($this->destination->server, $this->id, 0); + + return $containers->pluck('Names')->toArray(); + } + + public function stopContainers(array $containerNames, $server, int $timeout = 600) + { + $processes = []; + foreach ($containerNames as $containerName) { + $processes[$containerName] = $this->stopContainer($containerName, $server, $timeout); + } + + $startTime = time(); + while (count($processes) > 0) { + $finishedProcesses = array_filter($processes, function ($process) { + return !$process->running(); + }); + foreach ($finishedProcesses as $containerName => $process) { + unset($processes[$containerName]); + $this->removeContainer($containerName, $server); + } + + if (time() - $startTime >= $timeout) { + $this->forceStopRemainingContainers(array_keys($processes), $server); + break; + } + + usleep(100000); + } + } + + public function stopContainer(string $containerName, $server, int $timeout): InvokedProcess + { + return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); + } + + public function removeContainer(string $containerName, $server) + { + instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false); + } + + public function forceStopRemainingContainers(array $containerNames, $server) + { + foreach ($containerNames as $containerName) { + instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false); + $this->removeContainer($containerName, $server); + } + } + public function delete_configurations() { $server = data_get($this, 'destination.server'); From 41be1f7666c67ce53afdf994427a9489d6d5fe96 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:39:43 +0200 Subject: [PATCH 019/101] use public function --- app/Actions/Application/StopApplication.php | 45 +++++--------- app/Actions/Service/StopService.php | 65 +-------------------- 2 files changed, 18 insertions(+), 92 deletions(-) diff --git a/app/Actions/Application/StopApplication.php b/app/Actions/Application/StopApplication.php index 73abeba7a..5f5846f55 100644 --- a/app/Actions/Application/StopApplication.php +++ b/app/Actions/Application/StopApplication.php @@ -12,41 +12,28 @@ class StopApplication public function handle(Application $application, bool $previewDeployments = false) { - if ($application->destination->server->isSwarm()) { - instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server); - return; - } - - $servers = collect([]); - $servers->push($application->destination->server); - $application->additional_servers->map(function ($server) use ($servers) { - $servers->push($server); - }); - foreach ($servers as $server) { + try { + $server = $application->destination->server; if (!$server->isFunctional()) { return 'Server is not functional'; } - if ($previewDeployments) { - $containers = getCurrentApplicationContainerStatus($server, $application->id, includePullrequests: true); - } else { - $containers = getCurrentApplicationContainerStatus($server, $application->id, 0); - } - if ($containers->count() > 0) { - foreach ($containers as $container) { - $containerName = data_get($container, 'Names'); - if ($containerName) { - instant_remote_process(command: ["docker stop --time=30 $containerName"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm $containerName"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm -f {$containerName}"], server: $server, throwError: false); - } - } - } - if ($application->build_pack === 'dockercompose') { - $uuid = $application->uuid; - $application->delete_connected_networks($uuid); + ray('Stopping application: ' . $application->name); + if ($server->isSwarm()) { + instant_remote_process(["docker stack rm {$application->uuid}"], $server); + return; + } + + $containersToStop = $application->getContainersToStop($previewDeployments); + $application->stopContainers($containersToStop, $server); + + if ($application->build_pack === 'dockercompose') { + $application->delete_connected_networks($application->uuid); CleanupDocker::run($server, true); } + } catch (\Exception $e) { + ray($e->getMessage()); + return $e->getMessage(); } } } diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index 035781885..6b348f830 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -5,8 +5,6 @@ namespace App\Actions\Service; use App\Models\Service; use App\Actions\Server\CleanupDocker; use Lorisleiva\Actions\Concerns\AsAction; -use Illuminate\Support\Facades\Process; -use Illuminate\Process\InvokedProcess; class StopService { @@ -21,9 +19,8 @@ class StopService } ray('Stopping service: ' . $service->name); - $containersToStop = $this->getContainersToStop($service); - - $this->stopContainers($containersToStop, $server); + $containersToStop = $service->getContainersToStop(); + $service->stopContainers($containersToStop, $server); if (!$isDeleteOperation) { $service->delete_connected_networks($service->uuid); @@ -34,62 +31,4 @@ class StopService return $e->getMessage(); } } - - private function getContainersToStop(Service $service): array - { - $containersToStop = []; - $applications = $service->applications()->get(); - foreach ($applications as $application) { - $containersToStop[] = "{$application->name}-{$service->uuid}"; - } - $dbs = $service->databases()->get(); - foreach ($dbs as $db) { - $containersToStop[] = "{$db->name}-{$service->uuid}"; - } - return $containersToStop; - } - - private function stopContainers(array $containerNames, $server, int $timeout = 300) - { - $processes = []; - foreach ($containerNames as $containerName) { - $processes[$containerName] = $this->stopContainer($containerName, $server, $timeout); - } - - $startTime = time(); - while (count($processes) > 0) { - $finishedProcesses = array_filter($processes, function ($process) { - return !$process->running(); - }); - foreach ($finishedProcesses as $containerName => $process) { - unset($processes[$containerName]); - $this->removeContainer($containerName, $server); - } - - if (time() - $startTime >= $timeout) { - $this->forceStopRemainingContainers(array_keys($processes), $server); - break; - } - - usleep(100000); - } - } - - private function stopContainer(string $containerName, $server, int $timeout): InvokedProcess - { - return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); - } - - private function removeContainer(string $containerName, $server) - { - instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false); - } - - private function forceStopRemainingContainers(array $containerNames, $server) - { - foreach ($containerNames as $containerName) { - instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false); - $this->removeContainer($containerName, $server); - } - } } From 2ca6ffb84e59d3221fb5a6c4d320fae5c091988d Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:57:53 +0200 Subject: [PATCH 020/101] fix public function service.php --- app/Actions/Service/StopService.php | 2 +- app/Models/Service.php | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index 6b348f830..85c61dc83 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -31,4 +31,4 @@ class StopService return $e->getMessage(); } } -} +} \ No newline at end of file diff --git a/app/Models/Service.php b/app/Models/Service.php index 14213ee9a..64796283c 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -121,20 +121,25 @@ class Service extends BaseModel return $this->morphToMany(Tag::class, 'taggable'); } - public function getContainersToStop(bool $previewDeployments = false): array + public function getContainersToStop(): array { - $containers = $previewDeployments - ? getCurrentApplicationContainerStatus($this->destination->server, $this->id, includePullrequests: true) - : getCurrentApplicationContainerStatus($this->destination->server, $this->id, 0); - - return $containers->pluck('Names')->toArray(); + $containersToStop = []; + $applications = $this->applications()->get(); + foreach ($applications as $application) { + $containersToStop[] = "{$application->name}-{$this->uuid}"; + } + $dbs = $this->databases()->get(); + foreach ($dbs as $db) { + $containersToStop[] = "{$db->name}-{$this->uuid}"; + } + return $containersToStop; } - public function stopContainers(array $containerNames, $server, int $timeout = 600) + public function stopContainers(array $containerNames, $server, int $timeout = 300) { $processes = []; foreach ($containerNames as $containerName) { - $processes[$containerName] = $this->stopContainer($containerName, $server, $timeout); + $processes[$containerName] = $this->stopContainer($containerName, $timeout); } $startTime = time(); @@ -142,7 +147,7 @@ class Service extends BaseModel $finishedProcesses = array_filter($processes, function ($process) { return !$process->running(); }); - foreach ($finishedProcesses as $containerName => $process) { + foreach (array_keys($finishedProcesses) as $containerName) { unset($processes[$containerName]); $this->removeContainer($containerName, $server); } @@ -156,7 +161,7 @@ class Service extends BaseModel } } - public function stopContainer(string $containerName, $server, int $timeout): InvokedProcess + public function stopContainer(string $containerName, int $timeout): InvokedProcess { return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); } From c566152f3777a53e4e86dd7713a08599e889f3a5 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 20:28:57 +0200 Subject: [PATCH 021/101] improve graceful_shutdown_container --- app/Jobs/ApplicationDeploymentJob.php | 45 ++++++++++++++++++++------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 473cbc679..42c1ba43c 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -28,6 +28,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; use Illuminate\Support\Sleep; use Illuminate\Support\Str; +use Illuminate\Support\Facades\Process; use RuntimeException; use Symfony\Component\Yaml\Yaml; use Throwable; @@ -894,7 +895,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue $envs->push("COOLIFY_FQDN={$this->application->fqdn}"); } if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) { - $url = str($this->application->fqdn)->replace('http://', '')->replace('https://', ''); + $url = str_replace('http://', '', $this->application->fqdn); + $url = str_replace('https://', '', $url); $envs->push("COOLIFY_URL={$url}"); } if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) { @@ -2027,24 +2029,43 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); $this->application_deployment_queue->addLogEntry('Building docker image completed.'); } - /** - * @param int $timeout in seconds - */ - private function graceful_shutdown_container(string $containerName, int $timeout = 30) + private function graceful_shutdown_container(string $containerName, int $timeout = 300) { try { - $this->execute_remote_command( - ["docker stop --time=$timeout $containerName", 'hidden' => true, 'ignore_errors' => true], - ["docker rm $containerName", 'hidden' => true, 'ignore_errors' => true] - ); + $process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); + + $startTime = time(); + while ($process->running()) { + if (time() - $startTime >= $timeout) { + $this->execute_remote_command( + ["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true] + ); + break; + } + usleep(100000); + } + + $isRunning = $this->execute_remote_command( + ["docker inspect -f '{{.State.Running}}' $containerName", 'hidden' => true, 'ignore_errors' => true] + ) === 'true'; + + if ($isRunning) { + $this->execute_remote_command( + ["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true] + ); + } } catch (\Exception $error) { - // report error if needed + $this->application_deployment_queue->addLogEntry("Error stopping container $containerName: " . $error->getMessage(), 'stderr'); } + $this->remove_container($containerName); + } + + private function remove_container(string $containerName) + { $this->execute_remote_command( ["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true] ); - } private function stop_running_container(bool $force = false) @@ -2057,7 +2078,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); if ($this->pull_request_id === 0) { $containers = $containers->filter(function ($container) { - return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id; + return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id; }); } $containers->each(function ($container) { From 16a5c601e3f93d8a8fc43672958299f6bacbcd7a Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:15:45 +0200 Subject: [PATCH 022/101] graceful db stop and db deletion fixes --- app/Actions/Database/StopDatabase.php | 50 ++++++++++++++++++++++++--- app/Actions/Service/StopService.php | 2 +- app/Jobs/DeleteResourceJob.php | 16 ++------- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/app/Actions/Database/StopDatabase.php b/app/Actions/Database/StopDatabase.php index d562ec56f..29ce794cf 100644 --- a/app/Actions/Database/StopDatabase.php +++ b/app/Actions/Database/StopDatabase.php @@ -10,25 +10,65 @@ use App\Models\StandaloneMongodb; use App\Models\StandaloneMysql; use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; +use Illuminate\Support\Facades\Process; use Lorisleiva\Actions\Concerns\AsAction; +use App\Actions\Server\CleanupDocker; class StopDatabase { use AsAction; - public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database) + public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database, bool $isDeleteOperation = false) { $server = $database->destination->server; - if (! $server->isFunctional()) { + if (!$server->isFunctional()) { return 'Server is not functional'; } - instant_remote_process(command: ["docker stop --time=30 $database->uuid"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm $database->uuid"], server: $server, throwError: false); - instant_remote_process(command: ["docker rm -f $database->uuid"], server: $server, throwError: false); + $this->stopContainer($database, $database->uuid, 300); + if (!$isDeleteOperation) { + $this->deleteConnectedNetworks($database->uuid, $server); //Probably not needed as DBs do not have a network normally + CleanupDocker::run($server, true); + } if ($database->is_public) { StopDatabaseProxy::run($database); } + + return 'Database stopped successfully'; + } + + private function stopContainer($database, string $containerName, int $timeout = 300): void + { + $server = $database->destination->server; + + $process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); + + $startTime = time(); + while ($process->running()) { + if (time() - $startTime >= $timeout) { + $this->forceStopContainer($containerName, $server); + break; + } + usleep(100000); + } + + $this->removeContainer($containerName, $server); + } + + private function forceStopContainer(string $containerName, $server): void + { + instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false); + } + + private function removeContainer(string $containerName, $server): void + { + instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false); + } + + private function deleteConnectedNetworks($uuid, $server) + { + instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false); + instant_remote_process(["docker network rm {$uuid}"], $server, false); } } diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index 85c61dc83..6b348f830 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -31,4 +31,4 @@ class StopService return $e->getMessage(); } } -} \ No newline at end of file +} diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index 6e102266a..1f4e835e9 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -56,17 +56,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue case 'standalone-dragonfly': case 'standalone-clickhouse': $persistentStorages = $this->resource?->persistentStorages()?->get(); - StopDatabase::run($this->resource); - // TODO - // DBs do not have a network normally? - //if ($this->deleteConnectedNetworks) { - // $this->resource?->delete_connected_networks($this->resource->uuid); - // } - // } - // $server = data_get($this->resource, 'server'); - // if ($this->deleteImages && $server) { - // CleanupDocker::run($server, true); - // } + StopDatabase::run($this->resource, true); break; case 'service': StopService::run($this->resource, true); @@ -80,10 +70,10 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue if ($this->deleteConfigurations) { $this->resource?->delete_configurations(); } - + $server = data_get($this->resource, 'server'); if ($this->deleteImages && $server) { - CleanupDocker::run($server, true); + CleanupDocker::run($server, true); //this is run for DBs but it does not work for DBs } if ($this->deleteConnectedNetworks) { From 7d1179e7c89bf38d5607c8f52ff1b6eb559e6d70 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:25:39 +0200 Subject: [PATCH 023/101] fix cleanup images for databases --- app/Jobs/DeleteResourceJob.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index 1f4e835e9..b0983bc47 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -69,11 +69,21 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue } if ($this->deleteConfigurations) { $this->resource?->delete_configurations(); + ray('Iam am running now, Configurations should disapear right?'); } - $server = data_get($this->resource, 'server'); - if ($this->deleteImages && $server) { - CleanupDocker::run($server, true); //this is run for DBs but it does not work for DBs + $isDatabase = $this->resource instanceof StandalonePostgresql + || $this->resource instanceof StandaloneRedis + || $this->resource instanceof StandaloneMongodb + || $this->resource instanceof StandaloneMysql + || $this->resource instanceof StandaloneMariadb + || $this->resource instanceof StandaloneKeydb + || $this->resource instanceof StandaloneDragonfly + || $this->resource instanceof StandaloneClickhouse; + $server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server'); + if (($this->deleteImages || $isDatabase) && $server) { + CleanupDocker::run($server, true); + ray('I am running now, images should disappear right?'); } if ($this->deleteConnectedNetworks) { From 840e225aa86befa895a41a789106fe317e85d66d Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 22:43:18 +0200 Subject: [PATCH 024/101] formatting and waring text --- app/Jobs/DeleteResourceJob.php | 4 +- app/Models/ServiceDatabase.php | 4 +- .../project/database/heading.blade.php | 128 ++++++++---------- 3 files changed, 63 insertions(+), 73 deletions(-) diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index b0983bc47..b8a8756cb 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -69,9 +69,8 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue } if ($this->deleteConfigurations) { $this->resource?->delete_configurations(); - ray('Iam am running now, Configurations should disapear right?'); } - + $isDatabase = $this->resource instanceof StandalonePostgresql || $this->resource instanceof StandaloneRedis || $this->resource instanceof StandaloneMongodb @@ -83,7 +82,6 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue $server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server'); if (($this->deleteImages || $isDatabase) && $server) { CleanupDocker::run($server, true); - ray('I am running now, images should disappear right?'); } if ($this->deleteConnectedNetworks) { diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index 4a749913e..7bbcb4f4e 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -21,7 +21,7 @@ class ServiceDatabase extends BaseModel public function restart() { - $container_id = $this->name.'-'.$this->service->uuid; + $container_id = $this->name . '-' . $this->service->uuid; remote_process(["docker restart {$container_id}"], $this->service->server); } @@ -78,7 +78,7 @@ class ServiceDatabase extends BaseModel public function workdir() { - return service_configuration_dir()."/{$this->service->uuid}"; + return service_configuration_dir() . "/{$this->service->uuid}"; } public function service() diff --git a/resources/views/livewire/project/database/heading.blade.php b/resources/views/livewire/project/database/heading.blade.php index cee1f0520..73824421d 100644 --- a/resources/views/livewire/project/database/heading.blade.php +++ b/resources/views/livewire/project/database/heading.blade.php @@ -7,88 +7,80 @@ - + \ No newline at end of file From b5360e5e7518b3f804301d255f87fe7958eec23e Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 23:12:30 +0200 Subject: [PATCH 025/101] improve CleanupDocker.php --- app/Actions/Server/CleanupDocker.php | 35 ++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index 0009e001d..82515ab6b 100644 --- a/app/Actions/Server/CleanupDocker.php +++ b/app/Actions/Server/CleanupDocker.php @@ -11,15 +11,36 @@ class CleanupDocker public function handle(Server $server, bool $force = true) { - // cleanup docker images, containers, and builder caches + $commonCommands = [ + 'docker container prune -f --filter "label=coolify.managed=true"', + 'docker image prune -f', + 'docker builder prune -f', + 'docker network prune -f', + ]; + + $forceCommands = [ + 'docker container rm $(docker container ls -aq --filter status=exited --filter status=created)', + 'docker image prune -af', + 'docker builder prune -af', + 'docker system prune -af', + 'docker network prune -f', + ]; + + $additionalCommands = [ + 'docker rmi $(docker images -f "dangling=true" -q)', + 'docker network rm $(docker network ls -q -f "unused=true")', + 'docker system prune -f', + ]; + if ($force) { - instant_remote_process(['docker image prune -af'], $server, false); - instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false); - instant_remote_process(['docker builder prune -af'], $server, false); + $commands = array_merge($forceCommands, $commonCommands, $additionalCommands); + $commands[] = 'docker rm $(docker ps -a -q --filter status=exited --filter status=created)'; } else { - instant_remote_process(['docker image prune -f'], $server, false); - instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false); - instant_remote_process(['docker builder prune -f'], $server, false); + $commands = array_merge($commonCommands, $additionalCommands); + } + + foreach ($commands as $command) { + instant_remote_process([$command], $server, false); } } } From 5b54dc8792f884e28a7a5b7606086fb880c5cf77 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Fri, 9 Aug 2024 23:25:57 +0200 Subject: [PATCH 026/101] Revert "improve CleanupDocker.php" This reverts commit b5360e5e7518b3f804301d255f87fe7958eec23e. --- app/Actions/Server/CleanupDocker.php | 35 ++++++---------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index 82515ab6b..0009e001d 100644 --- a/app/Actions/Server/CleanupDocker.php +++ b/app/Actions/Server/CleanupDocker.php @@ -11,36 +11,15 @@ class CleanupDocker public function handle(Server $server, bool $force = true) { - $commonCommands = [ - 'docker container prune -f --filter "label=coolify.managed=true"', - 'docker image prune -f', - 'docker builder prune -f', - 'docker network prune -f', - ]; - - $forceCommands = [ - 'docker container rm $(docker container ls -aq --filter status=exited --filter status=created)', - 'docker image prune -af', - 'docker builder prune -af', - 'docker system prune -af', - 'docker network prune -f', - ]; - - $additionalCommands = [ - 'docker rmi $(docker images -f "dangling=true" -q)', - 'docker network rm $(docker network ls -q -f "unused=true")', - 'docker system prune -f', - ]; - + // cleanup docker images, containers, and builder caches if ($force) { - $commands = array_merge($forceCommands, $commonCommands, $additionalCommands); - $commands[] = 'docker rm $(docker ps -a -q --filter status=exited --filter status=created)'; + instant_remote_process(['docker image prune -af'], $server, false); + instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false); + instant_remote_process(['docker builder prune -af'], $server, false); } else { - $commands = array_merge($commonCommands, $additionalCommands); - } - - foreach ($commands as $command) { - instant_remote_process([$command], $server, false); + instant_remote_process(['docker image prune -f'], $server, false); + instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false); + instant_remote_process(['docker builder prune -f'], $server, false); } } } From 2a581147aa72c56aae32c731f0f51c81eb8d1e6e Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:43:59 +0200 Subject: [PATCH 027/101] new confirm delete dialog --- .../components/modal-confirmation.blade.php | 109 ++++-------------- .../livewire/project/shared/danger.blade.php | 72 ++++++++++-- 2 files changed, 86 insertions(+), 95 deletions(-) diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php index 7910d2eb2..56abba71d 100644 --- a/resources/views/components/modal-confirmation.blade.php +++ b/resources/views/components/modal-confirmation.blade.php @@ -1,62 +1,41 @@ @props([ 'title' => 'Are you sure?', 'isErrorButton' => false, - 'buttonTitle' => 'REWRITE THIS BUTTON TITLE PLEASSSSEEEE', + 'buttonTitle' => 'Confirm Action', 'buttonFullWidth' => false, 'customButton' => null, 'disabled' => false, 'action' => 'delete', 'content' => null, ]) -
@if ($customButton) - @if ($buttonFullWidth) - - {{ $customButton }} - - @else - - {{ $customButton }} - - @endif + + {{ $customButton }} + @else @if ($content)
{{ $content }}
@else - @if ($disabled) - @if ($buttonFullWidth) - - {{ $buttonTitle }} - - @else - - {{ $buttonTitle }} - - @endif - @elseif ($isErrorButton) - @if ($buttonFullWidth) - - {{ $buttonTitle }} - - @else - - {{ $buttonTitle }} - - @endif - @else - @if ($buttonFullWidth) - - {{ $buttonTitle }} - - @else - - {{ $buttonTitle }} - - @endif - @endif + + {{ $buttonTitle }} + @endif @endif diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index 1e1365749..1b24d0593 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -5,13 +5,69 @@
This will stop your containers, delete all related data, etc. Beware! There is no coming back!
+ -
This resource will be deleted. It is not reversible. Please think - again.

-

Actions

- - - - +
+ +
+
Select the actions you want to perform:
+ + + + +
+ Cancel + + Continue + +
+
+ + +
+ +
The following actions will be performed:
+
    + +
+
Please type DELETE to confirm this destructive action:
+ +
+ Back + + Permanently Delete + +
+
+
-
\ No newline at end of file + From ac50d8b4d8f4c9e34ff300a080825e9383433854 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:57:03 +0200 Subject: [PATCH 028/101] fix styling --- resources/views/livewire/project/shared/danger.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index 1b24d0593..c021208f0 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -26,10 +26,10 @@
Cancel Continue @@ -55,7 +55,7 @@
Please type DELETE to confirm this destructive action:
- +
Back Date: Tue, 27 Aug 2024 13:44:12 +0200 Subject: [PATCH 029/101] confirm with password --- app/Livewire/Project/Shared/Danger.php | 9 +- .../livewire/project/shared/danger.blade.php | 152 ++++++++++-------- 2 files changed, 93 insertions(+), 68 deletions(-) diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index cff1d453a..5a49460da 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -5,6 +5,8 @@ namespace App\Livewire\Project\Shared; use App\Jobs\DeleteResourceJob; use Livewire\Component; use Visus\Cuid2\Cuid2; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Auth; class Danger extends Component { @@ -32,8 +34,13 @@ class Danger extends Component $this->environmentName = data_get($parameters, 'environment_name'); } - public function delete() + public function delete($selectedActions, $password) { + if (!Hash::check($password, Auth::user()->password)) { + $this->addError('password', 'The provided password is incorrect.'); + return; + } + try { // $this->authorize('delete', $this->resource); $this->resource->delete(); diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index c021208f0..11521a8c6 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -1,73 +1,91 @@ -
-

Danger Zone

-
Woah. I hope you know what are you doing.
-

Delete Resource

-
This will stop your containers, delete all related data, etc. Beware! There is no coming - back! -
+
+

Danger Zone

- -
- -
-
Select the actions you want to perform:
- - - - -
- Cancel - - Continue - -
+
+
+
+

Delete Resource

+

Once you delete a resource, there is no going back. Please be certain.

+ +
+ +
+
Select the actions you want to perform:
+ + + + +
+ Cancel + + Continue + +
+
- -
- -
The following actions will be performed:
-
    - -
-
Please type DELETE to confirm this destructive action:
- -
- Back - - Permanently Delete - -
-
+
-
+
From 472667624821eb783db40b7287f54a72015c9fdb Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:19:37 +0200 Subject: [PATCH 030/101] make things more clear --- app/Actions/Service/DeleteService.php | 4 ++-- app/Jobs/DeleteResourceJob.php | 6 ++--- app/Livewire/Project/Shared/Danger.php | 4 ++-- .../components/modal-confirmation.blade.php | 18 ++++++--------- .../livewire/project/shared/danger.blade.php | 22 +++++++++++++------ 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 238e6b954..93c79383e 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -10,7 +10,7 @@ class DeleteService { use AsAction; - public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $deleteImages, bool $deleteConnectedNetworks) + public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $dockerCleanup, bool $deleteConnectedNetworks) { try { $server = data_get($service, 'server'); @@ -69,7 +69,7 @@ class DeleteService $service->tags()->detach(); $service->forceDelete(); - if ($deleteImages) { + if ($dockerCleanup) { CleanupDocker::run($server, true); } } diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index b8a8756cb..1ebbf4681 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -33,7 +33,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations, public bool $deleteVolumes, - public bool $deleteImages, + public bool $dockerCleanup, public bool $deleteConnectedNetworks ) { } @@ -60,7 +60,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue break; case 'service': StopService::run($this->resource, true); - DeleteService::run($this->resource, $this->deleteConfigurations, $this->deleteVolumes, $this->deleteImages, $this->deleteConnectedNetworks); + DeleteService::run($this->resource, $this->deleteConfigurations, $this->deleteVolumes, $this->dockerCleanup, $this->deleteConnectedNetworks); break; } @@ -80,7 +80,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue || $this->resource instanceof StandaloneDragonfly || $this->resource instanceof StandaloneClickhouse; $server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server'); - if (($this->deleteImages || $isDatabase) && $server) { + if (($this->dockerCleanup || $isDatabase) && $server) { CleanupDocker::run($server, true); } diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index 5a49460da..1fe7ec21a 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -20,7 +20,7 @@ class Danger extends Component public bool $delete_volumes = true; - public bool $delete_images = true; + public bool $docker_cleanup = true; public bool $delete_connected_networks = true; @@ -48,7 +48,7 @@ class Danger extends Component $this->resource, $this->delete_configurations, $this->delete_volumes, - $this->delete_images, + $this->docker_cleanup, $this->delete_connected_networks ); diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php index 56abba71d..46910310c 100644 --- a/resources/views/components/modal-confirmation.blade.php +++ b/resources/views/components/modal-confirmation.blade.php @@ -8,15 +8,7 @@ 'action' => 'delete', 'content' => null, ]) -
@if ($customButton) @@ -32,7 +24,6 @@ @click="modalOpen=true" class="{{ $buttonFullWidth ? 'w-full' : '' }} {{ $isErrorButton ? 'bg-red-500 hover:bg-red-600 text-white' : '' }}" :disabled="$disabled" - wire:target > {{ $buttonTitle }} @@ -43,7 +34,7 @@ class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen" x-cloak>

{{ $title }}

+
{{ $slot }} diff --git a/resources/views/livewire/project/shared/danger.blade.php b/resources/views/livewire/project/shared/danger.blade.php index 11521a8c6..d7360310f 100644 --- a/resources/views/livewire/project/shared/danger.blade.php +++ b/resources/views/livewire/project/shared/danger.blade.php @@ -14,23 +14,25 @@ >
-
Select the actions you want to perform:
+
+
Select the actions you want to perform:
+
- +
Cancel - + Continue
@@ -44,6 +46,12 @@
The following actions will be performed:
    +
  • + + + + All containers of this resource will be stopped and permanently deleted. +
  • From 76cb473db80528056a25b22bb10b2ab50385596b Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Sat, 31 Aug 2024 13:06:55 +0200 Subject: [PATCH 048/101] fix default checkbox state --- .../components/modal-confirmation.blade.php | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php index acf641203..0896a1bc8 100644 --- a/resources/views/components/modal-confirmation.blade.php +++ b/resources/views/components/modal-confirmation.blade.php @@ -34,7 +34,7 @@ copied: false, submitAction: @js($submitAction), passwordError: '', - selectedActions: [], + selectedActions: @js(collect($checkboxes)->pluck('id')->all()), resetModal() { this.step = this.initialStep; this.deleteText = ''; @@ -73,7 +73,14 @@ this.copied = false; }, 2000); }, - + toggleAction(id) { + const index = this.selectedActions.indexOf(id); + if (index > -1) { + this.selectedActions.splice(index, 1); + } else { + this.selectedActions.push(id); + } + } }" @keydown.escape.window="modalOpen = false; resetModal()" :class="{ 'z-40': modalOpen }" class="relative w-auto h-auto" @password-error.window="passwordError = $event.detail"> @if ($customButton) @if ($buttonFullWidth) @@ -144,7 +151,13 @@
    Select the actions you want to perform:
@foreach($checkboxes as $index => $checkbox) - + @endforeach
@endif @@ -166,12 +179,14 @@ @endforeach @foreach($checkboxes as $checkbox) -
  • - - - - {{ $checkbox['label'] }} -
  • + @endforeach @if($confirmWithText) From bcfca40f3a7c979fed16c26742d797c61553cea6 Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Sat, 31 Aug 2024 13:10:45 +0200 Subject: [PATCH 049/101] rest checkboxes on close --- resources/views/components/modal-confirmation.blade.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php index 0896a1bc8..02283a1a6 100644 --- a/resources/views/components/modal-confirmation.blade.php +++ b/resources/views/components/modal-confirmation.blade.php @@ -41,6 +41,7 @@ this.password = ''; this.userConfirmText = ''; this.passwordError = ''; + this.selectedActions = @js(collect($checkboxes)->pluck('id')->all()); }, step1ButtonText: @js($step1ButtonText), step2ButtonText: @js($step2ButtonText), @@ -157,6 +158,7 @@ :label="$checkbox['label']" x-on:change="toggleAction('{{ $checkbox['id'] }}')" :checked="true" + x-bind:checked="selectedActions.includes('{{ $checkbox['id'] }}')" > @endforeach
    From b118a627d001caef33a1be4bf918b817edb75b4f Mon Sep 17 00:00:00 2001 From: ayntk-ai <122374094+ayntk-ai@users.noreply.github.com> Date: Sat, 31 Aug 2024 13:41:08 +0200 Subject: [PATCH 050/101] fix password validation and password error --- .../components/modal-confirmation.blade.php | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php index 02283a1a6..f8b0c4a26 100644 --- a/resources/views/components/modal-confirmation.blade.php +++ b/resources/views/components/modal-confirmation.blade.php @@ -40,32 +40,34 @@ this.deleteText = ''; this.password = ''; this.userConfirmText = ''; - this.passwordError = ''; this.selectedActions = @js(collect($checkboxes)->pluck('id')->all()); + $wire.$refresh(); }, step1ButtonText: @js($step1ButtonText), step2ButtonText: @js($step2ButtonText), step3ButtonText: @js($step3ButtonText), validatePassword() { - this.passwordError = ''; if (this.confirmWithPassword && !this.password) { - this.passwordError = 'Password is required.'; - return false; + return 'Password is required.'; } - return true; + return ''; }, submitForm() { - if (this.validatePassword()) { - $wire.call(this.submitAction, this.password, this.selectedActions) - .then(result => { - if (result === true) { - this.modalOpen = false; - this.resetModal(); - } else if (typeof result === 'string') { - this.passwordError = result; - } - }); + if (this.confirmWithPassword) { + this.passwordError = this.validatePassword(); + if (this.passwordError) { + return; + } } + $wire.call(this.submitAction, this.password, this.selectedActions) + .then(result => { + if (result === true) { + this.modalOpen = false; + this.resetModal(); + } else if (typeof result === 'string') { + this.passwordError = result; + } + }); }, copyConfirmText() { navigator.clipboard.writeText(this.confirmText); @@ -82,7 +84,7 @@ this.selectedActions.push(id); } } -}" @keydown.escape.window="modalOpen = false; resetModal()" :class="{ 'z-40': modalOpen }" class="relative w-auto h-auto" @password-error.window="passwordError = $event.detail"> +}" @keydown.escape.window="modalOpen = false; resetModal()" :class="{ 'z-40': modalOpen }" class="relative w-auto h-auto"> @if ($customButton) @if ($buttonFullWidth) @@ -134,7 +136,7 @@ @endif -