ui updates on server

This commit is contained in:
Andras Bacsai
2024-10-17 14:56:36 +02:00
parent 4c95647b96
commit 2315bdb93f
29 changed files with 614 additions and 373 deletions

View File

@@ -5,13 +5,13 @@ namespace App\Actions\Server;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class RemoveServer
class DeleteServer
{
use AsAction;
public function handle(Server $server)
{
StopSentinel::run($server);
$server->delete();
$server->forceDelete();
}
}

View File

@@ -34,9 +34,9 @@ class StartSentinel
'COLLECTOR_RETENTION_PERIOD_DAYS' => $metrics_history,
];
if (isDev()) {
data_set($environments, 'DEBUG', 'true');
// data_set($environments, 'DEBUG', 'true');
$mount_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/sentinel';
$image = 'sentinel';
// $image = 'sentinel';
}
$docker_environments = '-e "' . implode('" -e "', array_map(fn($key, $value) => "$key=$value", array_keys($environments), $environments)) . '"';

View File

@@ -2,7 +2,7 @@
namespace App\Http\Controllers\Api;
use App\Actions\Server\RemoveServer;
use App\Actions\Server\DeleteServer;
use App\Actions\Server\ValidateServer;
use App\Enums\ProxyStatus;
use App\Enums\ProxyTypes;
@@ -726,7 +726,8 @@ class ServersController extends Controller
if ($server->definedResources()->count() > 0) {
return response()->json(['message' => 'Server has resources, so you need to delete them before.'], 400);
}
RemoveServer::dispatch($server);
$server->delete();
DeleteServer::dispatch($server);
return response()->json(['message' => 'Server deleted.']);
}

View File

@@ -66,7 +66,7 @@ class Show extends Component
return ! $alreadyAddedNetworks->contains('network', $network['Name']);
});
if ($this->networks->count() === 0) {
$this->dispatch('success', 'No new networks found.');
$this->dispatch('success', 'No new destinations found on this server.');
return;
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Livewire\Server;
use App\Models\Server;
use Livewire\Component;
class Advanced extends Component
{
public Server $server;
protected $rules = [
'server.settings.concurrent_builds' => 'required|integer|min:1',
'server.settings.dynamic_timeout' => 'required|integer|min:1',
'server.settings.force_docker_cleanup' => 'required|boolean',
'server.settings.docker_cleanup_frequency' => 'required_if:server.settings.force_docker_cleanup,true|string',
'server.settings.docker_cleanup_threshold' => 'required_if:server.settings.force_docker_cleanup,false|integer|min:1|max:100',
'server.settings.delete_unused_volumes' => 'boolean',
'server.settings.delete_unused_networks' => 'boolean',
];
protected $validationAttributes = [
'server.settings.concurrent_builds' => 'Concurrent Builds',
'server.settings.dynamic_timeout' => 'Dynamic Timeout',
'server.settings.force_docker_cleanup' => 'Force Docker Cleanup',
'server.settings.docker_cleanup_frequency' => 'Docker Cleanup Frequency',
'server.settings.docker_cleanup_threshold' => 'Docker Cleanup Threshold',
'server.settings.delete_unused_volumes' => 'Delete Unused Volumes',
'server.settings.delete_unused_networks' => 'Delete Unused Networks',
];
public function instantSave()
{
try {
$this->validate();
$this->server->settings->save();
$this->dispatch('success', 'Server updated.');
$this->dispatch('refreshServerShow');
} catch (\Throwable $e) {
$this->server->settings->refresh();
return handleError($e, $this);
}
}
public function submit()
{
try {
$frequency = $this->server->settings->docker_cleanup_frequency;
if (empty($frequency) || ! validate_cron_expression($frequency)) {
$this->server->settings->docker_cleanup_frequency = '*/10 * * * *';
throw new \Exception('Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.');
}
$this->server->settings->save();
$this->dispatch('success', 'Server updated.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.server.advanced');
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Livewire\Server;
use App\Models\Server;
use Livewire\Component;
class CloudflareTunnels extends Component
{
public Server $server;
protected $rules = [
'server.settings.is_cloudflare_tunnel' => 'required|boolean',
];
protected $validationAttributes = [
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
];
public function instantSave()
{
try {
$this->validate();
$this->server->settings->save();
$this->dispatch('success', 'Server updated.');
$this->dispatch('refreshServerShow');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function manualCloudflareConfig()
{
$this->server->settings->is_cloudflare_tunnel = true;
$this->server->settings->save();
$this->server->refresh();
$this->dispatch('success', 'Cloudflare Tunnels enabled.');
}
public function render()
{
return view('livewire.server.cloudflare-tunnels');
}
}

View File

@@ -2,7 +2,7 @@
namespace App\Livewire\Server;
use App\Actions\Server\RemoveServer;
use App\Actions\Server\DeleteServer;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
@@ -28,8 +28,8 @@ class Delete extends Component
return;
}
RemoveServer::run($this->server);
$this->server->delete();
DeleteServer::dispatch($this->server);
return redirect()->route('server.index');
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@@ -89,7 +89,7 @@ class Form extends Component
'server.settings.sentinel_metrics_history_days' => 'Metrics History',
'server.settings.sentinel_push_interval_seconds' => 'Push Interval',
'server.settings.is_sentinel_enabled' => 'Server API',
'server.settings.sentinel_custom_url' => 'Sentinel URL',
'server.settings.sentinel_custom_url' => 'Coolify URL',
'server.settings.server_timezone' => 'Server Timezone',
'server.settings.delete_unused_volumes' => 'Delete Unused Volumes',
'server.settings.delete_unused_networks' => 'Delete Unused Networks',
@@ -106,7 +106,8 @@ class Form extends Component
$this->server->settings->delete_unused_networks = $server->settings->delete_unused_networks;
}
public function checkSyncStatus(){
public function checkSyncStatus()
{
$this->server->refresh();
$this->server->settings->refresh();
}
@@ -114,9 +115,10 @@ class Form extends Component
public function regenerateSentinelToken()
{
try {
$this->server->generateSentinelToken();
$this->server->settings->generateSentinelToken();
$this->server->settings->refresh();
$this->dispatch('success', 'Sentinel token regenerated. Please restart your Sentinel.');
$this->restartSentinel(notification: false);
$this->dispatch('success', 'Token regenerated & Sentinel restarted.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -152,18 +154,28 @@ class Form extends Component
$this->dispatch('proxyStatusUpdated');
}
public function updatedServerSettingsIsSentinelEnabled($value){
if($value === false){
public function updatedServerSettingsIsSentinelEnabled($value)
{
$this->validate();
$this->validate([
'server.settings.sentinel_custom_url' => 'required|url',
]);
if ($value === false) {
StopSentinel::dispatch($this->server);
$this->server->settings->is_metrics_enabled = false;
$this->server->settings->save();
$this->server->sentinelHeartbeat(isReset: true);
} else {
StartSentinel::run($this->server);
try {
StartSentinel::run($this->server);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}
public function updatedServerSettingsIsMetricsEnabled(){
public function updatedServerSettingsIsMetricsEnabled()
{
$this->restartSentinel();
}
@@ -171,6 +183,7 @@ class Form extends Component
public function instantSave()
{
try {
$this->validate();
refresh_server_connection($this->server->privateKey);
$this->validateServer(false);
@@ -179,6 +192,14 @@ class Form extends Component
$this->dispatch('success', 'Server updated.');
$this->dispatch('refreshServerShow');
// if ($this->server->isSentinelEnabled()) {
// StartSentinel::run($this->server);
// } else {
// StopSentinel::run($this->server);
// $this->server->settings->is_metrics_enabled = false;
// $this->server->settings->save();
// $this->server->sentinelHeartbeat(isReset: true);
// }
// if ($this->server->isSentinelEnabled()) {
// PullSentinelImageJob::dispatchSync($this->server);
// ray('Sentinel is enabled');
@@ -196,16 +217,23 @@ class Form extends Component
// $this->checkPortForServerApi();
} catch (\Throwable $e) {
$this->server->settings->refresh();
return handleError($e, $this);
}
}
public function restartSentinel()
public function restartSentinel($notification = true)
{
try {
$this->validate();
$this->validate([
'server.settings.sentinel_custom_url' => 'required|url',
]);
$version = get_latest_sentinel_version();
StartSentinel::run($this->server, $version, true);
$this->dispatch('success', 'Sentinel started.');
if ($notification) {
$this->dispatch('success', 'Sentinel started.');
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -227,7 +255,7 @@ class Form extends Component
$this->server->settings->save();
$this->dispatch('proxyStatusUpdated');
} else {
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$error);
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: ' . $error);
return;
}

View File

@@ -1,16 +0,0 @@
<?php
namespace App\Livewire\Server\Proxy;
use App\Models\Server;
use Livewire\Component;
class Modal extends Component
{
public Server $server;
public function proxyStatusUpdated()
{
$this->dispatch('proxyStatusUpdated');
}
}

View File

@@ -15,7 +15,9 @@ class Resources extends Component
public $parameters = [];
public Collection $unmanagedContainers;
public Collection $containers;
public $activeTab = 'managed';
public function getListeners()
{
@@ -50,14 +52,29 @@ class Resources extends Component
public function refreshStatus()
{
$this->server->refresh();
$this->loadUnmanagedContainers();
if ($this->activeTab === 'managed') {
$this->loadManagedContainers();
} else {
$this->loadUnmanagedContainers();
}
$this->dispatch('success', 'Resource statuses refreshed.');
}
public function loadManagedContainers()
{
try {
$this->activeTab = 'managed';
$this->containers = $this->server->refresh()->definedResources();
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function loadUnmanagedContainers()
{
$this->activeTab = 'unmanaged';
try {
$this->unmanagedContainers = $this->server->loadUnmanagedContainers();
$this->containers = $this->server->loadUnmanagedContainers();
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -65,13 +82,14 @@ class Resources extends Component
public function mount()
{
$this->unmanagedContainers = collect();
$this->containers = collect();
$this->parameters = get_route_parameters();
try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) {
return redirect()->route('server.index');
}
$this->loadManagedContainers();
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -2,7 +2,6 @@
namespace App\Livewire\Server;
use App\Models\PrivateKey;
use App\Models\Server;
use Livewire\Component;
@@ -14,15 +13,29 @@ class ShowPrivateKey extends Component
public $parameters;
public function mount()
{
$this->parameters = get_route_parameters();
}
public function setPrivateKey($privateKeyId)
{
$originalPrivateKeyId = $this->server->getOriginal('private_key_id');
try {
$privateKey = PrivateKey::findOrFail($privateKeyId);
$this->server->update(['private_key_id' => $privateKey->id]);
$this->server->refresh();
$this->dispatch('success', 'Private key updated successfully.');
$this->server->update(['private_key_id' => $privateKeyId]);
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
if ($uptime) {
$this->dispatch('success', 'Private key updated successfully.');
} else {
throw new \Exception('Server is not reachable.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.<br><br>Error: '.$error);
}
} catch (\Exception $e) {
$this->server->update(['private_key_id' => $originalPrivateKeyId]);
$this->server->validateConnection();
$this->dispatch('error', 'Failed to update private key: '.$e->getMessage());
} finally {
$this->dispatch('refreshServerShow');
$this->server->refresh();
}
}
@@ -33,18 +46,16 @@ class ShowPrivateKey extends Component
if ($uptime) {
$this->dispatch('success', 'Server is reachable.');
} else {
ray($error);
$this->dispatch('error', 'Server is not reachable.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.<br><br>Error: '.$error);
return;
}
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->dispatch('refreshServerShow');
$this->server->refresh();
}
}
public function mount()
{
$this->parameters = get_route_parameters();
}
}

View File

@@ -7,6 +7,7 @@ use App\Enums\ProxyTypes;
use App\Jobs\PullSentinelImageJob;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
@@ -17,7 +18,6 @@ use OpenApi\Attributes as OA;
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
use Spatie\Url\Url;
use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
#[OA\Schema(
@@ -45,7 +45,7 @@ use Symfony\Component\Yaml\Yaml;
class Server extends BaseModel
{
use SchemalessAttributesTrait;
use SchemalessAttributesTrait,SoftDeletes;
public static $batch_counter = 0;
@@ -97,7 +97,8 @@ class Server extends BaseModel
}
}
});
static::deleting(function ($server) {
static::forceDeleting(function ($server) {
$server->destinations()->each(function ($destination) {
$destination->delete();
});
@@ -527,34 +528,6 @@ $schema://$host {
Storage::disk('ssh-mux')->delete($this->muxFilename());
}
public function generateSentinelUrl() {
if ($this->isLocalhost()) {
return 'http://host.docker.internal:8000';
}
$settings = InstanceSettings::get();
if ($settings->fqdn) {
return $settings->fqdn;
}
if ($settings->ipv4) {
return $settings->ipv4 . ':8000';
}
if ($settings->ipv6) {
return $settings->ipv6 . ':8000';
}
return null;
}
public function generateSentinelToken()
{
$data = [
'server_uuid' => $this->uuid,
];
$token = json_encode($data);
$encrypted = encrypt($token);
$this->settings->sentinel_token = $encrypted;
$this->settings->save();
return $encrypted;
}
public function sentinelHeartbeat(bool $isReset = false)
{
@@ -568,7 +541,7 @@ $schema://$host {
public function isSentinelEnabled()
{
return $this->isMetricsEnabled() || $this->isServerApiEnabled() || !$this->isBuildServer();
return ($this->isMetricsEnabled() || $this->isServerApiEnabled()) && !$this->isBuildServer();
}
public function isMetricsEnabled()

View File

@@ -59,10 +59,59 @@ class ServerSetting extends Model
protected static function booted()
{
static::creating(function ($setting) {
$setting->is_sentinel_enabled = true;
try {
if (str($setting->sentinel_token)->isEmpty()) {
$setting->generateSentinelToken(save: false);
}
if (str($setting->sentinel_custom_url)->isEmpty()) {
$url = $setting->generateSentinelUrl(save: false);
if (str($url)->isEmpty()) {
$setting->is_sentinel_enabled = false;
} else {
$setting->is_sentinel_enabled = true;
}
}
} catch (\Throwable $e) {
loggy('Error creating server setting: ' . $e->getMessage());
}
});
}
public function generateSentinelToken(bool $save = true)
{
$data = [
'server_uuid' => $this->server->uuid,
];
$token = json_encode($data);
$encrypted = encrypt($token);
$this->sentinel_token = $encrypted;
if ($save) {
$this->save();
}
return $encrypted;
}
public function generateSentinelUrl(bool $save = true)
{
$domain = null;
$settings = InstanceSettings::get();
if ($this->server->isLocalhost()) {
$domain = 'http://host.docker.internal:8000';
} else if ($settings->fqdn) {
$domain = $settings->fqdn;
} else if ($settings->ipv4) {
$domain = $settings->ipv4 . ':8000';
} else if ($settings->ipv6) {
$domain = $settings->ipv6 . ':8000';
}
$this->sentinel_custom_url = $domain;
if ($save) {
$this->save();
}
return $domain;
}
public function server()
{
return $this->belongsTo(Server::class);