diff --git a/app/Actions/Server/InstallLogDrain.php b/app/Actions/Server/InstallLogDrain.php index e43642e7a..2a1b03260 100644 --- a/app/Actions/Server/InstallLogDrain.php +++ b/app/Actions/Server/InstallLogDrain.php @@ -198,7 +198,7 @@ Files: } $restart_command = [ "echo 'Stopping old Fluent Bit'", - "cd $config_path && docker rm -f coolify-log-drain || true", + "cd $config_path && docker compose down --remove-orphans || true", "echo 'Starting Fluent Bit'", "cd $config_path && docker compose up -d --remove-orphans", ]; diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 1b14e7e12..ccf1bba3d 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -618,6 +618,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } } else { $this->dockerImageTag = str($this->commit)->substr(0, 128); + if ($this->application->docker_registry_image_tag) { + $this->dockerImageTag = $this->application->docker_registry_image_tag; + } if ($this->application->docker_registry_image_name) { $this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build"); $this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}"); diff --git a/app/Livewire/Admin/Index.php b/app/Livewire/Admin/Index.php index 3c13cd771..4ff4ff21b 100644 --- a/app/Livewire/Admin/Index.php +++ b/app/Livewire/Admin/Index.php @@ -11,6 +11,9 @@ class Index extends Component public $users = []; public function mount() { + if (!isCloud()) { + return redirect()->route('dashboard'); + } if (auth()->user()->id !== 0 && session('adminToken') === null) { return redirect()->route('dashboard'); } diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index eee62c93d..8cb1dd7d2 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -3,6 +3,7 @@ namespace App\Livewire\Boarding; use App\Actions\Server\InstallDocker; +use App\Enums\ProxyTypes; use App\Models\PrivateKey; use App\Models\Project; use App\Models\Server; @@ -121,15 +122,16 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== } $this->selectedExistingPrivateKey = $this->createdServer->privateKey->id; $this->serverPublicKey = $this->createdServer->privateKey->publicKey(); - $this->validateServer(); + $this->installServer(); } public function getProxyType() { - $proxyTypeSet = $this->createdServer->proxy->type; - if (!$proxyTypeSet) { - $this->currentState = 'select-proxy'; - return; - } + $this->selectProxy(ProxyTypes::TRAEFIK_V2->value); + // $proxyTypeSet = $this->createdServer->proxy->type; + // if (!$proxyTypeSet) { + // $this->currentState = 'select-proxy'; + // return; + // } $this->getProjects(); } public function selectExistingPrivateKey() @@ -193,7 +195,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== $this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel; $this->createdServer->settings->save(); $this->createdServer->addInitialNetwork(); - $this->validateServer(); + $this->currentState = 'validate-server'; } public function installServer() { @@ -219,7 +221,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== $dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true); $dockerVersion = checkMinimumDockerEngineVersion($dockerVersion); if (is_null($dockerVersion)) { - $this->currentState = 'install-docker'; + $this->currentState = 'validate-server'; throw new \Exception('Docker not found or old version is installed.'); } $this->createdServer->settings()->update([ @@ -227,27 +229,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== ]); $this->getProxyType(); } catch (\Throwable $e) { - // $this->dockerInstallationStarted = false; return handleError(error: $e, livewire: $this); } } - public function installDocker() - { - try { - $this->dockerInstallationStarted = true; - $activity = InstallDocker::run($this->createdServer); - $this->dispatch('installDocker'); - $this->dispatch('activityMonitor', $activity->id); - } catch (\Throwable $e) { - $this->dockerInstallationStarted = false; - return handleError(error: $e, livewire: $this); - } - } - public function dockerInstalledOrSkipped() - { - $this->validateServer(); - } - public function selectProxy(string|null $proxyType = null) + public function selectProxy(?string $proxyType = null) { if (!$proxyType) { return $this->getProjects(); diff --git a/app/Livewire/Dashboard.php b/app/Livewire/Dashboard.php index 696ae09c4..a44cd18af 100644 --- a/app/Livewire/Dashboard.php +++ b/app/Livewire/Dashboard.php @@ -22,6 +22,7 @@ class Dashboard extends Component } public function cleanup_queue() { + $this->dispatch('success', 'Cleanup started.'); Artisan::queue('app:init', [ '--cleanup-deployments' => 'true' ]); diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php index 1b19c445a..01ab13c4e 100644 --- a/app/Livewire/Project/Application/Heading.php +++ b/app/Livewire/Project/Application/Heading.php @@ -56,15 +56,15 @@ class Heading extends Component return; } if ($this->application->destination->server->isSwarm() && str($this->application->docker_registry_image_name)->isEmpty()) { - $this->dispatch('error', 'Failed to deploy', 'To deploy to a Swarm cluster you must set a Docker image name first.'); + $this->dispatch('error', 'Failed to deploy.', 'To deploy to a Swarm cluster you must set a Docker image name first.'); return; } if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) { - $this->dispatch('error', 'Failed to deploy', 'To use a build server, you must first set a Docker image.
More information here: documentation'); + $this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.
More information here: documentation'); return; } if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) { - $this->dispatch('error', 'Failed to deploy', 'To deploy to more than one server, you must first set a Docker image.
More information here: documentation'); + $this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); return; } $this->setDeploymentUuid(); @@ -103,7 +103,7 @@ class Heading extends Component public function restart() { if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) { - $this->dispatch('error', 'Failed to deploy', 'To deploy to more than one server, you must first set a Docker image.
More information here: documentation'); + $this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); return; } $this->setDeploymentUuid(); diff --git a/app/Livewire/Project/Service/Index.php b/app/Livewire/Project/Service/Index.php index 21009cb44..ede42c2c7 100644 --- a/app/Livewire/Project/Service/Index.php +++ b/app/Livewire/Project/Service/Index.php @@ -27,12 +27,12 @@ class Index extends Component $this->parameters = get_route_parameters(); $this->query = request()->query(); $this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail(); - $service = $this->service->applications()->whereName($this->parameters['service_name'])->first(); + $service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first(); if ($service) { $this->serviceApplication = $service; $this->serviceApplication->getFilesFromServer(); } else { - $this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first(); + $this->serviceDatabase = $this->service->databases()->whereUuid($this->parameters['stack_service_uuid'])->first(); $this->serviceDatabase->getFilesFromServer(); } $this->s3s = currentTeam()->s3s; diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php index cf5e2632f..3d816149b 100644 --- a/app/Livewire/Project/Shared/Destination.php +++ b/app/Livewire/Project/Shared/Destination.php @@ -37,10 +37,13 @@ class Destination extends Component $this->networks = $this->networks->reject(function ($network) use ($all_networks) { return $all_networks->pluck('id')->contains($network->id); }); - } public function redeploy(int $network_id, int $server_id) { + if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) { + $this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); + return; + } $deployment_uuid = new Cuid2(7); $server = Server::find($server_id); $destination = StandaloneDocker::find($network_id); diff --git a/app/Livewire/Server/Proxy/Status.php b/app/Livewire/Server/Proxy/Status.php index a41994c8f..bd0ffe431 100644 --- a/app/Livewire/Server/Proxy/Status.php +++ b/app/Livewire/Server/Proxy/Status.php @@ -12,7 +12,7 @@ class Status extends Component public Server $server; public bool $polling = false; public int $numberOfPolls = 0; - protected $listeners = ['proxyStatusUpdated', 'startProxyPolling']; + protected $listeners = ['proxyStatusUpdated' => '$refresh', 'startProxyPolling']; public function startProxyPolling() { diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php index e5d2a4b39..4d37feca2 100644 --- a/app/Livewire/Server/ValidateAndInstall.php +++ b/app/Livewire/Server/ValidateAndInstall.php @@ -2,6 +2,7 @@ namespace App\Livewire\Server; +use App\Actions\Proxy\StartProxy; use App\Models\Server; use Livewire\Component; @@ -14,18 +15,23 @@ class ValidateAndInstall extends Component public $uptime = null; public $supported_os_type = null; public $docker_installed = null; + public $docker_compose_installed = null; public $docker_version = null; + public $proxy_started = false; public $error = null; protected $listeners = ['validateServer' => 'init', 'validateDockerEngine', 'validateServerNow' => 'validateServer']; public function init(bool $install = true) { + $this->install = $install; $this->uptime = null; $this->supported_os_type = null; $this->docker_installed = null; $this->docker_version = null; + $this->docker_compose_installed = null; + $this->proxy_started = null; $this->error = null; $this->number_of_tries = 0; $this->dispatch('validateServerNow'); @@ -43,6 +49,11 @@ class ValidateAndInstall extends Component if ($swarmInstalled) { $this->dispatch('success', 'Docker Swarm is initiated.'); } + } else { + $proxy = StartProxy::run($this->server); + if ($proxy) { + $this->proxy_started = true; + } } } catch (\Throwable $e) { return handleError($e, $this); @@ -67,9 +78,9 @@ class ValidateAndInstall extends Component public function validateDockerEngine() { $this->docker_installed = $this->server->validateDockerEngine(); - if (!$this->docker_installed) { + $this->docker_compose_installed = $this->server->validateDockerCompose(); + if (!$this->docker_installed || !$this->docker_compose_installed) { if ($this->install) { - ray($this->number_of_tries, $this->max_tries); if ($this->number_of_tries == $this->max_tries) { $this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: documentation.'; return; diff --git a/app/Models/Application.php b/app/Models/Application.php index 959d06d7f..0ba1d24d1 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -200,9 +200,6 @@ class Application extends BaseModel set: fn ($value) => $value === "" ? null : $value, ); } - - // Normal Deployments - public function portsMappingsArray(): Attribute { return Attribute::make( @@ -214,7 +211,7 @@ class Application extends BaseModel } public function realStatus() { - return $this->getRawOriginal('status'); + return $this->getRawOriginal('status'); } public function status(): Attribute { diff --git a/app/Models/Server.php b/app/Models/Server.php index bdbb0309d..e4a7b895a 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -443,6 +443,21 @@ class Server extends BaseModel $this->validateCoolifyNetwork(isSwarm: false, isBuildServer: $this->settings->is_build_server); return true; } + public function validateDockerCompose($throwError = false) + { + $dockerCompose = instant_remote_process(["docker compose version"], $this, false); + if (is_null($dockerCompose)) { + $this->settings->is_usable = false; + $this->settings->save(); + if ($throwError) { + throw new \Exception('Server is not usable. Docker Compose is not installed.'); + } + return false; + } + $this->settings->is_usable = true; + $this->settings->save(); + return true; + } public function validateDockerSwarm() { $swarmStatus = instant_remote_process(["docker info|grep -i swarm"], $this, false); diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php index 9b16ebfed..134cc6aad 100644 --- a/bootstrap/helpers/constants.php +++ b/bootstrap/helpers/constants.php @@ -13,6 +13,11 @@ const VALID_CRON_STRINGS = [ const RESTART_MODE = 'unless-stopped'; const DATABASE_DOCKER_IMAGES = [ + 'bitnami/mariadb', + 'bitnami/mongodb', + 'bitnami/mysql', + 'bitnami/postgresql', + 'bitnami/redis', 'mysql', 'mariadb', 'postgres', diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 3ebec43b9..eb0898bfd 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -349,18 +349,8 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null foreach ($matches as $match) { $option = $match[1]; $value = isset($match[2]) && $match[2] !== '' ? $match[2] : true; - if ($list_options->contains($option)) { - $value = explode(',', $value); - } - if (array_key_exists($option, $options)) { - if (is_array($options[$option])) { - $options[$option][] = $value; - } else { - $options[$option] = [$options[$option], $value]; - } - } else { - $options[$option] = $value; - } + $options[$option][] = $value; + $options[$option] = array_unique($options[$option]); } $options = collect($options); // Easily get mappings from https://github.com/composerize/composerize/blob/master/packages/composerize/src/mappings.js @@ -370,7 +360,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null } if ($option === '--ulimit') { $ulimits = collect([]); - collect($value)->map(function ($ulimit) use ($ulimits){ + collect($value)->map(function ($ulimit) use ($ulimits) { $ulimit = explode('=', $ulimit); $type = $ulimit[0]; $limits = explode(':', $ulimit[1]); @@ -381,7 +371,6 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null 'soft' => $soft_limit, 'hard' => $hard_limit ]); - } else { $soft_limit = $ulimit[1]; $ulimits->put($type, [ diff --git a/config/sentry.php b/config/sentry.php index 5f2ea6fb6..4a2eff3f2 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.212', + 'release' => '4.0.0-beta.213', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 01e2a62f9..b1372fd9d 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ + stroke-width="1.5" stroke="currentColor" fill="none" + stroke-linecap="round" stroke-linejoin="round"> @@ -84,7 +84,7 @@ @endif @if (data_get($application, 'ports_mappings_array')) @foreach ($application->ports_mappings_array as $port) - @if (isDev()) + @if ($application->destination->server->id === 0)
  • @@ -114,9 +114,29 @@ - Port {{ $port }} + {{ $application->destination->server->ip }}:{{ explode(':', $port)[0] }}
  • + @if (count($application->additional_servers) > 0) + @foreach ($application->additional_servers as $server) +
  • + + + + + + + + {{ $server->ip }}:{{ explode(':', $port)[0] }} + +
  • + @endforeach + @endif @endif @endforeach @endif diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index e4cafb6b9..39a022dc4 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -40,109 +40,139 @@ - -