diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index ad663de8b..a24ac6b29 100644 --- a/app/Actions/Server/CleanupDocker.php +++ b/app/Actions/Server/CleanupDocker.php @@ -12,29 +12,21 @@ class CleanupDocker public function handle(Server $server) { - $commands = $this->getCommands($force); + $commands = $this->getCommands(); foreach ($commands as $command) { instant_remote_process([$command], $server, false); } } - private function getCommands(bool $force): array + private function getCommands(): array { $commonCommands = [ 'docker container prune -f --filter "label=coolify.managed=true"', - 'docker image prune -f', - 'docker builder prune -f', + 'docker image prune -af', + 'docker builder prune -af', ]; - if ($force) { - return array_merge([ - 'docker container prune -f --filter "label=coolify.managed=true"', - 'docker image prune -af', - 'docker builder prune -af', - ], $commonCommands); - } - return $commonCommands; } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 12b514c6a..fd4c508a0 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -106,7 +106,11 @@ class Kernel extends ConsoleKernel //The lines below need to be added as soon as timzone is merged!! //$serverTimezone = $server->settings->server_timezone; //$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer(); - $schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->onOneServer(); + if ($server->settings->force_docker_cleanup) { + $schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->onOneServer(); + } else { + $schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer(); + } } } @@ -118,7 +122,7 @@ class Kernel extends ConsoleKernel return; } foreach ($scheduled_backups as $scheduled_backup) { - if (!$scheduled_backup->enabled) { + if (! $scheduled_backup->enabled) { continue; } if (is_null(data_get($scheduled_backup, 'database'))) { @@ -150,7 +154,7 @@ class Kernel extends ConsoleKernel $service = $scheduled_task->service; $application = $scheduled_task->application; - if (!$application && !$service) { + if (! $application && ! $service) { ray('application/service attached to scheduled task does not exist'); $scheduled_task->delete(); @@ -177,7 +181,7 @@ class Kernel extends ConsoleKernel protected function commands(): void { - $this->load(__DIR__ . '/Commands'); + $this->load(__DIR__.'/Commands'); require base_path('routes/console.php'); } diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index e53b54f5b..f95cd2920 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; @@ -17,14 +18,24 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public $timeout = 300; + public $timeout = 600; - public $tries = 2; + public $tries = 1; public ?string $usageBefore = null; public function __construct(public Server $server) {} + public function middleware(): array + { + return [new WithoutOverlapping($this->server->id)]; + } + + public function uniqueId(): int + { + return $this->server->id; + } + public function handle(): void { try { @@ -32,34 +43,30 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue return; } if ($this->server->settings->force_docker_cleanup) { - Log::info('DockerCleanupJob force cleanup on ' . $this->server->name); - CleanupDocker::run(server: $this->server, force: true); + Log::info('DockerCleanupJob force cleanup on '.$this->server->name); + CleanupDocker::run(server: $this->server); return; } - - return; - } - try { $this->usageBefore = $this->server->getDiskUsage(); if (str($this->usageBefore)->isEmpty() || $this->usageBefore === null || $this->usageBefore === 0) { - Log::info('DockerCleanupJob force cleanup on ' . $this->server->name); - CleanupDocker::run(server: $this->server, force: true); + Log::info('DockerCleanupJob force cleanup on '.$this->server->name); + CleanupDocker::run(server: $this->server); return; } - if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) { + if ($this->usageBefore >= $this->server->settings->docker_cleanup_threshold) { CleanupDocker::run(server: $this->server); $usageAfter = $this->server->getDiskUsage(); if ($usageAfter < $this->usageBefore) { - $this->server->team?->notify(new DockerCleanup($this->server, 'Saved ' . ($this->usageBefore - $usageAfter) . '% disk space.')); - Log::info('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name); + $this->server->team?->notify(new DockerCleanup($this->server, 'Saved '.($this->usageBefore - $usageAfter).'% disk space.')); + Log::info('DockerCleanupJob done: Saved '.($this->usageBefore - $usageAfter).'% disk space on '.$this->server->name); } else { - Log::info('DockerCleanupJob failed to save disk space on ' . $this->server->name); + Log::info('DockerCleanupJob failed to save disk space on '.$this->server->name); } } else { - Log::info('No need to clean up ' . $this->server->name); + Log::info('No need to clean up '.$this->server->name); } } catch (\Throwable $e) { CleanupDocker::run(server: $this->server); @@ -67,4 +74,4 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue throw $e; } } -} \ No newline at end of file +} diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php index 7c176c952..1ab70c004 100644 --- a/app/Livewire/Server/Form.php +++ b/app/Livewire/Server/Form.php @@ -18,8 +18,6 @@ class Form extends Component public ?string $wildcard_domain = null; - public int $cleanup_after_percentage; - public bool $dockerInstallationStarted = false; public bool $revalidate = false; @@ -81,7 +79,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 (empty($frequency) || ! 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 * * * *'; } @@ -169,7 +167,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.

Check this documentation for further help.

Error: ' . $error); + $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.

Check this documentation for further help.

Error: '.$error); return; } @@ -185,31 +183,35 @@ class Form extends Component public function submit() { - if (isCloud() && !isDev()) { - $this->validate(); - $this->validate([ - 'server.ip' => 'required', - ]); - } else { - $this->validate(); - } - $uniqueIPs = Server::all()->reject(function (Server $server) { - return $server->id === $this->server->id; - })->pluck('ip')->toArray(); - if (in_array($this->server->ip, $uniqueIPs)) { - $this->dispatch('error', 'IP address is already in use by another team.'); + try { + if (isCloud() && ! isDev()) { + $this->validate(); + $this->validate([ + 'server.ip' => 'required', + ]); + } else { + $this->validate(); + } + $uniqueIPs = Server::all()->reject(function (Server $server) { + return $server->id === $this->server->id; + })->pluck('ip')->toArray(); + if (in_array($this->server->ip, $uniqueIPs)) { + $this->dispatch('error', 'IP address is already in use by another team.'); - return; + return; + } + refresh_server_connection($this->server->privateKey); + $this->server->settings->wildcard_domain = $this->wildcard_domain; + if ($this->server->settings->force_docker_cleanup) { + $this->server->settings->docker_cleanup_frequency = $this->server->settings->docker_cleanup_frequency; + } else { + $this->server->settings->docker_cleanup_threshold = $this->server->settings->docker_cleanup_threshold; + } + $this->server->settings->save(); + $this->server->save(); + $this->dispatch('success', 'Server updated.'); + } catch (\Throwable $e) { + return handleError($e, $this); } - refresh_server_connection($this->server->privateKey); - $this->server->settings->wildcard_domain = $this->wildcard_domain; - if ($this->server->settings->force_docker_cleanup) { - $this->server->settings->docker_cleanup_frequency = $this->server->settings->docker_cleanup_frequency; - } else { - $this->server->settings->docker_cleanup_threshold = $this->server->settings->docker_cleanup_threshold; - } - $this->server->settings->save(); - $this->server->save(); - $this->dispatch('success', 'Server updated.'); } -} \ No newline at end of file +} diff --git a/app/Models/Service.php b/app/Models/Service.php index 091516144..3d19b067b 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -2,13 +2,11 @@ namespace App\Models; -use App\Enums\ProxyTypes; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; -use Illuminate\Support\Str; use OpenApi\Attributes as OA; use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; @@ -25,7 +23,7 @@ use Symfony\Component\Yaml\Yaml; 'description' => ['type' => 'string', 'description' => 'The description of the service.'], 'docker_compose_raw' => ['type' => 'string', 'description' => 'The raw docker-compose.yml file of the service.'], 'docker_compose' => ['type' => 'string', 'description' => 'The docker-compose.yml file that is parsed and modified by Coolify.'], - 'destination_type' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'], + 'destination_type' => ['type' => 'string', 'description' => 'Destination type.'], 'destination_id' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'], 'connect_to_docker_network' => ['type' => 'boolean', 'description' => 'The flag to connect the service to the predefined Docker network.'], 'is_container_label_escape_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label escape.'], diff --git a/app/Notifications/Server/DockerCleanup.php b/app/Notifications/Server/DockerCleanup.php index f8195ec1d..682ed7a1a 100644 --- a/app/Notifications/Server/DockerCleanup.php +++ b/app/Notifications/Server/DockerCleanup.php @@ -44,7 +44,7 @@ class DockerCleanup extends Notification implements ShouldQueue // $mail->view('emails.high-disk-usage', [ // 'name' => $this->server->name, // 'disk_usage' => $this->disk_usage, - // 'threshold' => $this->cleanup_after_percentage, + // 'threshold' => $this->docker_cleanup_threshold, // ]); // return $mail; // } diff --git a/app/Notifications/Server/HighDiskUsage.php b/app/Notifications/Server/HighDiskUsage.php index 3c68afe7b..34cb22091 100644 --- a/app/Notifications/Server/HighDiskUsage.php +++ b/app/Notifications/Server/HighDiskUsage.php @@ -17,7 +17,7 @@ class HighDiskUsage extends Notification implements ShouldQueue public $tries = 1; - public function __construct(public Server $server, public int $disk_usage, public int $cleanup_after_percentage) {} + public function __construct(public Server $server, public int $disk_usage, public int $docker_cleanup_threshold) {} public function via(object $notifiable): array { @@ -46,7 +46,7 @@ class HighDiskUsage extends Notification implements ShouldQueue $mail->view('emails.high-disk-usage', [ 'name' => $this->server->name, 'disk_usage' => $this->disk_usage, - 'threshold' => $this->cleanup_after_percentage, + 'threshold' => $this->docker_cleanup_threshold, ]); return $mail; @@ -54,7 +54,7 @@ class HighDiskUsage extends Notification implements ShouldQueue public function toDiscord(): string { - $message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup."; + $message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->docker_cleanup_threshold}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup."; return $message; } @@ -62,7 +62,7 @@ class HighDiskUsage extends Notification implements ShouldQueue public function toTelegram(): array { return [ - 'message' => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.", + 'message' => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->docker_cleanup_threshold}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.", ]; } } diff --git a/database/migrations/2024_08_09_215659_add_server_cleanup_fields_to_server_settings_table.php b/database/migrations/2024_08_09_215659_add_server_cleanup_fields_to_server_settings_table.php index 4e1bfb357..b5300c905 100644 --- a/database/migrations/2024_08_09_215659_add_server_cleanup_fields_to_server_settings_table.php +++ b/database/migrations/2024_08_09_215659_add_server_cleanup_fields_to_server_settings_table.php @@ -1,5 +1,6 @@ boolean('force_docker_cleanup')->default(false); $table->string('docker_cleanup_frequency')->default('*/10 * * * *'); $table->integer('docker_cleanup_threshold')->default(80); - // Remove old columns $table->dropColumn('cleanup_after_percentage'); $table->dropColumn('is_force_cleanup_enabled'); }); + foreach ($serverSettings as $serverSetting) { + $serverSetting->force_docker_cleanup = $serverSetting->is_force_cleanup_enabled; + $serverSetting->docker_cleanup_threshold = $serverSetting->cleanup_after_percentage; + $serverSetting->save(); + } + } /** @@ -32,15 +40,21 @@ class AddServerCleanupFieldsToServerSettingsTable extends Migration */ public function down() { + $serverSettings = ServerSetting::all(); Schema::table('server_settings', function (Blueprint $table) { $table->dropColumn('force_docker_cleanup'); $table->dropColumn('docker_cleanup_frequency'); $table->dropColumn('docker_cleanup_threshold'); - // Add back old columns $table->integer('cleanup_after_percentage')->default(80); $table->boolean('force_server_cleanup')->default(false); + $table->boolean('is_force_cleanup_enabled')->default(false); }); + foreach ($serverSettings as $serverSetting) { + $serverSetting->is_force_cleanup_enabled = $serverSetting->force_docker_cleanup; + $serverSetting->cleanup_after_percentage = $serverSetting->docker_cleanup_threshold; + $serverSetting->save(); + } } } diff --git a/openapi.yaml b/openapi.yaml index 9f6bc3889..482ff6f05 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -4523,14 +4523,14 @@ components: properties: id: type: integer - cleanup_after_percentage: - type: integer concurrent_builds: type: integer dynamic_timeout: type: integer force_disabled: type: boolean + force_server_cleanup: + type: boolean is_build_server: type: boolean is_cloudflare_tunnel: @@ -4577,6 +4577,10 @@ components: type: integer metrics_token: type: string + docker_cleanup_frequency: + type: string + docker_cleanup_threshold: + type: integer server_id: type: integer wildcard_domain: @@ -4613,6 +4617,9 @@ components: docker_compose: type: string description: 'The docker-compose.yml file that is parsed and modified by Coolify.' + destination_type: + type: string + description: 'Destination type.' destination_id: type: integer description: 'The unique identifier of the destination where the service is running.' diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index ee8855321..474521c9c 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -3,68 +3,73 @@

General

@if ($server->id === 0) - - You could lose a lot of functionalities if you change the server details of the server where Coolify - is - running on.
Please think again. -
+ + You could lose a lot of functionalities if you change the server details of the server where Coolify + is + running on.
Please think again. +
@else - Save - @if ($server->isFunctional()) - - Validate & configure - - - - - Revalidate server - - - @endif + Save + @if ($server->isFunctional()) + + Validate & configure + + + + + Revalidate server + + + @endif @endif
@if ($server->isFunctional()) - Server is reachable and validated. + Server is reachable and validated. @else - You can't use this server until it is validated. + You can't use this server until it is validated. @endif @if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id !== 0) - - Validate & configure - - - - - Validate Server & Install Docker Engine - - - @if ($server->validation_logs) -

Previous Validation Logs

-
- {!! $server->validation_logs !!} -
- @endif + + Validate & configure + + + + + Validate Server & Install Docker Engine + + + @if ($server->validation_logs) +

Previous Validation Logs

+
+ {!! $server->validation_logs !!} +
+ @endif @endif @if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id === 0) - - Validate Server - + + Validate Server + @endif @if ($server->isForceDisabled() && isCloud()) -
The system has disabled the server because you have exceeded the - number of servers for which you have paid.
+
The system has disabled the server because you have exceeded the + number of servers for which you have paid.
@endif
@if (!$server->settings->is_swarm_worker && !$server->settings->is_build_server) - + @endif
- +
@@ -72,80 +77,103 @@
@if ($server->isFunctional()) - @if (!$server->isLocalhost()) - -
-

Cloudflare Tunnels -

- -
- @if ($server->settings->is_cloudflare_tunnel) - - @else - - - - @endif - @if (!$server->isBuildServer()) -

Swarm (experimental)

-
Read the docs here. -
- @if ($server->settings->is_swarm_worker) - - @else - - @endif + @if (!$server->isLocalhost()) + +
+

Cloudflare Tunnels +

+ +
+ @if ($server->settings->is_cloudflare_tunnel) + + @else + + + + @endif + @if (!$server->isBuildServer()) +

Swarm (experimental)

+
Read the docs here. +
+ @if ($server->settings->is_swarm_worker) + + @else + + @endif - @if ($server->settings->is_swarm_manager) - + @if ($server->settings->is_swarm_manager) + + @else + + @endif + @endif + @endif @else - - @endif - @endif - @endif - @else -
-

Cloudflare Tunnels -

- -
- @if ($server->settings->is_cloudflare_tunnel) - - @else - - - - @endif +
+

Cloudflare Tunnels +

+ +
+ @if ($server->settings->is_cloudflare_tunnel) + + @else + + + + @endif @endif
@if ($server->isFunctional()) -

Settings

-
-
-
-
- @if ($server->settings->force_docker_cleanup) - - @else - - @endif +

Settings

+
+
+
+ +
+ @if ($server->settings->force_docker_cleanup) + + @else + + @endif +
+
+ + +
-
- - +
+

Sentinel

+ {{-- @if ($server->isSentinelEnabled()) --}} + {{-- Restart --}} + {{-- @endif --}}
-
-
-

Sentinel

- {{-- @if ($server->isSentinelEnabled()) --}} - {{-- Restart --}} - {{-- @endif --}} -
-
Metrics are disabled until a few bugs are fixed.
- {{--
+
Metrics are disabled until a few bugs are fixed.
+ {{--
@@ -161,4 +189,4 @@
--}} @endif -
\ No newline at end of file +