diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php index a945670d4..72ce80b6b 100644 --- a/app/Actions/Server/UpdateCoolify.php +++ b/app/Actions/Server/UpdateCoolify.php @@ -20,7 +20,6 @@ public function handle($manual_update = false) { try { $settings = InstanceSettings::get(); - ray('Running InstanceAutoUpdateJob'); $this->server = Server::find(0); if (! $this->server) { return; @@ -48,7 +47,6 @@ public function handle($manual_update = false) private function update() { if (isDev()) { - ray('Running in dev mode'); remote_process([ 'sleep 10', ], $this->server); diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index fdfbc8aff..41d821e8a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -13,6 +13,8 @@ use App\Jobs\PullTemplatesFromCDN; use App\Jobs\ScheduledTaskJob; use App\Jobs\ServerStatusJob; +use App\Jobs\UpdateCoolifyJob; +use App\Jobs\CheckForUpdatesJob; use App\Models\InstanceSettings; use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledTask; @@ -31,10 +33,6 @@ protected function schedule(Schedule $schedule): void $settings = InstanceSettings::get(); if (isDev()) { - // Instance Jobs - $schedule->command('horizon:snapshot')->everyMinute(); - $schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer(); - $schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer(); // Server Jobs $this->check_scheduled_backups($schedule); $this->check_resources($schedule); @@ -45,10 +43,16 @@ protected function schedule(Schedule $schedule): void // Instance Jobs $schedule->command('horizon:snapshot')->everyFiveMinutes(); $schedule->command('cleanup:unreachable-servers')->daily(); - $schedule->job(new PullCoolifyImageJob)->cron($settings->update_check_frequency)->onOneServer(); - $schedule->job(new PullTemplatesFromCDN)->everyThirtyMinutes()->onOneServer(); - $schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer(); - + $schedule->job(new PullTemplatesFromCDN)->daily()->onOneServer(); + $schedule->job(new CleanupInstanceStuffsJob)->everyFiveMinutes()->onOneServer(); + + if ($settings->update_check_frequency && $this->isValidCronExpression($settings->update_check_frequency)) { + $schedule->job(new PullCoolifyImageJob)->cron($settings->update_check_frequency)->onOneServer(); + } else { + // Default to every 12 hours if not set or invalid + $schedule->job(new PullCoolifyImageJob)->twiceDaily()->onOneServer(); + } + // Server Jobs $this->scheduleUpdates($schedule); $this->pull_images($schedule); @@ -56,7 +60,6 @@ protected function schedule(Schedule $schedule): void $this->check_scheduled_tasks($schedule); $this->check_scheduled_backups($schedule); - $schedule->command('cleanup:database --yes')->daily(); $schedule->command('uploads:clear')->everyTwoMinutes(); } @@ -79,13 +82,31 @@ private function scheduleUpdates($schedule) $settings = InstanceSettings::get(); // Schedule update check - if ($settings->update_check_frequency) { + if ($settings->update_check_frequency && $this->isValidCronExpression($settings->update_check_frequency)) { $schedule->job(new CheckForUpdatesJob())->cron($settings->update_check_frequency)->onOneServer(); + } else { + // Default to every 12 hours if not set or invalid + $schedule->job(new CheckForUpdatesJob())->twiceDaily()->onOneServer(); } // Schedule auto-update - if ($settings->is_auto_update_enabled && $settings->auto_update_frequency) { - $schedule->job(new UpdateCoolifyJob())->cron($settings->auto_update_frequency)->onOneServer(); + if ($settings->is_auto_update_enabled) { + if ($settings->auto_update_frequency && $this->isValidCronExpression($settings->auto_update_frequency)) { + $schedule->job(new UpdateCoolifyJob())->cron($settings->auto_update_frequency)->onOneServer(); + } else { + // Default to every 24 hours if not set or invalid + $schedule->job(new UpdateCoolifyJob())->daily()->onOneServer(); + } + } + } + + private function isValidCronExpression($expression) + { + try { + new \Cron\CronExpression($expression); + return true; + } catch (\Exception $e) { + return false; } } diff --git a/app/Jobs/CheckForUpdatesJob.php b/app/Jobs/CheckForUpdatesJob.php index 8141dd3fa..e18e37ed6 100644 --- a/app/Jobs/CheckForUpdatesJob.php +++ b/app/Jobs/CheckForUpdatesJob.php @@ -33,14 +33,12 @@ public function handle(): void if (version_compare($latest_version, $current_version, '>')) { // New version available $settings->update(['new_version_available' => true]); - // Optionally, you can trigger a notification here } else { $settings->update(['new_version_available' => false]); } } } catch (\Throwable $e) { - // Log the error or send a notification - ray('CheckForUpdatesJob failed: ' . $e->getMessage()); + // Consider implementing a notification to administrators } } } \ No newline at end of file diff --git a/app/Jobs/PullCoolifyImageJob.php b/app/Jobs/PullCoolifyImageJob.php index 9c8145b70..624dc4414 100644 --- a/app/Jobs/PullCoolifyImageJob.php +++ b/app/Jobs/PullCoolifyImageJob.php @@ -43,7 +43,6 @@ public function handle(): void if (version_compare($latest_version, $current_version, '<')) { return; } - // The actual update process will be handled by the UpdateCoolifyJob } catch (\Throwable $e) { throw $e; } diff --git a/app/Jobs/UpdateCoolifyJob.php b/app/Jobs/UpdateCoolifyJob.php index 243e3934f..5c8e8e679 100644 --- a/app/Jobs/UpdateCoolifyJob.php +++ b/app/Jobs/UpdateCoolifyJob.php @@ -2,7 +2,6 @@ namespace App\Jobs; -use App\Actions\Server\UpdateCoolify; use App\Models\InstanceSettings; use App\Models\Server; use Illuminate\Bus\Queueable; @@ -11,6 +10,8 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use App\Actions\Server\UpdateCoolify; +use Illuminate\Support\Facades\Log; class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue { @@ -22,23 +23,31 @@ public function handle(): void { try { $settings = InstanceSettings::get(); - if (!$settings->is_auto_update_enabled || !$settings->new_version_available) { + if (!$settings->is_auto_update_enabled) { + Log::info('Auto-update is disabled. Skipping update check.'); + return; + } + + if (!$settings->new_version_available) { + Log::info('No new version available. Skipping update.'); return; } $server = Server::findOrFail(0); if (!$server) { + Log::error('Server not found. Cannot proceed with update.'); return; } + Log::info('Starting Coolify update process...'); UpdateCoolify::run(false); // false means it's not a manual update - // After successful update, reset the new_version_available flag $settings->update(['new_version_available' => false]); + Log::info('Coolify update completed successfully.'); } catch (\Throwable $e) { - // Log the error or send a notification - ray('UpdateCoolifyJob failed: ' . $e->getMessage()); + Log::error('UpdateCoolifyJob failed: ' . $e->getMessage()); + // Consider implementing a notification to administrators } } } \ No newline at end of file diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php index 7439e112f..0d0b110b6 100644 --- a/app/Livewire/Settings/Configuration.php +++ b/app/Livewire/Settings/Configuration.php @@ -5,6 +5,7 @@ use App\Models\InstanceSettings as ModelsInstanceSettings; use App\Models\Server; use Livewire\Component; +use Cron\CronExpression; class Configuration extends Component { @@ -20,6 +21,10 @@ class Configuration extends Component public bool $is_api_enabled; + public ?string $auto_update_frequency; + + public ?string $update_check_frequency; + protected string $dynamic_config_path = '/data/coolify/proxy/dynamic'; protected Server $server; @@ -32,6 +37,9 @@ class Configuration extends Component 'settings.custom_dns_servers' => 'nullable', 'settings.instance_name' => 'nullable', 'settings.allowed_ips' => 'nullable', + 'settings.is_auto_update_enabled' => 'boolean', + 'auto_update_frequency' => 'nullable|string', + 'update_check_frequency' => 'required|string', ]; protected $validationAttributes = [ @@ -41,6 +49,9 @@ class Configuration extends Component 'settings.public_port_max' => 'Public port max', 'settings.custom_dns_servers' => 'Custom DNS servers', 'settings.allowed_ips' => 'Allowed IPs', + 'settings.is_auto_update_enabled' => 'Auto Update Enabled', + 'auto_update_frequency' => 'Auto Update Frequency', + 'update_check_frequency' => 'Update Check Frequency', ]; public function mount() @@ -50,6 +61,8 @@ public function mount() $this->is_registration_enabled = $this->settings->is_registration_enabled; $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled; $this->is_api_enabled = $this->settings->is_api_enabled; + $this->auto_update_frequency = $this->settings->auto_update_frequency; + $this->update_check_frequency = $this->settings->update_check_frequency; } public function instantSave() @@ -59,6 +72,8 @@ public function instantSave() $this->settings->is_registration_enabled = $this->is_registration_enabled; $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled; $this->settings->is_api_enabled = $this->is_api_enabled; + $this->settings->auto_update_frequency = $this->auto_update_frequency; + $this->settings->update_check_frequency = $this->update_check_frequency; $this->settings->save(); $this->dispatch('success', 'Settings updated!'); } @@ -76,6 +91,16 @@ public function submit() } $this->validate(); + if ($this->is_auto_update_enabled && !$this->validateCronExpression($this->auto_update_frequency)) { + $this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.'); + return; + } + + if (!$this->validateCronExpression($this->update_check_frequency)) { + $this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.'); + return; + } + if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) { if (! validate_dns_entry($this->settings->fqdn, $this->server)) { $this->dispatch('error', "Validating DNS failed.

Make sure you have added the DNS records correctly.

{$this->settings->fqdn}->{$this->server->ip}

Check this documentation for further help."); @@ -99,6 +124,14 @@ public function submit() $this->settings->allowed_ips = $this->settings->allowed_ips->unique(); $this->settings->allowed_ips = $this->settings->allowed_ips->implode(','); + $this->settings->do_not_track = $this->do_not_track; + $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled; + $this->settings->is_registration_enabled = $this->is_registration_enabled; + $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled; + $this->settings->is_api_enabled = $this->is_api_enabled; + $this->settings->auto_update_frequency = $this->auto_update_frequency; + $this->settings->update_check_frequency = $this->update_check_frequency; + $this->settings->save(); $this->server->setupDynamicProxyConfiguration(); if (! $error_show) { @@ -108,4 +141,38 @@ public function submit() return handleError($e, $this); } } -} + + private function validateCronExpression($expression): bool + { + if (empty($expression)) { + return false; + } + $isValid = false; + try { + $cronExpression = new CronExpression($expression); + $isValid = $cronExpression->getNextRunDate() !== false; + } catch (\Exception $e) { + $isValid = false; + } + + if (isset(VALID_CRON_STRINGS[$expression])) { + $isValid = true; + } + + return $isValid; + } + + public function updatedAutoUpdateFrequency() + { + if (!$this->validateCronExpression($this->auto_update_frequency)) { + $this->dispatch('error', 'Invalid Cron / Human expression.'); + } + } + + public function updatedUpdateCheckFrequency() + { + if (!$this->validateCronExpression($this->update_check_frequency)) { + $this->dispatch('error', 'Invalid Cron / Human expression.'); + } + } +} \ No newline at end of file diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php index bd3c41a1f..5bd421956 100644 --- a/app/Models/InstanceSettings.php +++ b/app/Models/InstanceSettings.php @@ -18,6 +18,9 @@ class InstanceSettings extends Model implements SendsEmail 'resale_license' => 'encrypted', 'smtp_password' => 'encrypted', 'allowed_ip_ranges' => 'array', + 'is_auto_update_enabled' => 'boolean', + 'auto_update_frequency' => 'string', + 'update_check_frequency' => 'string', ]; public function fqdn(): Attribute diff --git a/database/migrations/2024_08_05_142659_add_new_version_available_to_instance_settings_table.php b/database/migrations/2024_08_05_142659_add_new_version_available_to_instance_settings_table.php index 37cd93dc4..25c5666b8 100644 --- a/database/migrations/2024_08_05_142659_add_new_version_available_to_instance_settings_table.php +++ b/database/migrations/2024_08_05_142659_add_new_version_available_to_instance_settings_table.php @@ -12,8 +12,8 @@ public function up(): void { Schema::table('instance_settings', function (Blueprint $table) { - $table->string('update_check_frequency')->nullable(); - $table->string('auto_update_frequency')->nullable(); + $table->string('update_check_frequency')->default('0 */12 * * *')->nullable(); + $table->string('auto_update_frequency')->default('0 0 * * *')->nullable(); }); } diff --git a/resources/views/livewire/settings/configuration.blade.php b/resources/views/livewire/settings/configuration.blade.php index e2f375037..e9cb7267d 100644 --- a/resources/views/livewire/settings/configuration.blade.php +++ b/resources/views/livewire/settings/configuration.blade.php @@ -46,8 +46,10 @@ @if($is_auto_update_enabled) + @error('settings.auto_update_frequency') {{ $message }} @enderror @endif - + + @error('settings.update_check_frequency') {{ $message }} @enderror @endif