fix: server validation process

This commit is contained in:
Andras Bacsai 2023-10-09 11:00:18 +02:00
parent 5b584a6c6d
commit dcaa7a6ad7
30 changed files with 321 additions and 149 deletions

View File

@ -2,12 +2,14 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Server; use App\Models\Server;
use App\Models\StandaloneDocker; use App\Models\StandaloneDocker;
class InstallDocker class InstallDocker
{ {
public function __invoke(Server $server) use AsAction;
public function handle(Server $server)
{ {
$dockerVersion = '24.0'; $dockerVersion = '24.0';
$config = base64_encode('{ $config = base64_encode('{

View File

@ -1,32 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\PrivateKey;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
class ServerController extends Controller
{
use AuthorizesRequests, ValidatesRequests;
public function new_server()
{
$privateKeys = PrivateKey::ownedByCurrentTeam()->get();
if (!isCloud()) {
return view('server.create', [
'limit_reached' => false,
'private_keys' => $privateKeys,
]);
}
$team = currentTeam();
$servers = $team->servers->count();
['serverLimit' => $serverLimit] = $team->limits;
$limit_reached = $servers >= $serverLimit;
return view('server.create', [
'limit_reached' => $limit_reached,
'private_keys' => $privateKeys,
]);
}
}

View File

@ -220,7 +220,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
public function installDocker() public function installDocker()
{ {
$this->dockerInstallationStarted = true; $this->dockerInstallationStarted = true;
$activity = resolve(InstallDocker::class)($this->createdServer); $activity = InstallDocker::run($this->createdServer);
$this->emit('newMonitorActivity', $activity->id); $this->emit('newMonitorActivity', $activity->id);
} }
public function dockerInstalledOrSkipped() public function dockerInstalledOrSkipped()

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Livewire\Server;
use App\Models\PrivateKey;
use Livewire\Component;
class Create extends Component
{
public $private_keys = [];
public bool $limit_reached = false;
public function mount()
{
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
if (!isCloud()) {
$this->limit_reached = false;
return;
}
$team = currentTeam();
$servers = $team->servers->count();
['serverLimit' => $serverLimit] = $team->limits;
$this->limit_reached = $servers >= $serverLimit;
}
public function render()
{
return view('livewire.server.create');
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Livewire\Server\Destination;
use App\Models\Server;
use Livewire\Component;
class Show extends Component
{
public ?Server $server = null;
public $parameters = [];
public function mount()
{
$this->parameters = get_route_parameters();
try {
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.server.destination.show');
}
}

View File

@ -11,11 +11,12 @@ class Form extends Component
{ {
use AuthorizesRequests; use AuthorizesRequests;
public Server $server; public Server $server;
public $uptime; public bool $isValidConnection = false;
public $dockerVersion; public bool $isValidDocker = false;
public string|null $wildcard_domain = null; public ?string $wildcard_domain = null;
public int $cleanup_after_percentage; public int $cleanup_after_percentage;
public bool $dockerInstallationStarted = false; public bool $dockerInstallationStarted = false;
protected $listeners = ['serverRefresh'];
protected $rules = [ protected $rules = [
'server.name' => 'required|min:6', 'server.name' => 'required|min:6',
@ -44,37 +45,49 @@ class Form extends Component
$this->wildcard_domain = $this->server->settings->wildcard_domain; $this->wildcard_domain = $this->server->settings->wildcard_domain;
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage; $this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
} }
public function instantSave() { public function serverRefresh() {
$this->validateServer();
}
public function instantSave()
{
refresh_server_connection($this->server->privateKey); refresh_server_connection($this->server->privateKey);
$this->validateServer(); $this->validateServer();
$this->server->settings->save(); $this->server->settings->save();
} }
public function installDocker() public function installDocker()
{ {
$this->emit('installDocker');
$this->dockerInstallationStarted = true; $this->dockerInstallationStarted = true;
$activity = resolve(InstallDocker::class)($this->server); $activity = InstallDocker::run($this->server);
$this->emit('newMonitorActivity', $activity->id); $this->emit('newMonitorActivity', $activity->id);
} }
public function validateServer() public function validateServer($install = true)
{ {
try { try {
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true); $uptime = $this->server->validateConnection();
if ($uptime) { if ($uptime) {
$this->uptime = $uptime; $install && $this->emit('success', 'Server is reachable.');
$this->emit('success', 'Server is reachable.');
} else { } else {
$this->emit('error', 'Server is not reachable.'); $install &&$this->emit('error', 'Server is not reachable. Please check your connection and private key configuration.');
return; return;
} }
if ($dockerVersion) { $dockerInstalled = $this->server->validateDockerEngine();
$this->dockerVersion = $dockerVersion; if ($dockerInstalled) {
$this->emit('success', 'Docker Engine 23+ is installed!'); $install && $this->emit('success', 'Docker Engine is installed.<br> Checking version.');
} else { } else {
$this->emit('error', 'No Docker Engine or older than 23 version installed.'); $install && $this->installDocker();
return;
}
$dockerVersion = $this->server->validateDockerEngineVersion();
if ($dockerVersion) {
$install && $this->emit('success', 'Docker Engine version is 23+.');
} else {
$install && $this->installDocker();
return;
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this, customErrorMessage: "Server is not reachable: "); return handleError($e, $this);
} finally { } finally {
$this->emit('proxyStatusUpdated'); $this->emit('proxyStatusUpdated');
} }

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Livewire\Server\PrivateKey;
use App\Models\PrivateKey;
use App\Models\Server;
use Livewire\Component;
class Show extends Component
{
public ?Server $server = null;
public $privateKeys = [];
public $parameters = [];
public function mount()
{
$this->parameters = get_route_parameters();
try {
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
}
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.server.private-key.show');
}
}

View File

@ -11,7 +11,7 @@ class Deploy extends Component
public Server $server; public Server $server;
public bool $traefikDashboardAvailable = false; public bool $traefikDashboardAvailable = false;
public ?string $currentRoute = null; public ?string $currentRoute = null;
protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable']; protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable', 'serverRefresh' => 'proxyStatusUpdated'];
public function mount() { public function mount() {
$this->currentRoute = request()->route()->getName(); $this->currentRoute = request()->route()->getName();

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Livewire\Server\Proxy;
use App\Models\Server;
use Livewire\Component;
class Show extends Component
{
public ?Server $server = null;
public $parameters = [];
public function mount()
{
$this->parameters = get_route_parameters();
try {
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.all');
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.server.proxy.show');
}
}

View File

@ -26,7 +26,9 @@ class Status extends Component
} }
public function getProxyStatusWithNoti() public function getProxyStatusWithNoti()
{ {
$this->emit('success', 'Refreshed proxy status.'); if ($this->server->isFunctional()) {
$this->getProxyStatus(); $this->emit('success', 'Refreshed proxy status.');
$this->getProxyStatus();
}
} }
} }

View File

@ -10,8 +10,10 @@ class Show extends Component
{ {
use AuthorizesRequests; use AuthorizesRequests;
public ?Server $server = null; public ?Server $server = null;
public $parameters = [];
public function mount() public function mount()
{ {
$this->parameters = get_route_parameters();
try { try {
$this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first(); $this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) { if (is_null($this->server)) {
@ -21,6 +23,10 @@ class Show extends Component
return handleError($e, $this); return handleError($e, $this);
} }
} }
public function submit()
{
$this->emit('serverRefresh');
}
public function render() public function render()
{ {
return view('livewire.server.show'); return view('livewire.server.show');

View File

@ -32,36 +32,34 @@ class ShowPrivateKey extends Component
} }
} }
public function checkConnection() public function checkConnection($install = false)
{ {
try { try {
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true); $uptime = $this->server->validateConnection();
if ($uptime) { if ($uptime) {
$this->server->settings->update([ $install && $this->emit('success', 'Server is reachable.');
'is_reachable' => true
]);
$this->emit('success', 'Server is reachable with this private key.');
} else { } else {
$this->server->settings->update([ $install && $this->emit('error', 'Server is not reachable. Please check your connection and private key configuration.');
'is_reachable' => false,
'is_usable' => false
]);
$this->emit('error', 'Server is not reachable with this private key.');
return; return;
} }
if ($dockerVersion) { $dockerInstalled = $this->server->validateDockerEngine();
$this->server->settings->update([ if ($dockerInstalled) {
'is_usable' => true $install && $this->emit('success', 'Docker Engine is installed.<br> Checking version.');
]);
$this->emit('success', 'Server is usable for Coolify.');
} else { } else {
$this->server->settings->update([ $install && $this->installDocker();
'is_usable' => false return;
]); }
$this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.'); $dockerVersion = $this->server->validateDockerEngineVersion();
if ($dockerVersion) {
$install && $this->emit('success', 'Docker Engine version is 23+.');
} else {
$install && $this->installDocker();
return;
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} finally {
$this->emit('proxyStatusUpdated');
} }
} }

View File

@ -41,15 +41,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
{ {
return $this->server->uuid; return $this->server->uuid;
} }
public function handle()
private function checkServerConnection()
{
$uptime = instant_remote_process(['uptime'], $this->server, false);
if (!is_null($uptime)) {
return true;
}
}
public function handle(): void
{ {
try { try {
ray("checking server status for {$this->server->name}"); ray("checking server status for {$this->server->name}");
@ -57,9 +49,11 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
$serverUptimeCheckNumber = 0; $serverUptimeCheckNumber = 0;
$serverUptimeCheckNumberMax = 3; $serverUptimeCheckNumberMax = 3;
while (true) { while (true) {
ray('checking # ' . $serverUptimeCheckNumber);
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) { if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
send_internal_notification('Server unreachable: ' . $this->server->name); send_internal_notification('Server unreachable: ' . $this->server->name);
if ($this->server->unreachable_email_sent === false) { if ($this->server->unreachable_email_sent === false) {
ray('Server unreachable, sending notification...');
$this->server->team->notify(new Unreachable($this->server)); $this->server->team->notify(new Unreachable($this->server));
} }
$this->server->settings()->update([ $this->server->settings()->update([
@ -68,7 +62,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
$this->server->update(['unreachable_email_sent' => true]); $this->server->update(['unreachable_email_sent' => true]);
return; return;
} }
$result = $this->checkServerConnection(); $result = $this->server->validateConnection();
if ($result) { if ($result) {
break; break;
} }
@ -76,6 +70,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
sleep(5); sleep(5);
} }
if (data_get($this->server, 'unreachable_email_sent') === true) { if (data_get($this->server, 'unreachable_email_sent') === true) {
ray('Server is reachable again, sending notification...');
$this->server->team->notify(new Revived($this->server)); $this->server->team->notify(new Revived($this->server));
$this->server->update(['unreachable_email_sent' => false]); $this->server->update(['unreachable_email_sent' => false]);
} }
@ -88,7 +83,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
'is_usable' => true 'is_usable' => true
]); ]);
} }
$this->server->validateDockerEngine(true);
$containers = instant_remote_process(["docker container ls -q"], $this->server); $containers = instant_remote_process(["docker container ls -q"], $this->server);
if (!$containers) { if (!$containers) {
return; return;
@ -288,7 +283,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage()); send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
throw $e; return handleError($e);
} }
} }
} }

View File

@ -16,9 +16,11 @@ class Server extends BaseModel
protected static function booted() protected static function booted()
{ {
static::saved(function ($server) { static::saving(function ($server) {
$server->ip = Str::of($server->ip)->trim(); $server->forceFill([
$server->user = Str::of($server->user)->trim(); 'ip' => Str::of($server->ip)->trim(),
'user' => Str::of($server->user)->trim(),
]);
}); });
static::created(function ($server) { static::created(function ($server) {
@ -205,4 +207,48 @@ class Server extends BaseModel
{ {
return $this->settings->is_reachable && $this->settings->is_usable; return $this->settings->is_reachable && $this->settings->is_usable;
} }
public function validateConnection()
{
$uptime = instant_remote_process(['uptime'], $this, false);
if (!$uptime) {
$this->settings->is_reachable = false;
$this->settings->save();
return false;
}
$this->settings->is_reachable = true;
$this->settings->save();
return true;
}
public function validateDockerEngine($throwError = false)
{
$dockerBinary = instant_remote_process(["command -v docker"], $this, false);
if (is_null($dockerBinary)) {
$this->settings->is_usable = false;
$this->settings->save();
if ($throwError) {
throw new \Exception('Server is not usable.');
}
return false;
}
$this->settings->is_usable = true;
$this->settings->save();
$this->validateCoolifyNetwork();
return true;
}
public function validateDockerEngineVersion()
{
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this, false);
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
if (is_null($dockerVersion)) {
$this->settings->is_usable = false;
$this->settings->save();
return false;
}
$this->settings->is_usable = true;
$this->settings->save();
return true;
}
public function validateCoolifyNetwork() {
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
}
} }

View File

@ -4,6 +4,9 @@ namespace App\Notifications\Server;
use App\Models\Server; use App\Models\Server;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\TelegramChannel;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
@ -22,7 +25,21 @@ class Revived extends Notification implements ShouldQueue
public function via(object $notifiable): array public function via(object $notifiable): array
{ {
return setNotificationChannels($notifiable, 'status_changes'); $channels = [];
$isEmailEnabled = isEmailEnabled($notifiable);
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
if ($isDiscordEnabled) {
$channels[] = DiscordChannel::class;
}
if ($isEmailEnabled ) {
$channels[] = EmailChannel::class;
}
if ($isTelegramEnabled) {
$channels[] = TelegramChannel::class;
}
return $channels;
} }
public function toMail(): MailMessage public function toMail(): MailMessage

View File

@ -3,6 +3,9 @@
namespace App\Notifications\Server; namespace App\Notifications\Server;
use App\Models\Server; use App\Models\Server;
use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\TelegramChannel;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@ -20,7 +23,21 @@ class Unreachable extends Notification implements ShouldQueue
public function via(object $notifiable): array public function via(object $notifiable): array
{ {
return setNotificationChannels($notifiable, 'status_changes'); $channels = [];
$isEmailEnabled = isEmailEnabled($notifiable);
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
if ($isDiscordEnabled) {
$channels[] = DiscordChannel::class;
}
if ($isEmailEnabled ) {
$channels[] = EmailChannel::class;
}
if ($isTelegramEnabled) {
$channels[] = TelegramChannel::class;
}
return $channels;
} }
public function toMail(): MailMessage public function toMail(): MailMessage

View File

@ -56,7 +56,7 @@ a {
@apply flex items-center p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem]; @apply flex items-center p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem];
} }
.box-without-bg { .box-without-bg {
@apply flex items-center p-2 transition-colors min-h-16 hover:text-white hover:no-underline min-w-[24rem]; @apply flex items-center p-2 transition-colors min-h-16 hover:text-white hover:no-underline min-w-[24rem];
} }
.lds-heart { .lds-heart {

View File

@ -8,25 +8,25 @@
<nav class="navbar-main"> <nav class="navbar-main">
<a class="{{ request()->routeIs('server.show') ? 'text-white' : '' }}" <a class="{{ request()->routeIs('server.show') ? 'text-white' : '' }}"
href="{{ route('server.show', [ href="{{ route('server.show', [
'server_uuid' => Route::current()->parameters()['server_uuid'], 'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}"> ]) }}">
<button>General</button> <button>General</button>
</a> </a>
<a class="{{ request()->routeIs('server.private-key') ? 'text-white' : '' }}" <a class="{{ request()->routeIs('server.private-key') ? 'text-white' : '' }}"
href="{{ route('server.private-key', [ href="{{ route('server.private-key', [
'server_uuid' => Route::current()->parameters()['server_uuid'], 'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}"> ]) }}">
<button>Private Key</button> <button>Private Key</button>
</a> </a>
<a class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}" <a class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}"
href="{{ route('server.proxy', [ href="{{ route('server.proxy', [
'server_uuid' => Route::current()->parameters()['server_uuid'], 'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}"> ]) }}">
<button>Proxy</button> <button>Proxy</button>
</a> </a>
<a class="{{ request()->routeIs('server.destinations') ? 'text-white' : '' }}" <a class="{{ request()->routeIs('server.destinations') ? 'text-white' : '' }}"
href="{{ route('server.destinations', [ href="{{ route('server.destinations', [
'server_uuid' => Route::current()->parameters()['server_uuid'], 'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}"> ]) }}">
<button>Destinations</button> <button>Destinations</button>
</a> </a>

View File

@ -5,10 +5,6 @@ Container ({{ $containerName }}) has been restarted automatically on {{$serverNa
@if ($containerName === 'coolify-proxy') @if ($containerName === 'coolify-proxy')
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources. Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
Note: The proxy should not stop unexpectedly, so please check what is going on your server.
If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to Custom(None). If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to Custom(None).
@endif @endif

View File

@ -0,0 +1,9 @@
<div>
@if ($private_keys->count() === 0)
<h1>Create Private Key</h1>
<div class="subtitle">You need to create a private key before you can create a server.</div>
<livewire:private-key.create from="server" />
@else
<livewire:server.new.by-ip :private_keys="$private_keys" :limit_reached="$limit_reached" />
@endif
</div>

View File

@ -0,0 +1,4 @@
<div>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:destination.show :server="$server" />
</div>

View File

@ -1,4 +1,4 @@
<div> <div x-init="$wire.validateServer(false)">
<x-modal yesOrNo modalId="deleteServer" modalTitle="Delete Server"> <x-modal yesOrNo modalId="deleteServer" modalTitle="Delete Server">
<x-slot:modalBody> <x-slot:modalBody>
<p>This server will be deleted. It is not reversible. <br>Please think again..</p> <p>This server will be deleted. It is not reversible. <br>Please think again..</p>
@ -25,6 +25,11 @@
@else @else
Server is reachable and validated. Server is reachable and validated.
@endif @endif
@if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id !== 0)
<x-forms.button class="mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100" wire:click.prevent='validateServer' isHighlighted>
Validate Server & Install Docker Engine
</x-forms.button>
@endif
<div class="flex flex-col gap-2 pt-4"> <div class="flex flex-col gap-2 pt-4">
<div class="flex flex-col w-full gap-2 lg:flex-row"> <div class="flex flex-col w-full gap-2 lg:flex-row">
<x-forms.input id="server.name" label="Name" required /> <x-forms.input id="server.name" label="Name" required />
@ -42,27 +47,12 @@
</div> </div>
</div> </div>
<div class="w-64"> <div class="w-64">
<x-forms.checkbox instantSave helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>" <x-forms.checkbox instantSave
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="server.settings.is_cloudflare_tunnel" label="Cloudflare Tunnel" /> id="server.settings.is_cloudflare_tunnel" label="Cloudflare Tunnel" />
</div> </div>
</div> </div>
@if (!$server->settings->is_reachable)
<x-forms.button class="mt-8 mb-4 box" wire:click.prevent='validateServer'>
Validate Server
</x-forms.button>
@endif
@if ($server->settings->is_reachable && !$server->settings->is_usable && $server->id !== 0)
@if ($dockerInstallationStarted)
<x-forms.button class="mt-8 mb-4 box" wire:click.prevent='validateServer'>
Validate Server
</x-forms.button>
@else
<x-forms.button class="mt-8 mb-4 box" onclick="installDocker.showModal()"
wire:click.prevent='installDocker' isHighlighted>
Install Docker Engine 24.0
</x-forms.button>
@endif
@endif
@if ($server->isFunctional()) @if ($server->isFunctional())
<h3 class="py-4">Settings</h3> <h3 class="py-4">Settings</h3>
<x-forms.input id="cleanup_after_percentage" label="Disk Cleanup threshold (%)" required <x-forms.input id="cleanup_after_percentage" label="Disk Cleanup threshold (%)" required
@ -80,4 +70,11 @@
Delete Delete
</x-forms.button> </x-forms.button>
@endif @endif
<script>
Livewire.on('installDocker', () => {
console.log('asd');
installDocker.showModal();
})
</script>
</div> </div>

View File

@ -1,4 +1,4 @@
<x-layout> <div>
<x-server.navbar :server="$server" /> <x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.show-private-key :server="$server" :privateKeys="$privateKeys" /> <livewire:server.show-private-key :server="$server" :privateKeys="$privateKeys" />
</x-layout> </div>

View File

@ -0,0 +1,4 @@
<div>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.proxy :server="$server" />
</div>

View File

@ -1,7 +1,7 @@
<div> <div>
<x-modal noSubmit modalId="installDocker"> <x-modal modalId="installDocker">
<x-slot:modalBody> <x-slot:modalBody>
<livewire:activity-monitor header="Installation Logs" /> <livewire:activity-monitor header="Docker Installation Logs" />
</x-slot:modalBody> </x-slot:modalBody>
<x-slot:modalSubmit> <x-slot:modalSubmit>
<x-forms.button onclick="installDocker.close()" type="submit"> <x-forms.button onclick="installDocker.close()" type="submit">
@ -9,6 +9,6 @@
</x-forms.button> </x-forms.button>
</x-slot:modalSubmit> </x-slot:modalSubmit>
</x-modal> </x-modal>
<x-server.navbar :server="$server" /> <x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.form :server="$server" /> <livewire:server.form :server="$server" />
</div> </div>

View File

@ -1,3 +0,0 @@
<x-layout>
</x-layout>

View File

@ -1,4 +0,0 @@
<x-layout>
<x-server.navbar :server="$server" />
<livewire:destination.show :server="$server" />
</x-layout>

View File

@ -1,4 +0,0 @@
<x-layout>
<x-server.navbar :server="$server" />
<livewire:server.proxy :server="$server" />
</x-layout>

View File

@ -1,4 +0,0 @@
<x-layout>
<x-server.navbar :server="$server" />
<livewire:server.form :server="$server" />
</x-layout>

View File

@ -13,6 +13,10 @@ use App\Http\Livewire\Dev\Compose as Compose;
use App\Http\Livewire\Dashboard; use App\Http\Livewire\Dashboard;
use App\Http\Livewire\Project\Shared\Logs; use App\Http\Livewire\Project\Shared\Logs;
use App\Http\Livewire\Server\All; use App\Http\Livewire\Server\All;
use App\Http\Livewire\Server\Create;
use App\Http\Livewire\Server\Destination\Show as DestinationShow;
use App\Http\Livewire\Server\PrivateKey\Show as PrivateKeyShow;
use App\Http\Livewire\Server\Proxy\Show as ProxyShow;
use App\Http\Livewire\Server\Show; use App\Http\Livewire\Server\Show;
use App\Http\Livewire\Waitlist\Index as WaitlistIndex; use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
use App\Models\GithubApp; use App\Models\GithubApp;
@ -102,18 +106,11 @@ Route::middleware(['auth'])->group(function () {
Route::middleware(['auth'])->group(function () { Route::middleware(['auth'])->group(function () {
Route::get('/servers', All::class)->name('server.all'); Route::get('/servers', All::class)->name('server.all');
Route::get('/server/new', [ServerController::class, 'new_server'])->name('server.create'); Route::get('/server/new', Create::class)->name('server.create');
Route::get('/server/{server_uuid}', Show::class)->name('server.show'); Route::get('/server/{server_uuid}', Show::class)->name('server.show');
Route::get('/server/{server_uuid}/proxy', fn () => view('server.proxy', [ Route::get('/server/{server_uuid}/proxy', ProxyShow::class)->name('server.proxy');
'server' => Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->firstOrFail(), Route::get('/server/{server_uuid}/private-key', PrivateKeyShow::class)->name('server.private-key');
]))->name('server.proxy'); Route::get('/server/{server_uuid}/destinations', DestinationShow::class)->name('server.destinations');
Route::get('/server/{server_uuid}/private-key', fn () => view('server.private-key', [
'server' => Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->firstOrFail(),
'privateKeys' => PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false),
]))->name('server.private-key');
Route::get('/server/{server_uuid}/destinations', fn () => view('server.destinations', [
'server' => Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->firstOrFail()
]))->name('server.destinations');
}); });