From 1c39b133d395884517fba0bb331e87c8ca9374c8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 21 Jan 2025 13:18:02 +0100 Subject: [PATCH 01/11] version++ --- config/constants.php | 2 +- versions.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/constants.php b/config/constants.php index ce37915b3..dddaebbb5 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.384', + 'version' => '4.0.0-beta.385', 'self_hosted' => env('SELF_HOSTED', true), 'autoupdate' => env('AUTOUPDATE'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), diff --git a/versions.json b/versions.json index c0e8d950f..dd64cc010 100644 --- a/versions.json +++ b/versions.json @@ -1,10 +1,10 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.384" + "version": "4.0.0-beta.385" }, "nightly": { - "version": "4.0.0-beta.385" + "version": "4.0.0-beta.386" }, "helper": { "version": "1.0.4" From 7e58cb9125b118ef42a054c5fe7bcda46dec2ffc Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 21 Jan 2025 13:39:53 +0100 Subject: [PATCH 02/11] fix: load service templates on cloud --- app/Jobs/PullTemplatesFromCDN.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/PullTemplatesFromCDN.php b/app/Jobs/PullTemplatesFromCDN.php index 45c536e06..9a4c991bc 100644 --- a/app/Jobs/PullTemplatesFromCDN.php +++ b/app/Jobs/PullTemplatesFromCDN.php @@ -25,7 +25,7 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue public function handle(): void { try { - if (isDev() || isCloud()) { + if (isDev()) { return; } $response = Http::retry(3, 1000)->get(config('constants.services.official')); From 419710402657ceb0c4c870dae8a708f1c8b60ba2 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:02:29 +0100 Subject: [PATCH 03/11] fix(db): `finished_at` timestamps are not set for existing deployments --- ...date_finished_at_timestamps_if_not_set.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 database/migrations/2025_01_21_125205_update_finished_at_timestamps_if_not_set.php diff --git a/database/migrations/2025_01_21_125205_update_finished_at_timestamps_if_not_set.php b/database/migrations/2025_01_21_125205_update_finished_at_timestamps_if_not_set.php new file mode 100644 index 000000000..050e8f1ae --- /dev/null +++ b/database/migrations/2025_01_21_125205_update_finished_at_timestamps_if_not_set.php @@ -0,0 +1,37 @@ +whereNull('finished_at') + ->update(['finished_at' => DB::raw('updated_at')]); + } catch (\Exception $e) { + \Log::error('Failed to update not set finished_at timestamps for application_deployment_queues: '.$e->getMessage()); + } + + try { + DB::table('scheduled_database_backup_executions') + ->whereNull('finished_at') + ->update(['finished_at' => DB::raw('updated_at')]); + } catch (\Exception $e) { + \Log::error('Failed to update not set finished_at timestamps for scheduled_database_backup_executions: '.$e->getMessage()); + } + + try { + DB::table('scheduled_task_executions') + ->whereNull('finished_at') + ->update(['finished_at' => DB::raw('updated_at')]); + } catch (\Exception $e) { + \Log::error('Failed to update not set finished_at timestamps for scheduled_task_executions: '.$e->getMessage()); + } + } +}; From 7321c0b3db8dc5348dbbfc816310f69e38f010a1 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:02:42 +0100 Subject: [PATCH 04/11] Update service-templates.json --- templates/service-templates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/service-templates.json b/templates/service-templates.json index c703f9bdc..b95f9a032 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -449,7 +449,7 @@ "cloudflared": { "documentation": "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/?utm_source=coolify.io", "slogan": "Client for Cloudflare Tunnel, a daemon that exposes private services through the Cloudflare edge.", - "compose": "c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya19tb2RlOiBob3N0CiAgICBjb21tYW5kOiAndHVubmVsIC0tbm8tYXV0b3VwZGF0ZSBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnVFVOTkVMX1RPS0VOPSR7Q0xPVURGTEFSRV9UVU5ORUxfVE9LRU59Jwo=", + "compose": "c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya19tb2RlOiBob3N0CiAgICBjb21tYW5kOiAndHVubmVsIC0tbm8tYXV0b3VwZGF0ZSBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnVFVOTkVMX1RPS0VOPSR7Q0xPVURGTEFSRV9UVU5ORUxfVE9LRU59JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGNsb3VkZmxhcmVkCiAgICAgICAgLSAnLS12ZXJzaW9uJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", "tags": null, "logo": "svgs/cloudflared.svg", "minversion": "0.0.0" From 4b74ca56c387ee469797e3217ae1d4f55e20fd92 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 21 Jan 2025 14:09:07 +0100 Subject: [PATCH 05/11] feat(core): wip version of coolify.json --- app/Livewire/Project/Application/General.php | 1 + app/Models/Application.php | 45 ++-- app/Services/ConfigurationGenerator.php | 194 ++++++++++++++++++ app/Traits/HasConfiguration.php | 42 ++++ .../project/application/general.blade.php | 11 +- 5 files changed, 263 insertions(+), 30 deletions(-) create mode 100644 app/Services/ConfigurationGenerator.php create mode 100644 app/Traits/HasConfiguration.php diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 576f87801..f8e28d216 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -442,6 +442,7 @@ class General extends Component { $config = GenerateConfig::run($this->application, true); $fileName = str($this->application->name)->slug()->append('_config.json'); + dd($config); return response()->streamDownload(function () use ($config) { echo $config; diff --git a/app/Models/Application.php b/app/Models/Application.php index 289ef5b0f..3913ce37a 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -3,6 +3,8 @@ namespace App\Models; use App\Enums\ApplicationDeploymentStatus; +use App\Services\ConfigurationGenerator; +use App\Traits\HasConfiguration; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -105,7 +107,7 @@ use Visus\Cuid2\Cuid2; class Application extends BaseModel { - use HasFactory, SoftDeletes; + use HasConfiguration, HasFactory, SoftDeletes; private static $parserVersion = '4'; @@ -1640,35 +1642,28 @@ class Application extends BaseModel } } + public function getLimits(): array + { + return [ + 'limits_memory' => $this->limits_memory, + 'limits_memory_swap' => $this->limits_memory_swap, + 'limits_memory_swappiness' => $this->limits_memory_swappiness, + 'limits_memory_reservation' => $this->limits_memory_reservation, + 'limits_cpus' => $this->limits_cpus, + 'limits_cpuset' => $this->limits_cpuset, + 'limits_cpu_shares' => $this->limits_cpu_shares, + ]; + } + public function generateConfig($is_json = false) { - $config = collect([]); - if ($this->build_pack = 'nixpacks') { - $config = collect([ - 'build_pack' => 'nixpacks', - 'docker_registry_image_name' => $this->docker_registry_image_name, - 'docker_registry_image_tag' => $this->docker_registry_image_tag, - 'install_command' => $this->install_command, - 'build_command' => $this->build_command, - 'start_command' => $this->start_command, - 'base_directory' => $this->base_directory, - 'publish_directory' => $this->publish_directory, - 'custom_docker_run_options' => $this->custom_docker_run_options, - 'ports_exposes' => $this->ports_exposes, - 'ports_mappings' => $this->ports_mapping, - 'settings' => collect([ - 'is_static' => $this->settings->is_static, - ]), - ]); - } - $config = $config->filter(function ($value) { - return str($value)->isNotEmpty(); - }); + $generator = new ConfigurationGenerator($this); + if ($is_json) { - return json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + return $generator->toJson(); } - return $config; + return $generator->toArray(); } public function setConfig($config) diff --git a/app/Services/ConfigurationGenerator.php b/app/Services/ConfigurationGenerator.php new file mode 100644 index 000000000..a7e4b31be --- /dev/null +++ b/app/Services/ConfigurationGenerator.php @@ -0,0 +1,194 @@ +generateConfig(); + } + + protected function generateConfig(): void + { + if ($this->resource instanceof Application) { + $this->config = [ + 'id' => $this->resource->id, + 'name' => $this->resource->name, + 'uuid' => $this->resource->uuid, + 'description' => $this->resource->description, + 'coolify_details' => [ + 'project_uuid' => $this->resource->project()->uuid, + 'environment_uuid' => $this->resource->environment->uuid, + + 'destination_type' => $this->resource->destination_type, + 'destination_id' => $this->resource->destination_id, + 'source_type' => $this->resource->source_type, + 'source_id' => $this->resource->source_id, + 'private_key_id' => $this->resource->private_key_id, + ], + + 'post_deployment_command' => $this->resource->post_deployment_command, + 'post_deployment_command_container' => $this->resource->post_deployment_command_container, + 'pre_deployment_command' => $this->resource->pre_deployment_command, + 'pre_deployment_command_container' => $this->resource->pre_deployment_command_container, + 'build' => [ + 'type' => $this->resource->build_pack, + 'static_image' => $this->resource->static_image, + 'base_directory' => $this->resource->base_directory, + 'publish_directory' => $this->resource->publish_directory, + 'dockerfile' => $this->resource->dockerfile, + 'dockerfile_location' => $this->resource->dockerfile_location, + 'dockerfile_target_build' => $this->resource->dockerfile_target_build, + 'custom_docker_run_options' => $this->resource->custom_docker_options, + 'compose_parsing_version' => $this->resource->compose_parsing_version, + 'docker_compose' => $this->resource->docker_compose, + 'docker_compose_location' => $this->resource->docker_compose_location, + 'docker_compose_raw' => $this->resource->docker_compose_raw, + 'docker_compose_domains' => $this->resource->docker_compose_domains, + 'docker_compose_custom_start_command' => $this->resource->docker_compose_custom_start_command, + 'docker_compose_custom_build_command' => $this->resource->docker_compose_custom_build_command, + 'install_command' => $this->resource->install_command, + 'build_command' => $this->resource->build_command, + 'start_command' => $this->resource->start_command, + 'watch_paths' => $this->resource->watch_paths, + ], + 'source' => [ + 'git_repository' => $this->resource->git_repository, + 'git_branch' => $this->resource->git_branch, + 'git_commit_sha' => $this->resource->git_commit_sha, + 'repository_project_id' => $this->resource->repository_project_id, + ], + 'docker_registry_image' => $this->getDockerRegistryImage(), + 'domains' => [ + 'fqdn' => $this->resource->fqdn, + 'ports_exposes' => $this->resource->ports_exposes, + 'ports_mappings' => $this->resource->ports_mappings, + 'redirect' => $this->resource->redirect, + 'custom_nginx_configuration' => $this->resource->custom_nginx_configuration, + ], + 'environment_variables' => [ + 'production' => $this->getEnvironmentVariables(), + 'preview' => $this->getPreviewEnvironmentVariables(), + ], + 'settings' => $this->getApplicationSettings(), + 'preview' => $this->getPreview(), + 'limits' => $this->resource->getLimits(), + 'health_check' => [ + 'health_check_path' => $this->resource->health_check_path, + 'health_check_port' => $this->resource->health_check_port, + 'health_check_host' => $this->resource->health_check_host, + 'health_check_method' => $this->resource->health_check_method, + 'health_check_return_code' => $this->resource->health_check_return_code, + 'health_check_scheme' => $this->resource->health_check_scheme, + 'health_check_response_text' => $this->resource->health_check_response_text, + 'health_check_interval' => $this->resource->health_check_interval, + 'health_check_timeout' => $this->resource->health_check_timeout, + 'health_check_retries' => $this->resource->health_check_retries, + 'health_check_start_period' => $this->resource->health_check_start_period, + 'health_check_enabled' => $this->resource->health_check_enabled, + ], + 'webhooks_secrets' => [ + 'manual_webhook_secret_github' => $this->resource->manual_webhook_secret_github, + 'manual_webhook_secret_gitlab' => $this->resource->manual_webhook_secret_gitlab, + 'manual_webhook_secret_bitbucket' => $this->resource->manual_webhook_secret_bitbucket, + 'manual_webhook_secret_gitea' => $this->resource->manual_webhook_secret_gitea, + ], + 'swarm' => [ + 'swarm_replicas' => $this->resource->swarm_replicas, + 'swarm_placement_constraints' => $this->resource->swarm_placement_constraints, + ], + ]; + } + } + + protected function getPreview(): array + { + return [ + 'preview_url_template' => $this->resource->preview_url_template, + ]; + } + + protected function getDockerRegistryImage(): array + { + return [ + 'image' => $this->resource->docker_registry_image_name, + 'tag' => $this->resource->docker_registry_image_tag, + ]; + } + + protected function getEnvironmentVariables(): array + { + $variables = collect([]); + foreach ($this->resource->environment_variables as $env) { + $variables->push([ + 'key' => $env->key, + 'value' => $env->value, + 'is_build_time' => $env->is_build_time, + 'is_preview' => $env->is_preview, + 'is_multiline' => $env->is_multiline, + ]); + } + + return $variables->toArray(); + } + + protected function getPreviewEnvironmentVariables(): array + { + $variables = collect([]); + foreach ($this->resource->environment_variables_preview as $env) { + $variables->push([ + 'key' => $env->key, + 'value' => $env->value, + 'is_build_time' => $env->is_build_time, + 'is_preview' => $env->is_preview, + 'is_multiline' => $env->is_multiline, + ]); + } + + return $variables->toArray(); + } + + protected function getApplicationSettings(): array + { + $removedKeys = ['id', 'application_id', 'created_at', 'updated_at']; + $settings = $this->resource->settings->attributesToArray(); + $settings = collect($settings)->filter(function ($value, $key) use ($removedKeys) { + return ! in_array($key, $removedKeys); + })->sortBy(function ($value, $key) { + return $key; + })->toArray(); + + return $settings; + } + + public function saveJson(string $path): void + { + file_put_contents($path, json_encode($this->config, JSON_PRETTY_PRINT)); + } + + public function saveYaml(string $path): void + { + file_put_contents($path, Yaml::dump($this->config, 6, 2)); + } + + public function toArray(): array + { + return $this->config; + } + + public function toJson(): string + { + return json_encode($this->config, JSON_PRETTY_PRINT); + } + + public function toYaml(): string + { + return Yaml::dump($this->config, 6, 2); + } +} diff --git a/app/Traits/HasConfiguration.php b/app/Traits/HasConfiguration.php new file mode 100644 index 000000000..e572c45ea --- /dev/null +++ b/app/Traits/HasConfiguration.php @@ -0,0 +1,42 @@ +uuid}"; + if (! is_dir($configDir)) { + mkdir($configDir, 0755, true); + } + + $generator->saveJson($configDir.'/coolify.json'); + $generator->saveYaml($configDir.'/coolify.yaml'); + + // Generate a README file with basic information + file_put_contents( + $configDir.'/README.md', + generate_readme_file($this->name, now()->toIso8601String()) + ); + } + + public function getConfigurationAsJson(): string + { + return (new ConfigurationGenerator($this))->toJson(); + } + + public function getConfigurationAsYaml(): string + { + return (new ConfigurationGenerator($this))->toYaml(); + } + + public function getConfigurationAsArray(): array + { + return (new ConfigurationGenerator($this))->toArray(); + } +} diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 62acda056..026b3b579 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -5,13 +5,14 @@ Save - {{-- - + + {{-- Download Config - + --}} + {{-- - - --}} + --}} +
General configuration for your application.
From a230a425993903b410201b5de2edf93f5796afbe Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:53:44 +0100 Subject: [PATCH 06/11] fix(email): transactional email sending --- app/Notifications/Channels/EmailChannel.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Notifications/Channels/EmailChannel.php b/app/Notifications/Channels/EmailChannel.php index 6ffe5c4d7..98536d346 100644 --- a/app/Notifications/Channels/EmailChannel.php +++ b/app/Notifications/Channels/EmailChannel.php @@ -53,6 +53,7 @@ class EmailChannel if (! $type) { throw new Exception('No email settings found.'); } + config()->set('mail.default', $type); return; } From 2844c6ce36667d1e5e025df600c2a7a56e92186c Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:03:58 +0100 Subject: [PATCH 07/11] fix(ui): add missing save button for new Docker Cleanup page --- .../livewire/server/docker-cleanup.blade.php | 123 +++++++++--------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/resources/views/livewire/server/docker-cleanup.blade.php b/resources/views/livewire/server/docker-cleanup.blade.php index cdf48c6aa..8151b5358 100644 --- a/resources/views/livewire/server/docker-cleanup.blade.php +++ b/resources/views/livewire/server/docker-cleanup.blade.php @@ -6,72 +6,75 @@
-
-
-

Docker Cleanup

+
+
+
+

Docker Cleanup

+ Save +
+
Configure Docker cleanup settings for your server.
-
Configure Docker cleanup settings for your server.
-
-
-
-

Docker Cleanup

- -
-
- - @if (!$forceDockerCleanup) - - @endif +
+
+

Docker Cleanup

+ +
+
+ + @if (!$forceDockerCleanup) + + @endif +
+ +
+
+

+ Warning: Enable these + options only if you fully understand their implications and + consequences!
Improper use will result in data loss and could cause + functional issues. +

- +
  • Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).
  • +
  • Data from stopped containers volumes will be permanently lost.
  • +
  • No way to recover deleted volume data.
  • + " /> +
    -

    - Warning: Enable these - options only if you fully understand their implications and - consequences!
    Improper use will result in data loss and could cause - functional issues. -

    -
    - - -
    -
    +

    Recent executions (click to check output)

    From 43a4d9b467787a85cc64dfaa585fabac740d9dd1 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:09:03 +0100 Subject: [PATCH 08/11] fix(ui): show preview deployment environment variables --- .../project/shared/environment-variable/all.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index 3be31ba55..8723f926b 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -55,10 +55,10 @@

    Preview Deployments Environment Variables

    Environment (secrets) variables for Preview Deployments.
    - {{-- @foreach ($resource->environment_variables_preview as $env) + @foreach ($resource->environment_variables_preview as $env) - @endforeach --}} + @endforeach @endif @else
    From 31992f4d02a13feeeca0529405fbea436ac1bbd6 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:36:38 +0100 Subject: [PATCH 09/11] fix(ui):show error on terminal if container has no shell (bash/sh) --- .../Shared/ExecuteContainerCommand.php | 18 +++++ .../execute-container-command.blade.php | 77 +++++++++++-------- .../project/shared/terminal.blade.php | 5 -- 3 files changed, 64 insertions(+), 36 deletions(-) diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php index d12d8e26a..f993480c7 100644 --- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php +++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php @@ -27,6 +27,8 @@ class ExecuteContainerCommand extends Component public Collection $servers; + public bool $hasShell = true; + protected $rules = [ 'server' => 'required', 'container' => 'required', @@ -141,6 +143,16 @@ class ExecuteContainerCommand extends Component } } + private function checkShellAvailability(Server $server, string $container): bool + { + $escapedContainer = escapeshellarg($container); + $result = instant_remote_process([ + "docker exec {$escapedContainer} which bash || docker exec {$escapedContainer} which sh", + ], $server, false); + + return ! empty($result); + } + #[On('connectToServer')] public function connectToServer() { @@ -148,6 +160,7 @@ class ExecuteContainerCommand extends Component if ($this->server->isForceDisabled()) { throw new \RuntimeException('Server is disabled.'); } + $this->hasShell = true; $this->dispatch( 'send-terminal-command', false, @@ -201,6 +214,11 @@ class ExecuteContainerCommand extends Component throw new \RuntimeException('Server ownership verification failed.'); } + $this->hasShell = $this->checkShellAvailability($server, data_get($container, 'container.Names')); + if (! $this->hasShell) { + return; + } + $this->dispatch( 'send-terminal-command', true, diff --git a/resources/views/livewire/project/shared/execute-container-command.blade.php b/resources/views/livewire/project/shared/execute-container-command.blade.php index f9760ed65..b6a559927 100644 --- a/resources/views/livewire/project/shared/execute-container-command.blade.php +++ b/resources/views/livewire/project/shared/execute-container-command.blade.php @@ -16,43 +16,58 @@ @elseif ($type === 'server') @endif - @if ($type === 'server') - - Reconnect - -
    - + + @if(!$hasShell) +
    +
    +
    + + + +
    +

    Terminal Not Available

    +

    No shell (bash/sh) is available in this container. Please ensure either bash or sh is installed to use the terminal.

    +
    +
    +
    @else - @if (count($containers) > 0) - @if (count($containers) === 1) -
    - Reconnect -
    - @else -
    - - @foreach ($containers as $container) - @if ($loop->first) - - @endif - - @endforeach - - - Connect - -
    - @endif + @if ($type === 'server') +
    + Reconnect +
    @else -
    No containers are running.
    + @if (count($containers) === 0) +
    No containers are running.
    + @else + @if (count($containers) === 1) +
    + Reconnect +
    + @else +
    + + @foreach ($containers as $container) + @if ($loop->first) + + @endif + + @endforeach + + Connect +
    + @endif +
    + +
    + @endif @endif @endif
    diff --git a/resources/views/livewire/project/shared/terminal.blade.php b/resources/views/livewire/project/shared/terminal.blade.php index 26388cc0d..edf870ca7 100644 --- a/resources/views/livewire/project/shared/terminal.blade.php +++ b/resources/views/livewire/project/shared/terminal.blade.php @@ -1,9 +1,4 @@
    - {{--
    -
    - -
    -
    --}}
    From 1799081ebd7dc7f4ba429098a82e6e6e9d3c8689 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:55:07 +0100 Subject: [PATCH 10/11] fix(parser): resource URL should only be parsed if there is one --- app/Services/DockerImageParser.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Services/DockerImageParser.php b/app/Services/DockerImageParser.php index 4987f953d..1fd6625b3 100644 --- a/app/Services/DockerImageParser.php +++ b/app/Services/DockerImageParser.php @@ -43,7 +43,11 @@ class DockerImageParser public function getFullImageNameWithoutTag(): string { - return $this->registryUrl.'/'.$this->imageName; + if ($this->registryUrl) { + return $this->registryUrl.'/'.$this->imageName; + } + + return $this->imageName; } public function getRegistryUrl(): string From 8c937156c544ed01b14542b24d6460e61010cb20 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 21 Jan 2025 20:00:05 +0100 Subject: [PATCH 11/11] fix(core): compose parsing for apps --- bootstrap/helpers/shared.php | 1 - 1 file changed, 1 deletion(-) diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index ff2933f72..330f2627a 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -2872,7 +2872,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal data_forget($service, 'volumes.*.is_directory'); data_forget($service, 'exclude_from_hc'); data_set($service, 'environment', $serviceVariables->toArray()); - updateCompose($service); return $service; });