feat: add proxy start to server validation

fix: boarding flow updated
This commit is contained in:
Andras Bacsai 2024-02-12 11:46:36 +01:00
parent ddfc1440cd
commit e1468da36a
15 changed files with 134 additions and 77 deletions

View File

@ -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();

View File

@ -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.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
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.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
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.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
return;
}
$this->setDeploymentUuid();

View File

@ -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.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
return;
}
$deployment_uuid = new Cuid2(7);
$server = Server::find($server_id);
$destination = StandaloneDocker::find($network_id);

View File

@ -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()
{

View File

@ -2,6 +2,7 @@
namespace App\Livewire\Server;
use App\Actions\Proxy\StartProxy;
use App\Models\Server;
use Livewire\Component;
@ -16,17 +17,21 @@ class ValidateAndInstall extends Component
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');
@ -44,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);

View File

@ -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
{

View File

@ -28,8 +28,8 @@
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
target="_blank" href="{{ getFqdnWithoutPort($domain) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
@ -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)
<li>
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
target="_blank" href="http://localhost:{{ explode(':', $port)[0] }}">
@ -114,9 +114,29 @@
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
Port {{ $port }}
{{ $application->destination->server->ip }}:{{ explode(':', $port)[0] }}
</a>
</li>
@if (count($application->additional_servers) > 0)
@foreach ($application->additional_servers as $server)
<li>
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
target="_blank"
href="http://{{ $server->ip }}:{{ explode(':', $port)[0] }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
{{ $server->ip }}:{{ explode(':', $port)[0] }}
</a>
</li>
@endforeach
@endif
@endif
@endforeach
@endif

View File

@ -136,6 +136,7 @@
@endif
</a>
</li>
@if (isInstanceAdmin())
<li title="Settings" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="/settings">
@ -152,7 +153,16 @@
</a>
</li>
@endif
<li title="Boarding" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('boarding') }}">
<svg class="{{ request()->is('boarding*') ? 'text-warning icon' : 'icon' }}"
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M224 128a8 8 0 0 1-8 8h-88a8 8 0 0 1 0-16h88a8 8 0 0 1 8 8m-96-56h88a8 8 0 0 0 0-16h-88a8 8 0 0 0 0 16m88 112h-88a8 8 0 0 0 0 16h88a8 8 0 0 0 0-16M82.34 42.34L56 68.69L45.66 58.34a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32 0l32-32a8 8 0 0 0-11.32-11.32m0 64L56 132.69l-10.34-10.35a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32 0l32-32a8 8 0 0 0-11.32-11.32m0 64L56 196.69l-10.34-10.35a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32 0l32-32a8 8 0 0 0-11.32-11.32" />
</svg>
Boarding
</a>
</li>
</div>
</div>
</div>

View File

@ -1,4 +1,4 @@
@props(['closeWithX' => 'false', 'fullScreen' => 'false'])
@props(['closeWithX' => false, 'fullScreen' => false])
<div x-data="{
slideOverOpen: false
}" class="relative w-auto h-auto">
@ -19,7 +19,7 @@
x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-full"
@class([
'max-w-md w-screen' => !$fullScreen,
'max-w-7xl w-screen' => $fullScreen,
'max-w-4xl w-screen' => $fullScreen,
])>
<div
class="flex flex-col h-full py-6 overflow-hidden border-l shadow-lg bg-base-100 border-neutral-800">

View File

@ -121,18 +121,24 @@
There are already servers available for your Team. Do you want to use one of them?
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create one for me)
</x-forms.button>
<div>
<form wire:submit='selectExistingServer' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing servers" class="w-96" id='selectedExistingServer'>
@foreach ($servers as $server)
<option wire:key="{{ $loop->index }}" value="{{ $server->id }}">
{{ $server->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Server</x-forms.button>
</form>
<div class="flex flex-col gap-4">
<div>
<x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create one
for
me)
</x-forms.button>
</div>
<div>
<form wire:submit='selectExistingServer' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing servers" class="w-96" id='selectedExistingServer'>
@foreach ($servers as $server)
<option wire:key="{{ $loop->index }}" value="{{ $server->id }}">
{{ $server->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Server</x-forms.button>
</form>
</div>
</div>
@if (!$serverReachable)
This server is not reachable with the following public key.
@ -216,7 +222,7 @@
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="isCloudflareTunnel" label="Cloudflare Tunnel" />
</div>
<x-forms.button type="submit">Check Connection</x-forms.button>
<x-forms.button type="submit">Continue</x-forms.button>
</form>
</x-slot:actions>
<x-slot:explanation>
@ -227,14 +233,15 @@
@endif
</div>
<div>
@if ($currentState === 'install-docker')
<x-boarding-step title="Install Docker">
@if ($currentState === 'validate-server')
<x-boarding-step title="Validate & Configure Server">
<x-slot:question>
Could not find Docker Engine on your server. Do you want me to install it for you?
I need to validate your server (connection, Docker Engine, etc) and configure if something is
missing for me. Are you okay with this?
</x-slot:question>
<x-slot:actions>
<x-slide-over closeWithX fullScreen>
<x-slot:title>Configuring Server</x-slot:title>
<x-slot:title>Validating & Configuring</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$this->createdServer" />
</x-slot:content>
@ -254,7 +261,7 @@
</x-boarding-step>
@endif
</div>
<div>
{{-- <div>
@if ($currentState === 'select-proxy')
<x-boarding-step title="Select a Proxy">
<x-slot:question>
@ -281,7 +288,7 @@
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
</div> --}}
<div>
@if ($currentState === 'create-project')
<x-boarding-step title="Project">

View File

@ -213,9 +213,9 @@
@endif --}}
<div class="flex flex-col justify-center gap-4 text-left xl:flex-row xl:flex-wrap">
@forelse($servers as $server)
<div class="box group" wire:click="setServer({{ $server }})">
<div class="w-64 box group" wire:click="setServer({{ $server }})">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
<div class="font-bold group-hover:text-white">
{{ $server->name }}
</div>
<div class="text-xs group-hover:text-white">
@ -263,13 +263,13 @@
Standalone Docker <span class="text-xs">({{ $standaloneDocker->name }})</span>
</div>
<div class="text-xs group-hover:text-white">
network: {{ $standaloneDocker->network }}</div>
Network: {{ $standaloneDocker->network }}</div>
</div>
</div>
@endforeach
@endif
<a href="{{ route('destination.new', ['server_id' => $server_id]) }}"
class="items-center justify-center pb-10 text-center box-without-bg group bg-coollabs hover:bg-coollabs-100">
class="items-center justify-center text-center box-without-bg group bg-coollabs hover:bg-coollabs-100">
<div class="flex flex-col mx-6 ">
<div class="font-bold text-white">
+ Add New

View File

@ -55,21 +55,25 @@
@endforeach
@endif
</div>
{{-- @if ($resource->getMorphClass() === 'App\Models\Application')
@if ($resource->getMorphClass() === 'App\Models\Application')
@if (count($networks) > 0)
<h4>Choose another server</h4>
<div class="pb-4 description">(experimental) </div>
<div class="grid grid-cols-1 gap-4 ">
<div class="grid grid-cols-1 gap-4">
@foreach ($networks as $network)
<div wire:click="addServer('{{ $network->id }}','{{ data_get($network, 'server.id') }}')"
class="box w-96">
{{ data_get($network, 'server.name') }}
{{ $network->name }}
class="relative flex flex-col text-white cursor-default box w-96">
<div>
Server: {{ data_get($network, 'server.name') }}
</div>
<div>
Network: {{ data_get($network, 'name') }}
</div>
</div>
@endforeach
</div>
@else
<div class="text-neutral-500">No additional servers available to attach.</div>
@endif
@endif --}}
@endif
</div>

View File

@ -19,7 +19,7 @@
@endif
@if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id !== 0)
<x-slide-over closeWithX fullScreen>
<x-slot:title>Configuring Server</x-slot:title>
<x-slot:title>Validating & Configuring</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$server" />
</x-slot:content>

View File

@ -51,7 +51,7 @@
</x-forms.button>
</div>
@else
<button onclick="checkProxy()"
<button x-on:click="$wire.dispatch('checkProxy')"
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
@ -64,9 +64,6 @@
@endif
@endif
<script>
function checkProxy() {
window.Livewire.dispatch('checkProxy')
}
Livewire.on('proxyChecked', () => {
startProxy.showModal();
window.Livewire.dispatch('startProxy');

View File

@ -79,6 +79,28 @@
d="m243.28 68.24l-24-23.56a16 16 0 0 0-22.58 0L104 136l-.11-.11l-36.64-35.27a16 16 0 0 0-22.57.06l-24 24a16 16 0 0 0 0 22.61l71.62 72a16 16 0 0 0 22.63 0l128.4-128.38a16 16 0 0 0-.05-22.67M103.62 208L32 136l24-24l.11.11l36.64 35.27a16 16 0 0 0 22.52 0L208.06 56L232 79.6Z" />
</g>
</svg></div>
@if ($proxy_started)
<div class="flex w-64 gap-2">Proxy Started: <svg class="w-5 h-5 text-success" viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path
d="m237.66 85.26l-128.4 128.4a8 8 0 0 1-11.32 0l-71.6-72a8 8 0 0 1 0-11.31l24-24a8 8 0 0 1 11.32 0l36.68 35.32a8 8 0 0 0 11.32 0l92.68-91.32a8 8 0 0 1 11.32 0l24 23.6a8 8 0 0 1 0 11.31"
opacity=".2" />
<path
d="m243.28 68.24l-24-23.56a16 16 0 0 0-22.58 0L104 136l-.11-.11l-36.64-35.27a16 16 0 0 0-22.57.06l-24 24a16 16 0 0 0 0 22.61l71.62 72a16 16 0 0 0 22.63 0l128.4-128.38a16 16 0 0 0-.05-22.67M103.62 208L32 136l24-24l.11.11l36.64 35.27a16 16 0 0 0 22.52 0L208.06 56L232 79.6Z" />
</g>
</svg></div>
@else
@if ($error)
<div class="flex w-64 gap-2">Proxy Started: <svg class="w-5 h-5 text-error" viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M208.49 191.51a12 12 0 0 1-17 17L128 145l-63.51 63.49a12 12 0 0 1-17-17L111 128L47.51 64.49a12 12 0 0 1 17-17L128 111l63.51-63.52a12 12 0 0 1 17 17L145 128Z" />
</svg></div>
@else
<div class="w-64"><x-loading text="Proxy Started:" /></div>
@endif
@endif
@else
@if ($error)
<div class="flex w-64 gap-2">Docker Compose is installed: <svg class="w-5 h-5 text-error"
@ -90,6 +112,7 @@
<div class="w-64"><x-loading text="Docker Compose is installed:" /></div>
@endif
@endif
@endif
@isset($docker_version)
<div class="flex w-64 gap-2">Minimum Docker version installed: <svg class="w-5 h-5 text-success"
@ -103,6 +126,7 @@
</g>
</svg></div>
@endisset
<livewire:new-activity-monitor header="Logs" />
@isset($error)
<pre class="font-bold whitespace-pre-line text-error">{!! $error !!}</pre>