diff --git a/app/Actions/Proxy/CheckConfigurationSync.php b/app/Actions/Proxy/CheckConfiguration.php similarity index 76% rename from app/Actions/Proxy/CheckConfigurationSync.php rename to app/Actions/Proxy/CheckConfiguration.php index c1a261796..1526c9728 100644 --- a/app/Actions/Proxy/CheckConfigurationSync.php +++ b/app/Actions/Proxy/CheckConfiguration.php @@ -2,12 +2,14 @@ namespace App\Actions\Proxy; +use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Server; use Illuminate\Support\Str; -class CheckConfigurationSync +class CheckConfiguration { - public function __invoke(Server $server, bool $reset = false) + use AsAction; + public function handle(Server $server, bool $reset = false) { $proxy_path = get_proxy_path(); $proxy_configuration = instant_remote_process([ diff --git a/app/Actions/Proxy/SaveConfiguration.php b/app/Actions/Proxy/SaveConfiguration.php new file mode 100644 index 000000000..85aebf619 --- /dev/null +++ b/app/Actions/Proxy/SaveConfiguration.php @@ -0,0 +1,27 @@ +proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value; + $server->save(); + + return instant_remote_process([ + "mkdir -p $proxy_path", + "echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml", + ], $server); + } +} diff --git a/app/Actions/Proxy/SaveConfigurationSync.php b/app/Actions/Proxy/SaveConfigurationSync.php deleted file mode 100644 index d58dad0c7..000000000 --- a/app/Actions/Proxy/SaveConfigurationSync.php +++ /dev/null @@ -1,29 +0,0 @@ -proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value; - $server->save(); - - instant_remote_process([ - "mkdir -p $proxy_path", - "echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml", - ], $server); - } catch (\Throwable $e) { - ray($e); - } - - } -} diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index 37c9c492e..fd14f49d5 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -32,8 +32,10 @@ class StartProxy return "docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null 2>&1 || docker network create --attachable $network > /dev/null 2>&1"; }); - $configuration = resolve(CheckConfigurationSync::class)($server); - + $configuration = CheckConfiguration::run($server); + if (!$configuration) { + throw new \Exception("Configuration is not synced"); + } $docker_compose_yml_base64 = base64_encode($configuration); $server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value; $server->save(); diff --git a/app/Actions/Server/InstallDocker.php b/app/Actions/Server/InstallDocker.php index 49ea85c6b..39d0718db 100644 --- a/app/Actions/Server/InstallDocker.php +++ b/app/Actions/Server/InstallDocker.php @@ -4,11 +4,10 @@ namespace App\Actions\Server; use App\Models\Server; use App\Models\StandaloneDocker; -use App\Models\Team; class InstallDocker { - public function __invoke(Server $server, Team $team) + public function __invoke(Server $server, bool $instant = false) { $dockerVersion = '24.0'; $config = base64_encode('{ @@ -19,15 +18,16 @@ class InstallDocker } }'); $found = StandaloneDocker::where('server_id', $server->id); - if ($found->count() == 0) { + if ($found->count() == 0 && $server->id) { StandaloneDocker::create([ 'name' => 'coolify', 'network' => 'coolify', 'server_id' => $server->id, ]); } - if (isDev()) { - return remote_process([ + + if (isDev() && $server->id === 0) { + $command = [ "echo '####### Installing Prerequisites...'", "sleep 1", "echo '####### Installing/updating Docker Engine...'", @@ -35,9 +35,9 @@ class InstallDocker "sleep 4", "echo '####### Restarting Docker Engine...'", "ls -l /tmp" - ], $server); + ]; } else { - return remote_process([ + $command = [ "echo '####### Installing Prerequisites...'", "command -v jq >/dev/null || apt-get update", "command -v jq >/dev/null || apt install -y jq", @@ -53,7 +53,11 @@ class InstallDocker "echo '####### Creating default Docker network (coolify)...'", "docker network create --attachable coolify >/dev/null 2>&1 || true", "echo '####### Done!'" - ], $server); + ]; } + if ($instant) { + return instant_remote_process($command, $server); + } + return remote_process($command, $server); } } diff --git a/app/Http/Livewire/Boarding/Index.php b/app/Http/Livewire/Boarding/Index.php index dace2eb94..bc15c5579 100644 --- a/app/Http/Livewire/Boarding/Index.php +++ b/app/Http/Livewire/Boarding/Index.php @@ -15,6 +15,7 @@ class Index extends Component { public string $currentState = 'welcome'; + public ?string $selectedServerType = null; public ?Collection $privateKeys = null; public ?int $selectedExistingPrivateKey = null; public ?string $privateKeyType = null; @@ -37,6 +38,7 @@ class Index extends Component public ?int $selectedExistingProject = null; public ?Project $createdProject = null; + public bool $dockerInstallationStarted = false; public function mount() { $this->privateKeyName = generate_random_name(); @@ -64,12 +66,14 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== public function restartBoarding() { - if ($this->createdServer) { - $this->createdServer->delete(); - } - if ($this->createdPrivateKey) { - $this->createdPrivateKey->delete(); - } + // if ($this->selectedServerType !== 'localhost') { + // if ($this->createdServer) { + // $this->createdServer->delete(); + // } + // if ($this->createdPrivateKey) { + // $this->createdPrivateKey->delete(); + // } + // } return redirect()->route('boarding'); } public function skipBoarding() @@ -84,13 +88,14 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== public function setServerType(string $type) { - if ($type === 'localhost') { + $this->selectedServerType = $type; + if ($this->selectedServerType === 'localhost') { $this->createdServer = Server::find(0); if (!$this->createdServer) { return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.'); } return $this->validateServer('localhost'); - } elseif ($type === 'remote') { + } elseif ($this->selectedServerType === 'remote') { $this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get(); if ($this->privateKeys->count() > 0) { $this->selectedExistingPrivateKey = $this->privateKeys->first()->id; @@ -114,8 +119,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== } $this->selectedExistingPrivateKey = $this->createdServer->privateKey->id; $this->validateServer(); - $this->getProxyType(); - $this->getProjects(); } public function getProxyType() { @@ -128,6 +131,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== } public function selectExistingPrivateKey() { + $this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey); $this->currentState = 'create-server'; } public function createNewServer() @@ -150,39 +154,38 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== 'privateKeyName' => 'required', 'privateKey' => 'required', ]); + $this->createdPrivateKey = PrivateKey::create([ + 'name' => $this->privateKeyName, + 'description' => $this->privateKeyDescription, + 'private_key' => $this->privateKey, + 'team_id' => currentTeam()->id + ]); + $this->createdPrivateKey->save(); $this->currentState = 'create-server'; } public function saveServer() { $this->validate([ 'remoteServerName' => 'required', - 'remoteServerHost' => 'required|ip', + 'remoteServerHost' => 'required', 'remoteServerPort' => 'required|integer', 'remoteServerUser' => 'required', ]); $this->privateKey = formatPrivateKey($this->privateKey); - $this->createdPrivateKey = new PrivateKey(); - $this->createdPrivateKey->private_key = $this->privateKey; - $this->createdPrivateKey->name = $this->privateKeyName; - $this->createdPrivateKey->description = $this->privateKeyDescription; - $this->createdPrivateKey->team_id = currentTeam()->id; $foundServer = Server::whereIp($this->remoteServerHost)->first(); if ($foundServer) { return $this->emit('error', 'IP address is already in use by another team.'); } - $this->createdServer = new Server(); - $this->createdServer->uuid = (string)new Cuid2(7); - $this->createdServer->name = $this->remoteServerName; - $this->createdServer->ip = $this->remoteServerHost; - $this->createdServer->port = $this->remoteServerPort; - $this->createdServer->user = $this->remoteServerUser; - $this->createdServer->description = $this->remoteServerDescription; - $this->createdServer->privateKey = $this->createdPrivateKey; - $this->createdServer->team_id = currentTeam()->id; - - ray($this->createdServer); - - + $this->createdServer = Server::create([ + 'name' => $this->remoteServerName, + 'ip' => $this->remoteServerHost, + 'port' => $this->remoteServerPort, + 'user' => $this->remoteServerUser, + 'description' => $this->remoteServerDescription, + 'private_key_id' => $this->createdPrivateKey->id, + 'team_id' => currentTeam()->id, + ]); + $this->createdServer->save(); $this->validateServer(); } public function validateServer(?string $type = null) @@ -190,47 +193,36 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== try { $customErrorMessage = "Server is not reachable:"; config()->set('coolify.mux_enabled', false); + instant_remote_process(['uptime'], $this->createdServer, true); + + $this->createdServer->settings->update([ + 'is_reachable' => true, + ]); + $dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true); $dockerVersion = checkMinimumDockerEngineVersion($dockerVersion); if (is_null($dockerVersion)) { - throw new \Exception('No Docker Engine or older than 23 version installed.'); + $this->currentState = 'install-docker'; + throw new \Exception('Docker version is not supported or not installed.'); } - $customErrorMessage = "Cannot create Server or Private Key. Please try again."; - if ($type !== 'localhost') { - $createdPrivateKey = PrivateKey::create([ - 'name' => $this->privateKeyName, - 'description' => $this->privateKeyDescription, - 'private_key' => $this->privateKey, - 'team_id' => currentTeam()->id - ]); - $server = Server::create([ - 'name' => $this->remoteServerName, - 'ip' => $this->remoteServerHost, - 'port' => $this->remoteServerPort, - 'user' => $this->remoteServerUser, - 'description' => $this->remoteServerDescription, - 'private_key_id' => $createdPrivateKey->id, - 'team_id' => currentTeam()->id, - ]); - $server->settings->is_reachable = true; - $server->settings->is_usable = true; - $server->settings->save(); - } else { - $this->createdServer->settings->update([ - 'is_reachable' => true, - ]); - } - $this->getProxyType(); + $this->dockerInstalledOrSkipped(); } catch (\Throwable $e) { return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this); } } public function installDocker() { - $activity = resolve(InstallDocker::class)($this->createdServer, currentTeam()); + $this->dockerInstallationStarted = true; + $activity = resolve(InstallDocker::class)($this->createdServer); $this->emit('newMonitorActivity', $activity->id); - $this->currentState = 'select-proxy'; + } + public function dockerInstalledOrSkipped() + { + $this->createdServer->settings->update([ + 'is_usable' => true, + ]); + $this->getProxyType(); } public function selectProxy(string|null $proxyType = null) { diff --git a/app/Http/Livewire/Server/Form.php b/app/Http/Livewire/Server/Form.php index 56cec63da..9794610fe 100644 --- a/app/Http/Livewire/Server/Form.php +++ b/app/Http/Livewire/Server/Form.php @@ -15,6 +15,7 @@ class Form extends Component public $dockerVersion; public string|null $wildcard_domain = null; public int $cleanup_after_percentage; + public bool $dockerInstallationStarted = false; protected $rules = [ 'server.name' => 'required|min:6', @@ -44,7 +45,8 @@ class Form extends Component public function installDocker() { - $activity = resolve(InstallDocker::class)($this->server, currentTeam()); + $this->dockerInstallationStarted = true; + $activity = resolve(InstallDocker::class)($this->server); $this->emit('newMonitorActivity', $activity->id); } @@ -56,7 +58,10 @@ class Form extends Component $this->uptime = $uptime; $this->emit('success', 'Server is reachable.'); } else { + ray($this->uptime); + $this->emit('error', 'Server is not reachable.'); + return; } if ($dockerVersion) { diff --git a/app/Http/Livewire/Server/New/ByIp.php b/app/Http/Livewire/Server/New/ByIp.php index f08b2ddb8..7814aef8f 100644 --- a/app/Http/Livewire/Server/New/ByIp.php +++ b/app/Http/Livewire/Server/New/ByIp.php @@ -26,7 +26,7 @@ class ByIp extends Component protected $rules = [ 'name' => 'required|string', 'description' => 'nullable|string', - 'ip' => 'required|ip', + 'ip' => 'required', 'user' => 'required|string', 'port' => 'required|integer', ]; diff --git a/app/Http/Livewire/Server/Proxy.php b/app/Http/Livewire/Server/Proxy.php index d65518975..560bdd784 100644 --- a/app/Http/Livewire/Server/Proxy.php +++ b/app/Http/Livewire/Server/Proxy.php @@ -2,9 +2,8 @@ namespace App\Http\Livewire\Server; -use App\Actions\Proxy\CheckConfigurationSync; -use App\Actions\Proxy\SaveConfigurationSync; -use App\Enums\ProxyTypes; +use App\Actions\Proxy\CheckConfiguration; +use App\Actions\Proxy\SaveConfiguration; use App\Models\Server; use Livewire\Component; @@ -48,8 +47,7 @@ class Proxy extends Component public function submit() { try { - resolve(SaveConfigurationSync::class)($this->server); - + SaveConfiguration::run($this->server); $this->server->proxy->redirect_url = $this->redirect_url; $this->server->save(); @@ -63,7 +61,7 @@ class Proxy extends Component public function reset_proxy_configuration() { try { - $this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server, true); + $this->proxy_settings = CheckConfiguration::run($this->server, true); } catch (\Throwable $e) { return handleError($e); } @@ -72,8 +70,7 @@ class Proxy extends Component public function loadProxyConfiguration() { try { - ray('loadProxyConfiguration'); - $this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server); + $this->proxy_settings = CheckConfiguration::run($this->server); } catch (\Throwable $e) { return handleError($e); } diff --git a/app/Http/Livewire/Server/Proxy/Deploy.php b/app/Http/Livewire/Server/Proxy/Deploy.php index df369c49a..ad60ca53a 100644 --- a/app/Http/Livewire/Server/Proxy/Deploy.php +++ b/app/Http/Livewire/Server/Proxy/Deploy.php @@ -2,7 +2,7 @@ namespace App\Http\Livewire\Server\Proxy; -use App\Actions\Proxy\SaveConfigurationSync; +use App\Actions\Proxy\SaveConfiguration; use App\Actions\Proxy\StartProxy; use App\Models\Server; use Livewire\Component; @@ -13,20 +13,25 @@ class Deploy extends Component public $proxy_settings = null; protected $listeners = ['proxyStatusUpdated']; - public function proxyStatusUpdated() { + public function proxyStatusUpdated() + { $this->server->refresh(); } public function startProxy() { - if ( - $this->server->proxy->last_applied_settings && - $this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings - ) { - resolve(SaveConfigurationSync::class)($this->server); - } + try { + if ( + $this->server->proxy->last_applied_settings && + $this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings + ) { + SaveConfiguration::run($this->server); + } - $activity = resolve(StartProxy::class)($this->server); - $this->emit('newMonitorActivity', $activity->id); + $activity = resolve(StartProxy::class)($this->server); + $this->emit('newMonitorActivity', $activity->id); + } catch (\Throwable $e) { + return handleError($e); + } } public function stop() diff --git a/app/Http/Livewire/Server/Proxy/Status.php b/app/Http/Livewire/Server/Proxy/Status.php index f808fdb81..061728049 100644 --- a/app/Http/Livewire/Server/Proxy/Status.php +++ b/app/Http/Livewire/Server/Proxy/Status.php @@ -2,6 +2,7 @@ namespace App\Http\Livewire\Server\Proxy; +use App\Jobs\ContainerStatusJob; use App\Models\Server; use Livewire\Component; @@ -18,9 +19,7 @@ class Status extends Component { try { if ($this->server->isFunctional()) { - $container = getContainerStatus(server: $this->server, container_id: 'coolify-proxy'); - $this->server->proxy->status = $container; - $this->server->save(); + dispatch_sync(new ContainerStatusJob($this->server)); $this->emit('proxyStatusUpdated'); } } catch (\Throwable $e) { diff --git a/app/Http/Livewire/Server/ShowPrivateKey.php b/app/Http/Livewire/Server/ShowPrivateKey.php index c9054f7df..a0aa6e1e2 100644 --- a/app/Http/Livewire/Server/ShowPrivateKey.php +++ b/app/Http/Livewire/Server/ShowPrivateKey.php @@ -37,14 +37,27 @@ class ShowPrivateKey extends Component try { ['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true); if ($uptime) { + $this->server->settings->update([ + 'is_reachable' => true + ]); $this->emit('success', 'Server is reachable with this private key.'); } else { + $this->server->settings->update([ + 'is_reachable' => false, + 'is_usable' => false + ]); $this->emit('error', 'Server is not reachable with this private key.'); return; } if ($dockerVersion) { + $this->server->settings->update([ + 'is_usable' => true + ]); $this->emit('success', 'Server is usable for Coolify.'); } else { + $this->server->settings->update([ + 'is_usable' => false + ]); $this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.'); } } catch (\Throwable $e) { diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php index 6d5feb5ce..20d6640bd 100644 --- a/app/Jobs/ContainerStatusJob.php +++ b/app/Jobs/ContainerStatusJob.php @@ -40,7 +40,8 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted return $this->server->uuid; } - private function checkServerConnection() { + private function checkServerConnection() + { $uptime = instant_remote_process(['uptime'], $this->server, false); if (!is_null($uptime)) { return true; @@ -51,7 +52,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted try { // ray()->clearAll(); $serverUptimeCheckNumber = 0; - $serverUptimeCheckNumberMax = 5; + $serverUptimeCheckNumberMax = 3; while (true) { if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) { $this->server->settings()->update(['is_reachable' => false]); @@ -65,19 +66,29 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted $serverUptimeCheckNumber++; sleep(5); } + $containers = instant_remote_process(["docker container ls -q"], $this->server); + if (!$containers) { + return; + } $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server); $containers = format_docker_command_output_to_json($containers); $applications = $this->server->applications(); $databases = $this->server->databases(); $previews = $this->server->previews(); - if ($this->server->isProxyShouldRun()) { - $foundProxyContainer = $containers->filter(function ($value, $key) { - return data_get($value, 'Name') === '/coolify-proxy'; - })->first(); - if (!$foundProxyContainer) { + + /// Check if proxy is running + $foundProxyContainer = $containers->filter(function ($value, $key) { + return data_get($value, 'Name') === '/coolify-proxy'; + })->first(); + if (!$foundProxyContainer) { + if ($this->server->isProxyShouldRun()) { resolve(StartProxy::class)($this->server, false); $this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server)); } + } else { + ray($foundProxyContainer); + $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status'); + $this->server->save(); } $foundApplications = []; $foundApplicationPreviews = []; @@ -88,10 +99,10 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted $labels = Arr::undot(format_docker_labels_to_json($labels)); $labelId = data_get($labels, 'coolify.applicationId'); if ($labelId) { - if (str_contains($labelId,'-pr-')) { + if (str_contains($labelId, '-pr-')) { $previewId = (int) Str::after($labelId, '-pr-'); $applicationId = (int) Str::before($labelId, '-pr-'); - $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id',$previewId)->first(); + $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $previewId)->first(); if ($preview) { $foundApplicationPreviews[] = $preview->id; $statusFromDb = $preview->status; @@ -128,10 +139,9 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted } } } - } $notRunningApplications = $applications->pluck('id')->diff($foundApplications); - foreach($notRunningApplications as $applicationId) { + foreach ($notRunningApplications as $applicationId) { $application = $applications->where('id', $applicationId)->first(); if ($application->status === 'exited') { continue; @@ -170,14 +180,14 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted $this->server->team->notify(new ContainerStopped($containerName, $this->server, $url)); } $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases); - foreach($notRunningDatabases as $database) { + foreach ($notRunningDatabases as $database) { $database = $databases->where('id', $database)->first(); if ($database->status === 'exited') { continue; } $database->update(['status' => 'exited']); - $name = data_get($database, 'name'); + $name = data_get($database, 'name'); $fqdn = data_get($database, 'fqdn'); $containerName = $name; @@ -249,7 +259,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted return Str::startsWith(data_get($value, 'Name'), "/$uuid-pr-{$preview->id}"); })->first(); } - } foreach ($databases as $database) { $uuid = data_get($database, 'uuid'); @@ -276,7 +285,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted } } } - // TODO Monitor other containers not managed by Coolify + // TODO Monitor other containers not managed by Coolify } catch (\Throwable $e) { send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage()); ray($e->getMessage()); diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index c78a560c7..e3db2be36 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -96,7 +96,7 @@ function setup_default_redirect_404(string|null $redirect_url, Server $server) $traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml"; ray($redirect_url); if (empty($redirect_url)) { - remote_process([ + instant_remote_process([ "rm -f $traefik_default_redirect_file", ], $server); } else { @@ -157,7 +157,7 @@ function setup_default_redirect_404(string|null $redirect_url, Server $server) $base64 = base64_encode($yaml); ray("mkdir -p $traefik_dynamic_conf_path"); - remote_process([ + instant_remote_process([ "mkdir -p $traefik_dynamic_conf_path", "echo '$base64' | base64 -d > $traefik_default_redirect_file", ], $server); diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index ae16c7bc0..7017ead10 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -7,15 +7,13 @@ use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\PrivateKey; use App\Models\Server; -use App\Notifications\Server\NotReachable; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Sleep; -use Spatie\Activitylog\Models\Activity; use Illuminate\Support\Str; +use Spatie\Activitylog\Contracts\Activity; function remote_process( array $command, @@ -110,11 +108,28 @@ function instant_remote_process(array $command, Server $server, $throwError = tr if (!$throwError) { return null; } - throw new \RuntimeException($process->errorOutput(), $exitCode); + return excludeCertainErrors($process->errorOutput(), $exitCode); } return $output; } - +function excludeCertainErrors(string $errorOutput, ?int $exitCode = null) { + $ignoredErrors = collect([ + 'Permission denied (publickey', + 'Could not resolve hostname', + ]); + $ignored = false; + foreach ($ignoredErrors as $ignoredError) { + if (Str::contains($errorOutput, $ignoredError)) { + $ignored = true; + break; + } + } + if ($ignored) { + // TODO: Create new exception and disable in sentry + throw new \RuntimeException($errorOutput, $exitCode); + } + throw new \RuntimeException($errorOutput, $exitCode); +} function decode_remote_command_output(?ApplicationDeploymentQueue $application_deployment_queue = null): Collection { $application = Application::find(data_get($application_deployment_queue, 'application_id')); diff --git a/composer.json b/composer.json index 79f3793dd..217560b57 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "lcobucci/jwt": "^5.0.0", "league/flysystem-aws-s3-v3": "^3.0", "livewire/livewire": "^v2.12.3", + "lorisleiva/laravel-actions": "^2.7", "masmerise/livewire-toaster": "^1.2", "nubs/random-name-generator": "^2.2", "phpseclib/phpseclib": "~3.0", diff --git a/composer.lock b/composer.lock index 63d0c626d..551d78229 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cf138424c896f30b035bc8cdff63e8d1", + "content-hash": "de2c45be3f03d43430549d963778dc4a", "packages": [ { "name": "aws/aws-crt-php", @@ -3059,6 +3059,153 @@ ], "time": "2023-08-11T04:02:34+00:00" }, + { + "name": "lorisleiva/laravel-actions", + "version": "v2.7.1", + "source": { + "type": "git", + "url": "https://github.com/lorisleiva/laravel-actions.git", + "reference": "5250614fd6b77e8e2780be0206174e069e94661d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lorisleiva/laravel-actions/zipball/5250614fd6b77e8e2780be0206174e069e94661d", + "reference": "5250614fd6b77e8e2780be0206174e069e94661d", + "shasum": "" + }, + "require": { + "illuminate/contracts": "9.0 - 9.34 || ^9.36 || ^10.0", + "lorisleiva/lody": "^0.4", + "php": "^8.0" + }, + "require-dev": { + "orchestra/testbench": "^8.5", + "pestphp/pest": "^1.23", + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Lorisleiva\\Actions\\ActionServiceProvider" + ], + "aliases": { + "Action": "Lorisleiva\\Actions\\Facades\\Actions" + } + } + }, + "autoload": { + "psr-4": { + "Lorisleiva\\Actions\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Loris Leiva", + "email": "loris.leiva@gmail.com", + "homepage": "https://lorisleiva.com", + "role": "Developer" + } + ], + "description": "Laravel components that take care of one specific task", + "homepage": "https://github.com/lorisleiva/laravel-actions", + "keywords": [ + "action", + "command", + "component", + "controller", + "job", + "laravel", + "object" + ], + "support": { + "issues": "https://github.com/lorisleiva/laravel-actions/issues", + "source": "https://github.com/lorisleiva/laravel-actions/tree/v2.7.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/lorisleiva", + "type": "github" + } + ], + "time": "2023-08-24T10:20:57+00:00" + }, + { + "name": "lorisleiva/lody", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/lorisleiva/lody.git", + "reference": "1a43e8e423f3b2b64119542bc44a2071208fae16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lorisleiva/lody/zipball/1a43e8e423f3b2b64119542bc44a2071208fae16", + "reference": "1a43e8e423f3b2b64119542bc44a2071208fae16", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0|^9.0|^10.0", + "php": "^8.0" + }, + "require-dev": { + "orchestra/testbench": "^8.0", + "pestphp/pest": "^1.20.0", + "phpunit/phpunit": "^9.5.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Lorisleiva\\Lody\\LodyServiceProvider" + ], + "aliases": { + "Lody": "Lorisleiva\\Lody\\Lody" + } + } + }, + "autoload": { + "psr-4": { + "Lorisleiva\\Lody\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Loris Leiva", + "email": "loris.leiva@gmail.com", + "homepage": "https://lorisleiva.com", + "role": "Developer" + } + ], + "description": "Load files and classes as lazy collections in Laravel.", + "homepage": "https://github.com/lorisleiva/lody", + "keywords": [ + "classes", + "collection", + "files", + "laravel", + "load" + ], + "support": { + "issues": "https://github.com/lorisleiva/lody/issues", + "source": "https://github.com/lorisleiva/lody/tree/v0.4.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/lorisleiva", + "type": "github" + } + ], + "time": "2023-02-05T15:03:45+00:00" + }, { "name": "masmerise/livewire-toaster", "version": "1.3.0", @@ -13089,5 +13236,5 @@ "php": "^8.2" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/config/sentry.php b/config/sentry.php index bc82840f5..ad788a56e 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.40', + 'release' => '4.0.0-beta.41', // 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 5e04a22e3..9d505bcb9 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ - +

Server

@if ($server->settings->is_reachable) diff --git a/resources/views/layouts/boarding.blade.php b/resources/views/layouts/boarding.blade.php index a63d26a41..39b1ae252 100644 --- a/resources/views/layouts/boarding.blade.php +++ b/resources/views/layouts/boarding.blade.php @@ -1,9 +1,19 @@ @extends('layouts.base') @section('body') -
-
- {{ $slot }} -
-
-@parent +
+
+ + + + + + + Close + + + + {{ $slot }} +
+
+ @parent @endsection diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index 45a11c4bd..8cd3034eb 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -23,9 +23,12 @@ -

You do not to manage your servers too much. Coolify do it for you.

-

All configurations are stored on your server, so everything works without Coolify (except integrations and automations).

-

You will get notified on your favourite platform (Discord, Telegram, Email, etc.) when something goes wrong, or an action needed from your side.

+

You do not to manage your servers too much. Coolify do + it for you.

+

All configurations are stored on your server, so + everything works without Coolify (except integrations and automations).

+

You will get notified on your favourite platform (Discord, + Telegram, Email, etc.) when something goes wrong, or an action needed from your side.

Next @@ -194,16 +197,6 @@
@if ($currentState === 'install-docker') - - - - - - - Close - - - Could not find Docker Engine on your server. Do you want me to install it for you? @@ -211,8 +204,11 @@ - Let's do - it! + Let's do it! + @if ($dockerInstallationStarted) + + Next + @endif

This will install the latest Docker Engine on your server, configure a few things to be able diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index ebb40f78f..4f3df85c2 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -48,10 +48,16 @@ @endif @if ($server->settings->is_reachable && !$server->settings->is_usable && $server->id !== 0) - - Install Docker Engine 24.0 - + @if ($dockerInstallationStarted) + + Validate Server + + @else + + Install Docker Engine 24.0 + + @endif @endif @if ($server->isFunctional())

Settings

diff --git a/resources/views/livewire/server/proxy/status.blade.php b/resources/views/livewire/server/proxy/status.blade.php index db496f561..a8eca1a2b 100644 --- a/resources/views/livewire/server/proxy/status.blade.php +++ b/resources/views/livewire/server/proxy/status.blade.php @@ -1,4 +1,3 @@ -
@if ($server->proxy->status === 'running') @@ -7,7 +6,8 @@ @else @endif -