diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 31760f2fd3..734063b2ef 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -83,7 +83,7 @@ private function check_resources($schedule) } private function instance_auto_update($schedule) { - if (isDev()) { + if (isDev() || isCloud()) { return; } $settings = InstanceSettings::get(); diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 1717c5d085..efa906388d 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -95,7 +95,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private ?string $buildTarget = null; private Collection $saved_outputs; private ?string $full_healthcheck_url = null; - private bool $custom_healthcheck_found = false; private string $serverUser = 'root'; private string $serverUserHomeDir = '/root'; @@ -903,10 +902,13 @@ private function health_check() if ($this->server->isSwarm()) { // Implement healthcheck for swarm } else { - if ($this->application->isHealthcheckDisabled() && $this->custom_healthcheck_found === false) { + if ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found === false) { $this->newVersionIsHealthy = true; return; } + if ($this->application->custom_healthcheck_found) { + $this->application_deployment_queue->addLogEntry("Custom healthcheck found, skipping default healthcheck."); + } // ray('New container name: ', $this->container_name); if ($this->container_name) { $counter = 1; @@ -914,6 +916,12 @@ private function health_check() if ($this->full_healthcheck_url) { $this->application_deployment_queue->addLogEntry("Healthcheck URL (inside the container): {$this->full_healthcheck_url}"); } + $this->application_deployment_queue->addLogEntry("Waiting for the start period ({$this->application->health_check_start_period} seconds) before starting healthcheck."); + $sleeptime = 0; + while ($sleeptime < $this->application->health_check_start_period) { + Sleep::for(1)->seconds(); + $sleeptime++; + } while ($counter <= $this->application->health_check_retries) { $this->execute_remote_command( [ @@ -936,7 +944,11 @@ private function health_check() break; } $counter++; - Sleep::for($this->application->health_check_interval)->seconds(); + $sleeptime = 0; + while ($sleeptime < $this->application->health_check_interval) { + Sleep::for(1)->seconds(); + $sleeptime++; + } } } } @@ -1262,16 +1274,14 @@ private function generate_compose_file() return escapeDollarSign($value); }); $labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray(); + // Check for custom HEALTHCHECK - $this->custom_healthcheck_found = false; if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) { $this->execute_remote_command([ executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true ]); $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n")); - if (str($dockerfile)->contains('HEALTHCHECK')) { - $this->custom_healthcheck_found = true; - } + $this->application->parseHealthcheckFromDockerfile($dockerfile); } $docker_compose = [ 'version' => '3.8', @@ -1317,18 +1327,17 @@ private function generate_compose_file() if (!is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } - if (!$this->custom_healthcheck_found) { - $docker_compose['services'][$this->container_name]['healthcheck'] = [ - 'test' => [ - 'CMD-SHELL', - $this->generate_healthcheck_commands() - ], - 'interval' => $this->application->health_check_interval . 's', - 'timeout' => $this->application->health_check_timeout . 's', - 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period . 's' - ]; - } + $docker_compose['services'][$this->container_name]['healthcheck'] = [ + 'test' => [ + 'CMD-SHELL', + $this->generate_healthcheck_commands() + ], + 'interval' => $this->application->health_check_interval . 's', + 'timeout' => $this->application->health_check_timeout . 's', + 'retries' => $this->application->health_check_retries, + 'start_period' => $this->application->health_check_start_period . 's' + ]; + if (!is_null($this->application->limits_cpuset)) { data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset); } @@ -1595,10 +1604,10 @@ private function generate_local_persistent_volumes_only_volume_names() private function generate_healthcheck_commands() { - if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') { - // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl. - return 'exit 0'; - } + // if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') { + // // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl. + // return 'exit 0'; + // } if (!$this->application->health_check_port) { $health_check_port = $this->application->ports_exposes_array[0]; } else { diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index befeffed06..a09ebeadae 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -289,7 +289,7 @@ public function handle(): void if ($this->backup->save_s3) { $this->upload_to_s3(); } - $this->team?->notify(new BackupSuccess($this->backup, $this->database)); + $this->team?->notify(new BackupSuccess($this->backup, $this->database, $database)); $this->backup_log->update([ 'status' => 'success', 'message' => $this->backup_output, @@ -305,8 +305,7 @@ public function handle(): void ]); } send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage()); - $this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output)); - throw $e; + $this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output, $database)); } } } catch (\Throwable $e) { diff --git a/app/Jobs/InstanceAutoUpdateJob.php b/app/Jobs/InstanceAutoUpdateJob.php index fa5c29421c..dc35aa2b14 100644 --- a/app/Jobs/InstanceAutoUpdateJob.php +++ b/app/Jobs/InstanceAutoUpdateJob.php @@ -15,7 +15,8 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public $timeout = 120; + public $timeout = 600; + public $tries = 1; public function __construct(private bool $force = false) { diff --git a/app/Jobs/SendMessageToTelegramJob.php b/app/Jobs/SendMessageToTelegramJob.php index 4785da669a..4191b02feb 100644 --- a/app/Jobs/SendMessageToTelegramJob.php +++ b/app/Jobs/SendMessageToTelegramJob.php @@ -57,7 +57,7 @@ public function handle(): void } } $payload = [ - 'parse_mode' => 'markdown', + // 'parse_mode' => 'markdown', 'reply_markup' => json_encode([ 'inline_keyboard' => [ [...$inlineButtons], diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 6926e52cb6..8734229855 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -186,6 +186,7 @@ public function loadComposeFile($isInit = false) $this->dispatch('success', 'Docker compose file loaded.'); $this->dispatch('compose_loaded'); $this->dispatch('refresh_storages'); + $this->dispatch('refreshEnvs'); } catch (\Throwable $e) { $this->application->docker_compose_location = $this->initialDockerComposeLocation; $this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation; diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php index 0717a51f08..0b1e6edf56 100644 --- a/app/Livewire/Project/Application/Heading.php +++ b/app/Livewire/Project/Application/Heading.php @@ -39,7 +39,8 @@ public function check_status($showNotification = false) } if ($showNotification) $this->dispatch('success', "Success", "Application status updated."); - $this->dispatch('configurationChanged'); + // Removed because it caused flickering + // $this->dispatch('configurationChanged'); } public function force_deploy_without_cache() diff --git a/app/Livewire/Project/Database/Backup/Execution.php b/app/Livewire/Project/Database/Backup/Execution.php index 1f790d6438..000b6fb2be 100644 --- a/app/Livewire/Project/Database/Backup/Execution.php +++ b/app/Livewire/Project/Database/Backup/Execution.php @@ -35,11 +35,6 @@ public function mount() $this->executions = $executions; $this->s3s = currentTeam()->s3s; } - public function cleanupFailed() - { - $this->backup->executions()->where('status', 'failed')->delete(); - $this->dispatch('refreshBackupExecutions'); - } public function render() { return view('livewire.project.database.backup.execution'); diff --git a/app/Livewire/Project/Database/BackupExecutions.php b/app/Livewire/Project/Database/BackupExecutions.php index b127a685cf..a34d849f29 100644 --- a/app/Livewire/Project/Database/BackupExecutions.php +++ b/app/Livewire/Project/Database/BackupExecutions.php @@ -2,9 +2,7 @@ namespace App\Livewire\Project\Database; -use Illuminate\Support\Facades\Storage; use Livewire\Component; -use Symfony\Component\HttpFoundation\StreamedResponse; class BackupExecutions extends Component { @@ -16,11 +14,15 @@ public function getListeners() $userId = auth()->user()->id; return [ "echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions', - "refreshBackupExecutions", "deleteBackup" ]; } + public function cleanupFailed() + { + $this->backup->executions()->where('status', 'failed')->delete(); + $this->refreshBackupExecutions(); + } public function deleteBackup($exeuctionId) { $execution = $this->backup->executions()->where('id', $exeuctionId)->first(); diff --git a/app/Livewire/Project/Database/Import.php b/app/Livewire/Project/Database/Import.php index 74e41056a9..d435289fa4 100644 --- a/app/Livewire/Project/Database/Import.php +++ b/app/Livewire/Project/Database/Import.php @@ -27,6 +27,7 @@ class Import extends Component public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB'; public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE'; public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE'; + public string $mongodbRestoreCommand = 'mongorestore --authenticationDatabase=admin --username $MONGO_INITDB_ROOT_USERNAME --password $MONGO_INITDB_ROOT_PASSWORD --uri mongodb://localhost:27017 --gzip --archive='; public function getListeners() { @@ -62,8 +63,7 @@ public function getContainers() $this->resource->getMorphClass() == 'App\Models\StandaloneRedis' || $this->resource->getMorphClass() == 'App\Models\StandaloneKeydb' || $this->resource->getMorphClass() == 'App\Models\StandaloneDragonfly' || - $this->resource->getMorphClass() == 'App\Models\StandaloneClickhouse' || - $this->resource->getMorphClass() == 'App\Models\StandaloneMongodb' + $this->resource->getMorphClass() == 'App\Models\StandaloneClickhouse' ) { $this->unsupported = true; } @@ -101,6 +101,10 @@ public function runImport() $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->postgresqlRestoreCommand} {$tmpPath}'"; $this->importCommands[] = "rm {$tmpPath}"; break; + case 'App\Models\StandaloneMongodb': + $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mongodbRestoreCommand}{$tmpPath}'"; + $this->importCommands[] = "rm {$tmpPath}"; + break; } $this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'"; diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 8bbb5b0529..c6c699acf5 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -94,6 +94,17 @@ public function load_branch() $repository = str($this->repository_url)->after(':')->before('.git'); $this->repository_url = 'https://' . str($github_instance) . '/' . $repository; } + if ( + (str($this->repository_url)->startsWith('https://') || + str($this->repository_url)->startsWith('http://')) && + !str($this->repository_url)->endsWith('.git') && + !str($this->repository_url)->contains('github.com') + ) { + $this->repository_url = $this->repository_url . '.git'; + } + if (str($this->repository_url)->contains('github.com')) { + $this->repository_url = str($this->repository_url)->before('.git')->value(); + } } catch (\Throwable $e) { return handleError($e, $this); } @@ -170,7 +181,6 @@ public function submit() 'name' => generate_random_name(), 'git_repository' => $this->git_repository, 'git_branch' => $this->git_branch, - 'build_pack' => 'nixpacks', 'ports_exposes' => $this->port, 'publish_directory' => $this->publish_directory, 'environment_id' => $environment->id, @@ -183,7 +193,6 @@ public function submit() 'name' => generate_application_name($this->git_repository, $this->git_branch), 'git_repository' => $this->git_repository, 'git_branch' => $this->git_branch, - 'build_pack' => 'nixpacks', 'ports_exposes' => $this->port, 'publish_directory' => $this->publish_directory, 'environment_id' => $environment->id, @@ -195,7 +204,6 @@ public function submit() ]; } - $application = Application::create($application_init); $application->settings->is_static = $this->is_static; diff --git a/app/Livewire/Project/New/SimpleDockerfile.php b/app/Livewire/Project/New/SimpleDockerfile.php index 55b48041ad..172403a1ab 100644 --- a/app/Livewire/Project/New/SimpleDockerfile.php +++ b/app/Livewire/Project/New/SimpleDockerfile.php @@ -70,6 +70,8 @@ public function submit() 'fqdn' => $fqdn ]); + $application->parseHealthcheckFromDockerfile(dockerfile: collect(str($this->dockerfile)->trim()->explode("\n")), isInit: true); + return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, 'environment_name' => $environment->name, diff --git a/app/Livewire/Project/Shared/HealthChecks.php b/app/Livewire/Project/Shared/HealthChecks.php index 3bf507cab6..56f5a27593 100644 --- a/app/Livewire/Project/Shared/HealthChecks.php +++ b/app/Livewire/Project/Shared/HealthChecks.php @@ -17,18 +17,17 @@ class HealthChecks extends Component 'resource.health_check_return_code' => 'integer', 'resource.health_check_scheme' => 'string', 'resource.health_check_response_text' => 'nullable|string', - 'resource.health_check_interval' => 'integer', - 'resource.health_check_timeout' => 'integer', - 'resource.health_check_retries' => 'integer', + 'resource.health_check_interval' => 'integer|min:1', + 'resource.health_check_timeout' => 'integer|min:1', + 'resource.health_check_retries' => 'integer|min:1', 'resource.health_check_start_period' => 'integer', + 'resource.custom_healthcheck_found' => 'boolean', ]; public function instantSave() { $this->resource->save(); $this->dispatch('success', 'Health check updated.'); - - } public function submit() { diff --git a/app/Models/Application.php b/app/Models/Application.php index f28d389f48..98b2c23b13 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -963,4 +963,51 @@ public function getFilesFromServer(bool $isInit = false) { getFilesystemVolumesFromServer($this, $isInit); } + + public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false) { + if (str($dockerfile)->contains('HEALTHCHECK') && ($this->isHealthcheckDisabled() || $isInit)) { + $healthcheckCommand = null; + $lines = $dockerfile->toArray(); + foreach ($lines as $line) { + $trimmedLine = trim($line); + if (str_starts_with($trimmedLine, 'HEALTHCHECK')) { + $healthcheckCommand .= trim($trimmedLine, '\\ '); + continue; + } + if (isset($healthcheckCommand) && str_contains($trimmedLine, '\\')) { + $healthcheckCommand .= ' ' . trim($trimmedLine, '\\ '); + } + if (isset($healthcheckCommand) && !str_contains($trimmedLine, '\\') && !empty($healthcheckCommand)) { + $healthcheckCommand .= ' ' . $trimmedLine; + break; + } + } + if (str($healthcheckCommand)->isNotEmpty()) { + $interval = str($healthcheckCommand)->match('/--interval=(\d+)/'); + $timeout = str($healthcheckCommand)->match('/--timeout=(\d+)/'); + $start_period = str($healthcheckCommand)->match('/--start-period=(\d+)/'); + $start_interval = str($healthcheckCommand)->match('/--start-interval=(\d+)/'); + $retries = str($healthcheckCommand)->match('/--retries=(\d+)/'); + if ($interval->isNotEmpty()) { + $this->health_check_interval = $interval->toInteger(); + } + if ($timeout->isNotEmpty()) { + $this->health_check_timeout = $timeout->toInteger(); + } + if ($start_period->isNotEmpty()) { + $this->health_check_start_period = $start_period->toInteger(); + } + // if ($start_interval) { + // $this->health_check_start_interval = $start_interval->value(); + // } + if ($retries->isNotEmpty()) { + $this->health_check_retries = $retries->toInteger(); + } + if ($interval || $timeout || $start_period || $start_interval || $retries) { + $this->custom_healthcheck_found = true; + $this->save(); + } + } + } + } } diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php index 3746a32f5c..2197d51df6 100644 --- a/app/Models/StandaloneClickhouse.php +++ b/app/Models/StandaloneClickhouse.php @@ -207,7 +207,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return $this->clickhouse_db; - } } diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php index adc1ea6ccf..7b18666b8c 100644 --- a/app/Models/StandaloneDragonfly.php +++ b/app/Models/StandaloneDragonfly.php @@ -207,7 +207,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return '0'; - } } diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index ff91322a0e..c2c1b98daa 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -208,7 +208,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return '0'; - } } diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index 37d39f882e..5e18bbfde4 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -208,7 +208,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return $this->mariadb_database; - } } diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index 30414e26b8..8e4d327a38 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -223,7 +223,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return null; - } } diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index 53e9b6f226..eede451d77 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -209,7 +209,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return $this->mysql_database; - } } diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 6435c49de1..cf449a8155 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -208,7 +208,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return $this->postgres_db; - } } diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index de18c8c07d..da4701df90 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -204,7 +204,4 @@ public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); } - public function database_name() { - return '0'; - } } diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php index 1705deda18..05fe544d07 100644 --- a/app/Notifications/Application/DeploymentFailed.php +++ b/app/Notifications/Application/DeploymentFailed.php @@ -69,10 +69,10 @@ public function toMail(): MailMessage public function toDiscord(): string { if ($this->preview) { - $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; + $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: '; $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; } else { - $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; + $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): '; $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; } return $message; @@ -80,9 +80,9 @@ public function toDiscord(): string public function toTelegram(): array { if ($this->preview) { - $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; + $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: '; } else { - $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; + $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): '; } $buttons[] = [ "text" => "Deployment logs", diff --git a/app/Notifications/Database/BackupFailed.php b/app/Notifications/Database/BackupFailed.php index 67fd275894..7cad486b3f 100644 --- a/app/Notifications/Database/BackupFailed.php +++ b/app/Notifications/Database/BackupFailed.php @@ -15,21 +15,20 @@ class BackupFailed extends Notification implements ShouldQueue { use Queueable; - public $tries = 1; + public $backoff = 10; + public $tries = 2; public string $name; - public ?string $database_name = null; public string $frequency; - public function __construct(ScheduledDatabaseBackup $backup, public $database, public $output) + public function __construct(ScheduledDatabaseBackup $backup, public $database, public $output, public $database_name) { $this->name = $database->name; - $this->database_name = $database->database_name(); $this->frequency = $backup->frequency; } public function via(object $notifiable): array { - return [DiscordChannel::class, TelegramChannel::class, MailChannel::class]; + return setNotificationChannels($notifiable, 'database_backups'); } public function toMail(): MailMessage @@ -47,11 +46,11 @@ public function toMail(): MailMessage public function toDiscord(): string { - return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}"; + return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}"; } public function toTelegram(): array { - $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}"; + $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}"; return [ "message" => $message, ]; diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php index d69a31f462..c43a12276f 100644 --- a/app/Notifications/Database/BackupSuccess.php +++ b/app/Notifications/Database/BackupSuccess.php @@ -12,15 +12,14 @@ class BackupSuccess extends Notification implements ShouldQueue { use Queueable; - public $tries = 1; + public $backoff = 10; + public $tries = 3; public string $name; - public ?string $database_name = null; public string $frequency; - public function __construct(ScheduledDatabaseBackup $backup, public $database) + public function __construct(ScheduledDatabaseBackup $backup, public $database, public $database_name) { $this->name = $database->name; - $this->database_name = $database->database_name(); $this->frequency = $backup->frequency; } @@ -48,6 +47,7 @@ public function toDiscord(): string public function toTelegram(): array { $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful."; + ray($message); return [ "message" => $message, ]; diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index a70c85a72b..a087c92c5f 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -8,6 +8,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Str; use Spatie\Url\Url; +use Visus\Cuid2\Cuid2; function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null, ?bool $includePullrequests = false): Collection { @@ -272,7 +273,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, } return $labels->sort(); } -function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null) +function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false) { $labels = collect([]); $labels->push('traefik.enable=true'); @@ -313,7 +314,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ } foreach ($domains as $loop => $domain) { try { - // $uuid = new Cuid2(7); + if ($generate_unique_uuid) { + $uuid = new Cuid2(7); + } $url = Url::fromString($domain); $host = $url->getHost(); $path = $url->getPath(); diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php index 8e3c0337ed..26a69222ab 100644 --- a/bootstrap/helpers/services.php +++ b/bootstrap/helpers/services.php @@ -18,7 +18,7 @@ function collectRegex(string $name) } function replaceVariables($variable) { - return $variable->replaceFirst('$', '')->replaceFirst('{', '')->replaceLast('}', ''); + return $variable->before('}')->replaceFirst('$', '')->replaceFirst('{', ''); } function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Application $oneService, bool $isInit = false) @@ -27,7 +27,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli if ($oneService->getMorphClass() === 'App\Models\Application') { $workdir = $oneService->workdir(); $server = $oneService->destination->server; - } else{ + } else { $workdir = $oneService->service->workdir(); $server = $oneService->service->server; } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index b2c34900e4..0161b8c95c 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1620,7 +1620,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( uuid: $resource->uuid, domains: $fqdns, - serviceLabels: $serviceLabels + serviceLabels: $serviceLabels, + generate_unique_uuid: $resource->build_pack === 'dockercompose' )); $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy( network: $resource->destination->network, diff --git a/config/sentry.php b/config/sentry.php index e833ffdb26..15711e6517 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.270', + 'release' => '4.0.0-beta.271', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index e85b7f5a56..ebbaefd2a6 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ boolean('custom_healthcheck_found')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('custom_healthcheck_found'); + }); + } +}; diff --git a/resources/views/components/forms/input.blade.php b/resources/views/components/forms/input.blade.php index 2c6c1ef490..04b4a41c63 100644 --- a/resources/views/components/forms/input.blade.php +++ b/resources/views/components/forms/input.blade.php @@ -41,6 +41,7 @@ class="absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer hover:da wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300' wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled" type="{{ $type }}" @disabled($disabled) + min="{{ $attributes->get('min') }}" max="{{ $attributes->get('max') }}" @if ($id !== 'null') id={{ $id }} @endif name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"> @endif diff --git a/resources/views/components/popup-small.blade.php b/resources/views/components/popup-small.blade.php index 8370dcd077..97ed025fe3 100644 --- a/resources/views/components/popup-small.blade.php +++ b/resources/views/components/popup-small.blade.php @@ -6,23 +6,28 @@ x-transition:enter-start="translate-y-full" x-transition:enter-end="translate-y-0" x-transition:leave="transition ease-in duration-300" x-transition:leave-start="translate-y-0" x-transition:leave-end="translate-y-full" x-init="setTimeout(() => { bannerVisible = true }, bannerVisibleAfter);" - class="fixed bottom-0 right-0 h-auto duration-300 ease-out px-5 pb-5 max-w-[46rem] z-[999]" - x-cloak> + class="fixed bottom-0 right-0 h-auto duration-300 ease-out px-5 pb-5 max-w-[46rem] z-[999]" x-cloak>
+ class="flex flex-col items-center justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 dark:bg-coolgray-100/40 hover:dark:bg-coolgray-100/100 lg:p-8 lg:flex-row sm:rounded">
@if (isset($icon)) {{ $icon }} - @endif
-

+

{{ $title }}

-

{{ $description }}

+
{{ $description }}
+
diff --git a/resources/views/livewire/project/database/backup-executions.blade.php b/resources/views/livewire/project/database/backup-executions.blade.php index ae9428923f..34e2b965d9 100644 --- a/resources/views/livewire/project/database/backup-executions.blade.php +++ b/resources/views/livewire/project/database/backup-executions.blade.php @@ -1,43 +1,51 @@ -
- @forelse($executions as $execution) -
data_get($execution, 'status') === 'success', - 'border-red-500' => data_get($execution, 'status') === 'failed', - ])> - @if (data_get($execution, 'status') === 'running') -
- -
- @endif -
Database: {{ data_get($execution, 'database_name', 'N/A') }}
-
Status: {{ data_get($execution, 'status') }}
-
Started At: {{ data_get($execution, 'created_at') }}
- @if (data_get($execution, 'message')) -
Message: {{ data_get($execution, 'message') }}
- @endif -
Size: {{ data_get($execution, 'size') }} B / {{ round((int) data_get($execution, 'size') / 1024, 2) }} - kB / {{ round((int) data_get($execution, 'size') / 1024 / 1024, 3) }} MB -
-
Location: {{ data_get($execution, 'filename', 'N/A') }}
-
-
- @if (data_get($execution, 'status') === 'success') - Download +
+
+

Executions

+ Cleanup Failed Backups +
+
+ @forelse($executions as $execution) + data_get($execution, 'status') === 'success', + 'border-red-500' => data_get($execution, 'status') === 'failed', + ])> + @if (data_get($execution, 'status') === 'running') +
+ +
+ @endif +
Database: {{ data_get($execution, 'database_name', 'N/A') }}
+
Status: {{ data_get($execution, 'status') }}
+
Started At: {{ data_get($execution, 'created_at') }}
+ @if (data_get($execution, 'message')) +
Message: {{ data_get($execution, 'message') }}
@endif - - - Delete - - This will delete this backup. It is not reversible.
Please think again. -
-
- +
Size: {{ data_get($execution, 'size') }} B / + {{ round((int) data_get($execution, 'size') / 1024, 2) }} + kB / {{ round((int) data_get($execution, 'size') / 1024 / 1024, 3) }} MB +
+
Location: {{ data_get($execution, 'filename', 'N/A') }}
+
+
+ @if (data_get($execution, 'status') === 'success') + Download + @endif + + + Delete + + This will delete this backup. It is not reversible.
Please think again. +
+
+ - @empty -
No executions found.
- @endforelse + @empty +
No executions found.
+ @endforelse +