From 96ca72fcdbbc728f8750301ba48bac12d282bd3b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 30 Oct 2024 14:54:27 +0100 Subject: [PATCH] refactor server view (phuuu) --- ...{InstallLogDrain.php => StartLogDrain.php} | 14 +- app/Actions/Server/StopLogDrain.php | 2 +- app/Jobs/PushServerUpdateJob.php | 4 +- app/Jobs/ServerCheckJob.php | 6 +- app/Jobs/ServerStorageCheckJob.php | 1 - app/Livewire/Server/Advanced.php | 96 ++-- app/Livewire/Server/Charts.php | 9 + app/Livewire/Server/CloudflareTunnels.php | 21 +- app/Livewire/Server/Delete.php | 12 +- app/Livewire/Server/Destination/Show.php | 76 ++- app/Livewire/Server/Form.php | 2 +- app/Livewire/Server/LogDrains.php | 197 +++----- app/Livewire/Server/PrivateKey/Show.php | 51 +- app/Livewire/Server/Show.php | 189 ++++++- app/Livewire/Server/ShowPrivateKey.php | 68 --- app/Models/Server.php | 7 +- app/Models/ServerSetting.php | 1 - app/View/Components/Server/Sidebar.php | 27 - bootstrap/helpers/shared.php | 4 +- .../views/components/forms/checkbox.blade.php | 6 +- .../components/modal-confirmation.blade.php | 11 + .../views/components/modal-input.blade.php | 3 + .../views/components/server/navbar.blade.php | 8 +- .../components/server/sidebar-proxy.blade.php | 16 + .../views/components/server/sidebar.blade.php | 41 +- .../views/livewire/server/advanced.blade.php | 143 +++--- .../views/livewire/server/charts.blade.php | 461 +++++++++--------- .../server/cloudflare-tunnels.blade.php | 88 ++-- .../views/livewire/server/delete.blade.php | 47 +- .../server/destination/show.blade.php | 46 +- .../livewire/server/log-drains.blade.php | 212 ++++---- .../server/private-key/show.blade.php | 41 +- .../proxy/dynamic-configurations.blade.php | 2 +- .../livewire/server/proxy/logs.blade.php | 2 +- .../livewire/server/proxy/show.blade.php | 2 +- .../server/show-private-key.blade.php | 36 -- .../views/livewire/server/show.blade.php | 292 ++++++++--- routes/web.php | 14 +- 38 files changed, 1365 insertions(+), 893 deletions(-) rename app/Actions/Server/{InstallLogDrain.php => StartLogDrain.php} (96%) delete mode 100644 app/Livewire/Server/ShowPrivateKey.php delete mode 100644 app/View/Components/Server/Sidebar.php create mode 100644 resources/views/components/server/sidebar-proxy.blade.php delete mode 100644 resources/views/livewire/server/show-private-key.blade.php diff --git a/app/Actions/Server/InstallLogDrain.php b/app/Actions/Server/StartLogDrain.php similarity index 96% rename from app/Actions/Server/InstallLogDrain.php rename to app/Actions/Server/StartLogDrain.php index 43376efe5..0e8036cd9 100644 --- a/app/Actions/Server/InstallLogDrain.php +++ b/app/Actions/Server/StartLogDrain.php @@ -5,7 +5,7 @@ namespace App\Actions\Server; use App\Models\Server; use Lorisleiva\Actions\Concerns\AsAction; -class InstallLogDrain +class StartLogDrain { use AsAction; @@ -13,12 +13,16 @@ class InstallLogDrain { if ($server->settings->is_logdrain_newrelic_enabled) { $type = 'newrelic'; + StopLogDrain::run($server); } elseif ($server->settings->is_logdrain_highlight_enabled) { $type = 'highlight'; + StopLogDrain::run($server); } elseif ($server->settings->is_logdrain_axiom_enabled) { $type = 'axiom'; + StopLogDrain::run($server); } elseif ($server->settings->is_logdrain_custom_enabled) { $type = 'custom'; + StopLogDrain::run($server); } else { $type = 'none'; } @@ -151,6 +155,8 @@ services: - ./parsers.conf:/parsers.conf ports: - 127.0.0.1:24224:24224 + labels: + - coolify.managed=true restart: unless-stopped '); $readme = base64_encode('# New Relic Log Drain @@ -202,15 +208,11 @@ Files: throw new \Exception('Unknown log drain type.'); } $restart_command = [ - "echo 'Stopping old Fluent Bit'", - "cd $config_path && docker compose down --remove-orphans || true", "echo 'Starting Fluent Bit'", - "cd $config_path && docker compose up -d --remove-orphans", + "cd $config_path && docker compose up -d", ]; $command = array_merge($command, $add_envs_command, $restart_command); - StopLogDrain::run($server); - return instant_remote_process($command, $server); } catch (\Throwable $e) { return handleError($e); diff --git a/app/Actions/Server/StopLogDrain.php b/app/Actions/Server/StopLogDrain.php index a5bce94a5..96c2466de 100644 --- a/app/Actions/Server/StopLogDrain.php +++ b/app/Actions/Server/StopLogDrain.php @@ -12,7 +12,7 @@ class StopLogDrain public function handle(Server $server) { try { - return instant_remote_process(['docker rm -f coolify-log-drain || true'], $server); + return instant_remote_process(['docker rm -f coolify-log-drain'], $server, false); } catch (\Throwable $e) { return handleError($e); } diff --git a/app/Jobs/PushServerUpdateJob.php b/app/Jobs/PushServerUpdateJob.php index c1db7001d..74ab8fa40 100644 --- a/app/Jobs/PushServerUpdateJob.php +++ b/app/Jobs/PushServerUpdateJob.php @@ -6,7 +6,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; use App\Actions\Proxy\CheckProxy; use App\Actions\Proxy\StartProxy; -use App\Actions\Server\InstallLogDrain; +use App\Actions\Server\StartLogDrain; use App\Actions\Shared\ComplexStatusCheck; use App\Models\Application; use App\Models\ApplicationPreview; @@ -362,7 +362,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue private function checkLogDrainContainer() { if ($this->server->isLogDrainEnabled() && $this->foundLogDrainContainer === false) { - InstallLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server); } } } diff --git a/app/Jobs/ServerCheckJob.php b/app/Jobs/ServerCheckJob.php index 727149181..553796e87 100644 --- a/app/Jobs/ServerCheckJob.php +++ b/app/Jobs/ServerCheckJob.php @@ -5,7 +5,7 @@ namespace App\Jobs; use App\Actions\Docker\GetContainersStatus; use App\Actions\Proxy\CheckProxy; use App\Actions\Proxy\StartProxy; -use App\Actions\Server\InstallLogDrain; +use App\Actions\Server\StartLogDrain; use App\Models\Server; use App\Notifications\Container\ContainerRestarted; use Illuminate\Bus\Queueable; @@ -109,10 +109,10 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue if ($foundLogDrainContainer) { $status = data_get($foundLogDrainContainer, 'State.Status'); if ($status !== 'running') { - InstallLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server); } } else { - InstallLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server); } } } diff --git a/app/Jobs/ServerStorageCheckJob.php b/app/Jobs/ServerStorageCheckJob.php index c646f77eb..32737cc47 100644 --- a/app/Jobs/ServerStorageCheckJob.php +++ b/app/Jobs/ServerStorageCheckJob.php @@ -38,7 +38,6 @@ class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue if (is_null($this->percentage)) { $this->percentage = $this->server->storageCheck(); - loggy('Server storage check percentage: '.$this->percentage); } if (! $this->percentage) { return 'No percentage could be retrieved.'; diff --git a/app/Livewire/Server/Advanced.php b/app/Livewire/Server/Advanced.php index 4d2d777d4..87b42cf3c 100644 --- a/app/Livewire/Server/Advanced.php +++ b/app/Livewire/Server/Advanced.php @@ -4,45 +4,82 @@ namespace App\Livewire\Server; use App\Jobs\DockerCleanupJob; use App\Models\Server; +use Livewire\Attributes\Rule; 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.server_disk_usage_notification_threshold' => 'required|integer|min:50|max:100', - 'server.settings.delete_unused_volumes' => 'boolean', - 'server.settings.delete_unused_networks' => 'boolean', - ]; + public array $parameters = []; - protected $validationAttributes = [ + #[Rule(['integer', 'min:1'])] + public int $concurrentBuilds = 1; - '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.server_disk_usage_notification_threshold' => 'Server Disk Usage Notification Threshold', - 'server.settings.delete_unused_volumes' => 'Delete Unused Volumes', - 'server.settings.delete_unused_networks' => 'Delete Unused Networks', - ]; + #[Rule(['integer', 'min:1'])] + public int $dynamicTimeout = 1; + + #[Rule('boolean')] + public bool $forceDockerCleanup = false; + + #[Rule('string')] + public string $dockerCleanupFrequency = '*/10 * * * *'; + + #[Rule(['integer', 'min:1', 'max:99'])] + public int $dockerCleanupThreshold = 10; + + #[Rule(['integer', 'min:1', 'max:99'])] + public int $serverDiskUsageNotificationThreshold = 50; + + #[Rule('boolean')] + public bool $deleteUnusedVolumes = false; + + #[Rule('boolean')] + public bool $deleteUnusedNetworks = false; + + public function mount(string $server_uuid) + { + try { + $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); + $this->parameters = get_route_parameters(); + $this->syncData(); + } catch (\Throwable $e) { + return redirect()->route('server.show'); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->server->settings->concurrent_builds = $this->concurrentBuilds; + $this->server->settings->dynamic_timeout = $this->dynamicTimeout; + $this->server->settings->force_docker_cleanup = $this->forceDockerCleanup; + $this->server->settings->docker_cleanup_frequency = $this->dockerCleanupFrequency; + $this->server->settings->docker_cleanup_threshold = $this->dockerCleanupThreshold; + $this->server->settings->server_disk_usage_notification_threshold = $this->serverDiskUsageNotificationThreshold; + $this->server->settings->delete_unused_volumes = $this->deleteUnusedVolumes; + $this->server->settings->delete_unused_networks = $this->deleteUnusedNetworks; + $this->server->settings->save(); + } else { + $this->concurrentBuilds = $this->server->settings->concurrent_builds; + $this->dynamicTimeout = $this->server->settings->dynamic_timeout; + $this->forceDockerCleanup = $this->server->settings->force_docker_cleanup; + $this->dockerCleanupFrequency = $this->server->settings->docker_cleanup_frequency; + $this->dockerCleanupThreshold = $this->server->settings->docker_cleanup_threshold; + $this->serverDiskUsageNotificationThreshold = $this->server->settings->server_disk_usage_notification_threshold; + $this->deleteUnusedVolumes = $this->server->settings->delete_unused_volumes; + $this->deleteUnusedNetworks = $this->server->settings->delete_unused_networks; + } + } public function instantSave() { try { - $this->validate(); - $this->server->settings->save(); + $this->syncData(true); $this->dispatch('success', 'Server updated.'); - $this->dispatch('refreshServerShow'); + // $this->dispatch('refreshServerShow'); } catch (\Throwable $e) { - $this->server->settings->refresh(); - return handleError($e, $this); } } @@ -60,12 +97,11 @@ class Advanced extends Component 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.'); + if (! validate_cron_expression($this->dockerCleanupFrequency)) { + $this->dockerCleanupFrequency = $this->server->settings->getOriginal('docker_cleanup_frequency'); + throw new \Exception('Invalid Cron / Human expression for Docker Cleanup Frequency.'); } - $this->server->settings->save(); + $this->syncData(true); $this->dispatch('success', 'Server updated.'); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Server/Charts.php b/app/Livewire/Server/Charts.php index 20f8dd9ed..d879de65d 100644 --- a/app/Livewire/Server/Charts.php +++ b/app/Livewire/Server/Charts.php @@ -19,6 +19,15 @@ class Charts extends Component public bool $poll = true; + public function mount(string $server_uuid) + { + try { + $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + public function pollData() { if ($this->poll || $this->interval <= 10) { diff --git a/app/Livewire/Server/CloudflareTunnels.php b/app/Livewire/Server/CloudflareTunnels.php index 82bc789db..f16962bca 100644 --- a/app/Livewire/Server/CloudflareTunnels.php +++ b/app/Livewire/Server/CloudflareTunnels.php @@ -3,27 +3,33 @@ namespace App\Livewire\Server; use App\Models\Server; +use Livewire\Attributes\Rule; use Livewire\Component; class CloudflareTunnels extends Component { public Server $server; - protected $rules = [ - 'server.settings.is_cloudflare_tunnel' => 'required|boolean', - ]; + #[Rule(['required', 'boolean'])] + public bool $isCloudflareTunnelsEnabled; - protected $validationAttributes = [ - 'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel', - ]; + public function mount(string $server_uuid) + { + try { + $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); + $this->isCloudflareTunnelsEnabled = $this->server->settings->is_cloudflare_tunnel; + } catch (\Throwable $e) { + return handleError($e, $this); + } + } public function instantSave() { try { $this->validate(); + $this->server->settings->is_cloudflare_tunnel = $this->isCloudflareTunnelsEnabled; $this->server->settings->save(); $this->dispatch('success', 'Server updated.'); - $this->dispatch('refreshServerShow'); } catch (\Throwable $e) { return handleError($e, $this); } @@ -31,6 +37,7 @@ class CloudflareTunnels extends Component public function manualCloudflareConfig() { + $this->isCloudflareTunnelsEnabled = true; $this->server->settings->is_cloudflare_tunnel = true; $this->server->settings->save(); $this->server->refresh(); diff --git a/app/Livewire/Server/Delete.php b/app/Livewire/Server/Delete.php index cc2b335e1..b9e3944b5 100644 --- a/app/Livewire/Server/Delete.php +++ b/app/Livewire/Server/Delete.php @@ -4,6 +4,7 @@ namespace App\Livewire\Server; use App\Actions\Server\DeleteServer; use App\Models\InstanceSettings; +use App\Models\Server; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; @@ -13,7 +14,16 @@ class Delete extends Component { use AuthorizesRequests; - public $server; + public Server $server; + + public function mount(string $server_uuid) + { + try { + $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } public function delete($password) { diff --git a/app/Livewire/Server/Destination/Show.php b/app/Livewire/Server/Destination/Show.php index 986e16cbf..71c051a74 100644 --- a/app/Livewire/Server/Destination/Show.php +++ b/app/Livewire/Server/Destination/Show.php @@ -3,27 +3,87 @@ namespace App\Livewire\Server\Destination; use App\Models\Server; +use App\Models\StandaloneDocker; +use App\Models\SwarmDocker; +use Illuminate\Support\Collection; use Livewire\Component; class Show extends Component { - public ?Server $server = null; + public Server $server; - public $parameters = []; + public Collection $networks; - public function mount() + public function mount(string $server_uuid) { - $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->networks = collect(); + $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); + loggy($this->server); } catch (\Throwable $e) { return handleError($e, $this); } } + private function createNetworkAndAttachToProxy() + { + $connectProxyToDockerNetworks = connectProxyToNetworks($this->server); + instant_remote_process($connectProxyToDockerNetworks, $this->server, false); + } + + public function add($name) + { + if ($this->server->isSwarm()) { + $found = $this->server->swarmDockers()->where('network', $name)->first(); + if ($found) { + $this->dispatch('error', 'Network already added to this server.'); + + return; + } else { + SwarmDocker::create([ + 'name' => $this->server->name.'-'.$name, + 'network' => $this->name, + 'server_id' => $this->server->id, + ]); + } + } else { + $found = $this->server->standaloneDockers()->where('network', $name)->first(); + if ($found) { + $this->dispatch('error', 'Network already added to this server.'); + + return; + } else { + StandaloneDocker::create([ + 'name' => $this->server->name.'-'.$name, + 'network' => $name, + 'server_id' => $this->server->id, + ]); + } + $this->createNetworkAndAttachToProxy(); + } + } + + public function scan() + { + if ($this->server->isSwarm()) { + $alreadyAddedNetworks = $this->server->swarmDockers; + } else { + $alreadyAddedNetworks = $this->server->standaloneDockers; + } + $networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false); + $this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) { + return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none'; + })->filter(function ($network) use ($alreadyAddedNetworks) { + return ! $alreadyAddedNetworks->contains('network', $network['Name']); + }); + if ($this->networks->count() === 0) { + $this->dispatch('success', 'No new destinations found on this server.'); + + return; + } + $this->dispatch('success', 'Scan done.'); + } + public function render() { return view('livewire.server.destination.show'); diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php index bfe2853c0..697c7b76f 100644 --- a/app/Livewire/Server/Form.php +++ b/app/Livewire/Server/Form.php @@ -111,7 +111,7 @@ class Form extends Component { if ($field === 'server.settings.docker_cleanup_frequency') { $frequency = $this->server->settings->docker_cleanup_frequency; - if (empty($frequency) || ! validate_cron_expression($frequency)) { + if (! validate_cron_expression($frequency)) { $this->dispatch('error', 'Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.'); $this->server->settings->docker_cleanup_frequency = '*/10 * * * *'; } diff --git a/app/Livewire/Server/LogDrains.php b/app/Livewire/Server/LogDrains.php index 6e09eecdd..2bc24eff8 100644 --- a/app/Livewire/Server/LogDrains.php +++ b/app/Livewire/Server/LogDrains.php @@ -2,84 +2,96 @@ namespace App\Livewire\Server; -use App\Actions\Server\InstallLogDrain; +use App\Actions\Server\StartLogDrain; use App\Actions\Server\StopLogDrain; use App\Models\Server; +use Livewire\Attributes\Rule; use Livewire\Component; class LogDrains extends Component { public Server $server; - public $parameters = []; + #[Rule(['boolean'])] + public bool $isLogDrainNewRelicEnabled = false; - protected $rules = [ - 'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean', - 'server.settings.logdrain_newrelic_license_key' => 'required|string', - 'server.settings.logdrain_newrelic_base_uri' => 'required|string', - 'server.settings.is_logdrain_highlight_enabled' => 'required|boolean', - 'server.settings.logdrain_highlight_project_id' => 'required|string', - 'server.settings.is_logdrain_axiom_enabled' => 'required|boolean', - 'server.settings.logdrain_axiom_dataset_name' => 'required|string', - 'server.settings.logdrain_axiom_api_key' => 'required|string', - 'server.settings.is_logdrain_custom_enabled' => 'required|boolean', - 'server.settings.logdrain_custom_config' => 'required|string', - 'server.settings.logdrain_custom_config_parser' => 'nullable', - ]; + #[Rule(['boolean'])] + public bool $isLogDrainCustomEnabled = false; - protected $validationAttributes = [ - 'server.settings.is_logdrain_newrelic_enabled' => 'New Relic log drain', - 'server.settings.logdrain_newrelic_license_key' => 'New Relic license key', - 'server.settings.logdrain_newrelic_base_uri' => 'New Relic base URI', - 'server.settings.is_logdrain_highlight_enabled' => 'Highlight log drain', - 'server.settings.logdrain_highlight_project_id' => 'Highlight project ID', - 'server.settings.is_logdrain_axiom_enabled' => 'Axiom log drain', - 'server.settings.logdrain_axiom_dataset_name' => 'Axiom dataset name', - 'server.settings.logdrain_axiom_api_key' => 'Axiom API key', - 'server.settings.is_logdrain_custom_enabled' => 'Custom log drain', - 'server.settings.logdrain_custom_config' => 'Custom log drain configuration', - 'server.settings.logdrain_custom_config_parser' => 'Custom log drain configuration parser', - ]; + #[Rule(['boolean'])] + public bool $isLogDrainAxiomEnabled = false; - public function mount() + #[Rule(['string', 'nullable'])] + public ?string $logDrainNewRelicLicenseKey = null; + + #[Rule(['url', 'nullable'])] + public ?string $logDrainNewRelicBaseUri = null; + + #[Rule(['string', 'nullable'])] + public ?string $logDrainAxiomDatasetName = null; + + #[Rule(['string', 'nullable'])] + public ?string $logDrainAxiomApiKey = null; + + #[Rule(['string', 'nullable'])] + public ?string $logDrainCustomConfig = null; + + #[Rule(['string', 'nullable'])] + public ?string $logDrainCustomConfigParser = null; + + public function mount(string $server_uuid) { - $this->parameters = get_route_parameters(); try { - $server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first(); - if (is_null($server)) { - return redirect()->route('server.index'); - } - $this->server = $server; + $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); + $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); } } - public function configureLogDrain() + public function syncData(bool $toModel = false) + { + + if ($toModel) { + $this->validate(); + $this->server->settings->is_logdrain_newrelic_enabled = $this->isLogDrainNewRelicEnabled; + $this->server->settings->is_logdrain_axiom_enabled = $this->isLogDrainAxiomEnabled; + $this->server->settings->is_logdrain_custom_enabled = $this->isLogDrainCustomEnabled; + + $this->server->settings->logdrain_newrelic_license_key = $this->logDrainNewRelicLicenseKey; + $this->server->settings->logdrain_newrelic_base_uri = $this->logDrainNewRelicBaseUri; + $this->server->settings->logdrain_axiom_dataset_name = $this->logDrainAxiomDatasetName; + $this->server->settings->logdrain_axiom_api_key = $this->logDrainAxiomApiKey; + $this->server->settings->logdrain_custom_config = $this->logDrainCustomConfig; + $this->server->settings->logdrain_custom_config_parser = $this->logDrainCustomConfigParser; + + $this->server->settings->save(); + } else { + $this->isLogDrainNewRelicEnabled = $this->server->settings->is_logdrain_newrelic_enabled; + $this->isLogDrainAxiomEnabled = $this->server->settings->is_logdrain_axiom_enabled; + $this->isLogDrainCustomEnabled = $this->server->settings->is_logdrain_custom_enabled; + + $this->logDrainNewRelicLicenseKey = $this->server->settings->logdrain_newrelic_license_key; + $this->logDrainNewRelicBaseUri = $this->server->settings->logdrain_newrelic_base_uri; + $this->logDrainAxiomDatasetName = $this->server->settings->logdrain_axiom_dataset_name; + $this->logDrainAxiomApiKey = $this->server->settings->logdrain_axiom_api_key; + $this->logDrainCustomConfig = $this->server->settings->logdrain_custom_config; + $this->logDrainCustomConfigParser = $this->server->settings->logdrain_custom_config_parser; + } + + } + + public function instantSave() { try { - InstallLogDrain::run($this->server); - if (! $this->server->isLogDrainEnabled()) { - $this->dispatch('serverRefresh'); + $this->syncData(true); + if ($this->server->isLogDrainEnabled()) { + StartLogDrain::run($this->server); + $this->dispatch('success', 'Log drain service started.'); + } else { + StopLogDrain::run($this->server); $this->dispatch('success', 'Log drain service stopped.'); - - return; } - $this->dispatch('serverRefresh'); - $this->dispatch('success', 'Log drain service started.'); - } catch (\Throwable $e) { - return handleError($e, $this); - } - } - - public function instantSave(string $type) - { - try { - $ok = $this->submit($type); - if (! $ok) { - return; - } - $this->configureLogDrain(); } catch (\Throwable $e) { return handleError($e, $this); } @@ -88,79 +100,10 @@ class LogDrains extends Component public function submit(string $type) { try { - $this->resetErrorBag(); - if ($type === 'newrelic') { - $this->validate([ - 'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean', - 'server.settings.logdrain_newrelic_license_key' => 'required|string', - 'server.settings.logdrain_newrelic_base_uri' => 'required|string', - ]); - $this->server->settings->update([ - 'is_logdrain_highlight_enabled' => false, - 'is_logdrain_axiom_enabled' => false, - 'is_logdrain_custom_enabled' => false, - ]); - } elseif ($type === 'highlight') { - $this->validate([ - 'server.settings.is_logdrain_highlight_enabled' => 'required|boolean', - 'server.settings.logdrain_highlight_project_id' => 'required|string', - ]); - $this->server->settings->update([ - 'is_logdrain_newrelic_enabled' => false, - 'is_logdrain_axiom_enabled' => false, - 'is_logdrain_custom_enabled' => false, - ]); - } elseif ($type === 'axiom') { - $this->validate([ - 'server.settings.is_logdrain_axiom_enabled' => 'required|boolean', - 'server.settings.logdrain_axiom_dataset_name' => 'required|string', - 'server.settings.logdrain_axiom_api_key' => 'required|string', - ]); - $this->server->settings->update([ - 'is_logdrain_newrelic_enabled' => false, - 'is_logdrain_highlight_enabled' => false, - 'is_logdrain_custom_enabled' => false, - ]); - } elseif ($type === 'custom') { - $this->validate([ - 'server.settings.is_logdrain_custom_enabled' => 'required|boolean', - 'server.settings.logdrain_custom_config' => 'required|string', - 'server.settings.logdrain_custom_config_parser' => 'nullable', - ]); - $this->server->settings->update([ - 'is_logdrain_newrelic_enabled' => false, - 'is_logdrain_highlight_enabled' => false, - 'is_logdrain_axiom_enabled' => false, - ]); - } - if (! $this->server->isLogDrainEnabled()) { - StopLogDrain::dispatch($this->server); - } - $this->server->settings->save(); + $this->syncData(true); $this->dispatch('success', 'Settings saved.'); - - return true; } catch (\Throwable $e) { - if ($type === 'newrelic') { - $this->server->settings->update([ - 'is_logdrain_newrelic_enabled' => false, - ]); - } elseif ($type === 'highlight') { - $this->server->settings->update([ - 'is_logdrain_highlight_enabled' => false, - ]); - } elseif ($type === 'axiom') { - $this->server->settings->update([ - 'is_logdrain_axiom_enabled' => false, - ]); - } elseif ($type === 'custom') { - $this->server->settings->update([ - 'is_logdrain_custom_enabled' => false, - ]); - } - handleError($e, $this); - - return false; + return handleError($e, $this); } } diff --git a/app/Livewire/Server/PrivateKey/Show.php b/app/Livewire/Server/PrivateKey/Show.php index 0ad820428..64aa1884b 100644 --- a/app/Livewire/Server/PrivateKey/Show.php +++ b/app/Livewire/Server/PrivateKey/Show.php @@ -8,26 +8,63 @@ use Livewire\Component; class Show extends Component { - public ?Server $server = null; + public Server $server; public $privateKeys = []; public $parameters = []; - public function mount() + public function mount(string $server_uuid) { - $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->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); $this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false); } catch (\Throwable $e) { return handleError($e, $this); } } + public function setPrivateKey($privateKeyId) + { + $ownedPrivateKey = PrivateKey::ownedByCurrentTeam()->find($privateKeyId); + if (is_null($ownedPrivateKey)) { + $this->dispatch('error', 'You are not allowed to use this private key.'); + + return; + } + + $originalPrivateKeyId = $this->server->getOriginal('private_key_id'); + try { + $this->server->update(['private_key_id' => $privateKeyId]); + ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(justCheckingNewKey: true); + if ($uptime) { + $this->dispatch('success', 'Private key updated successfully.'); + } else { + throw new \Exception($error); + } + } catch (\Exception $e) { + $this->server->update(['private_key_id' => $originalPrivateKeyId]); + $this->server->validateConnection(); + $this->dispatch('error', $e->getMessage()); + } + } + + public function checkConnection() + { + try { + ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(); + if ($uptime) { + $this->dispatch('success', 'Server is reachable.'); + } else { + $this->dispatch('error', 'Server is not reachable.

Check this documentation for further help.

Error: '.$error); + + return; + } + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + public function render() { return view('livewire.server.private-key.show'); diff --git a/app/Livewire/Server/Show.php b/app/Livewire/Server/Show.php index 85c5f95f8..afc7682a4 100644 --- a/app/Livewire/Server/Show.php +++ b/app/Livewire/Server/Show.php @@ -3,38 +3,203 @@ namespace App\Livewire\Server; use App\Models\Server; -use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Livewire\Attributes\Rule; use Livewire\Component; class Show extends Component { - use AuthorizesRequests; - public Server $server; - public array $parameters; + #[Rule(['required'])] + public string $name; - protected $listeners = ['refreshServerShow']; + #[Rule(['nullable'])] + public ?string $description; - public function mount() + #[Rule(['required'])] + public string $ip; + + #[Rule(['required'])] + public string $user; + + #[Rule(['required'])] + public string $port; + + #[Rule(['nullable'])] + public ?string $validationLogs = null; + + #[Rule(['nullable', 'url'])] + public ?string $wildcardDomain; + + #[Rule(['required'])] + public bool $isReachable; + + #[Rule(['required'])] + public bool $isUsable; + + #[Rule(['required'])] + public bool $isSwarmManager; + + #[Rule(['required'])] + public bool $isSwarmWorker; + + #[Rule(['required'])] + public bool $isBuildServer; + + #[Rule(['required'])] + public bool $isMetricsEnabled; + + #[Rule(['required'])] + public string $sentinelToken; + + #[Rule(['nullable'])] + public ?string $sentinelUpdatedAt; + + #[Rule(['required', 'integer', 'min:1'])] + public int $sentinelMetricsRefreshRateSeconds; + + #[Rule(['required', 'integer', 'min:1'])] + public int $sentinelMetricsHistoryDays; + + #[Rule(['required', 'integer', 'min:10'])] + public int $sentinelPushIntervalSeconds; + + #[Rule(['nullable', 'url'])] + public ?string $sentinelCustomUrl; + + #[Rule(['required'])] + public bool $isSentinelEnabled; + + #[Rule(['required'])] + public bool $isSentinelDebugEnabled; + + #[Rule(['required'])] + public string $serverTimezone; + + public array $timezones; + + public function getListeners() + { + $teamId = auth()->user()->currentTeam()->id; + + return [ + "echo-private:team.{$teamId},CloudflareTunnelConfigured" => '$refresh', + 'refreshServerShow' => '$refresh', + ]; + } + + public function mount(string $server_uuid) { try { - $this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->firstOrFail(); - $this->parameters = get_route_parameters(); + $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); + $this->timezones = collect(timezone_identifiers_list())->sort()->values()->toArray(); + $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); } } - public function refreshServerShow() + public function syncData(bool $toModel = false) { - $this->server->refresh(); - $this->dispatch('$refresh'); + if ($toModel) { + $this->validate(); + $this->server->name = $this->name; + $this->server->description = $this->description; + $this->server->ip = $this->ip; + $this->server->user = $this->user; + $this->server->port = $this->port; + $this->server->validation_logs = $this->validationLogs; + $this->server->save(); + + $this->server->settings->is_swarm_manager = $this->isSwarmManager; + $this->server->settings->is_swarm_worker = $this->isSwarmWorker; + $this->server->settings->is_build_server = $this->isBuildServer; + $this->server->settings->is_metrics_enabled = $this->isMetricsEnabled; + $this->server->settings->sentinel_token = $this->sentinelToken; + $this->server->settings->sentinel_metrics_refresh_rate_seconds = $this->sentinelMetricsRefreshRateSeconds; + $this->server->settings->sentinel_metrics_history_days = $this->sentinelMetricsHistoryDays; + $this->server->settings->sentinel_push_interval_seconds = $this->sentinelPushIntervalSeconds; + $this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl; + $this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled; + $this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled; + $this->server->settings->server_timezone = $this->serverTimezone; + $this->server->settings->save(); + } else { + $this->name = $this->server->name; + $this->description = $this->server->description; + $this->ip = $this->server->ip; + $this->user = $this->server->user; + $this->port = $this->server->port; + $this->wildcardDomain = $this->server->settings->wildcard_domain; + $this->isReachable = $this->server->settings->is_reachable; + $this->isUsable = $this->server->settings->is_usable; + $this->isSwarmManager = $this->server->settings->is_swarm_manager; + $this->isSwarmWorker = $this->server->settings->is_swarm_worker; + $this->isBuildServer = $this->server->settings->is_build_server; + $this->isMetricsEnabled = $this->server->settings->is_metrics_enabled; + $this->sentinelToken = $this->server->settings->sentinel_token; + $this->sentinelMetricsRefreshRateSeconds = $this->server->settings->sentinel_metrics_refresh_rate_seconds; + $this->sentinelMetricsHistoryDays = $this->server->settings->sentinel_metrics_history_days; + $this->sentinelPushIntervalSeconds = $this->server->settings->sentinel_push_interval_seconds; + $this->sentinelCustomUrl = $this->server->settings->sentinel_custom_url; + $this->isSentinelEnabled = $this->server->settings->is_sentinel_enabled; + $this->isSentinelDebugEnabled = $this->server->settings->is_sentinel_debug_enabled; + $this->sentinelUpdatedAt = $this->server->settings->updated_at; + $this->serverTimezone = $this->server->settings->server_timezone; + } + } + + public function validateServer($install = true) + { + try { + $this->validationLogs = $this->server->validation_logs = null; + $this->server->save(); + $this->dispatch('init', $install); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + public function checkLocalhostConnection() + { + $this->syncData(true); + ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(); + if ($uptime) { + $this->dispatch('success', 'Server is reachable.'); + $this->server->settings->is_reachable = $this->isReachable = true; + $this->server->settings->is_usable = $this->isUsable = true; + $this->server->settings->save(); + $this->dispatch('proxyStatusUpdated'); + } else { + $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.

Check this documentation for further help.

Error: '.$error); + + return; + } + } + + public function regenerateSentinelToken() + { + try { + $this->server->settings->generateSentinelToken(); + $this->dispatch('success', 'Token regenerated & Sentinel restarted.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + public function instantSave() + { + $this->submit(); } public function submit() { - $this->dispatch('serverRefresh', false); + try { + $this->syncData(true); + $this->dispatch('success', 'Server updated'); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function render() diff --git a/app/Livewire/Server/ShowPrivateKey.php b/app/Livewire/Server/ShowPrivateKey.php deleted file mode 100644 index eaa71c70b..000000000 --- a/app/Livewire/Server/ShowPrivateKey.php +++ /dev/null @@ -1,68 +0,0 @@ -parameters = get_route_parameters(); - } - - public function setPrivateKey($privateKeyId) - { - $ownedPrivateKey = PrivateKey::ownedByCurrentTeam()->find($privateKeyId); - if (is_null($ownedPrivateKey)) { - $this->dispatch('error', 'You are not allowed to use this private key.'); - - return; - } - - $originalPrivateKeyId = $this->server->getOriginal('private_key_id'); - try { - $this->server->update(['private_key_id' => $privateKeyId]); - ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(justCheckingNewKey: true); - if ($uptime) { - $this->dispatch('success', 'Private key updated successfully.'); - } else { - throw new \Exception($error); - } - } catch (\Exception $e) { - $this->server->update(['private_key_id' => $originalPrivateKeyId]); - $this->server->validateConnection(); - $this->dispatch('error', $e->getMessage()); - } finally { - $this->dispatch('refreshServerShow'); - $this->server->refresh(); - } - } - - public function checkConnection() - { - try { - ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(); - if ($uptime) { - $this->dispatch('success', 'Server is reachable.'); - } else { - $this->dispatch('error', 'Server is not reachable.

Check this documentation for further help.

Error: '.$error); - - return; - } - } catch (\Throwable $e) { - return handleError($e, $this); - } finally { - $this->dispatch('refreshServerShow'); - $this->server->refresh(); - } - } -} diff --git a/app/Models/Server.php b/app/Models/Server.php index f1792c5e5..bd09f1b85 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -63,7 +63,11 @@ class Server extends BaseModel $payload['ip'] = str($server->ip)->trim(); } $server->forceFill($payload); - + }); + static::saved(function ($server) { + if ($server->privateKey->isDirty()) { + refresh_server_connection($server->privateKey); + } }); static::created(function ($server) { ServerSetting::create([ @@ -1027,7 +1031,6 @@ $schema://$host { $this->refresh(); $unreachableNotificationSent = (bool) $this->unreachable_notification_sent; $isReachable = (bool) $this->settings->is_reachable; - loggy('Server setting is_reachable changed to '.$isReachable.' for server '.$this->id.'. Unreachable notification sent: '.$unreachableNotificationSent); // If the server is reachable, send the reachable notification if it was sent before if ($isReachable === true) { if ($unreachableNotificationSent === true) { diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php index 7a8e7b8ed..bca16536e 100644 --- a/app/Models/ServerSetting.php +++ b/app/Models/ServerSetting.php @@ -117,7 +117,6 @@ class ServerSetting extends Model $domain = 'http://'.$settings->public_ipv6.':8000'; } $this->sentinel_custom_url = $domain; - loggy('Sentinel URL: '.$domain); if ($save) { $this->save(); } diff --git a/app/View/Components/Server/Sidebar.php b/app/View/Components/Server/Sidebar.php deleted file mode 100644 index f968b6d0c..000000000 --- a/app/View/Components/Server/Sidebar.php +++ /dev/null @@ -1,27 +0,0 @@ -dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds."); @@ -370,6 +369,9 @@ function translate_cron_expression($expression_to_validate): string } function validate_cron_expression($expression_to_validate): bool { + if (empty($expression_to_validate)) { + return false; + } $isValid = false; $expression = new CronExpression($expression_to_validate); $isValid = $expression->isValid(); diff --git a/resources/views/components/forms/checkbox.blade.php b/resources/views/components/forms/checkbox.blade.php index fed6ad77f..f5eb5778a 100644 --- a/resources/views/components/forms/checkbox.blade.php +++ b/resources/views/components/forms/checkbox.blade.php @@ -14,10 +14,7 @@ 'w-full' => $fullWidth, ])> @if (!$hideLabel) -