v4.0.0-beta.416 (#5729)
* feat(README): add InterviewPal sponsorship link and corresponding SVG icon
* chore(versions): update coolify version to 4.0.0-beta.413 and nightly version to 4.0.0-beta.414 in configuration files
* fix(terminal): enhance WebSocket client verification with authorized IPs in terminal server
* chore(versions): update realtime version to 1.0.8 in versions.json
* chore(versions): update realtime version to 1.0.8 in versions.json
* chore(docker): update soketi image version to 1.0.8 in production configuration files
* chore(versions): update coolify version to 4.0.0-beta.414 and nightly version to 4.0.0-beta.415 in configuration files
* fix(ApplicationDeploymentJob): ensure source is an object before checking GitHub app properties
* fix(ui): Disable livewire navigate feature (causing spam of setInterval())
* fix(ui): Remove required attribute from image input in service application view
* fix(ui): Change application image validation to be nullable in service application view
* fix(Server): Correct proxy path formatting for Traefik proxy type
* chore(versions): update coolify version to 4.0.0-beta.416 and nightly version to 4.0.0-beta.417 in configuration files; fix links in deployment view
* feat(Service): Add functionality to convert between applications and databases in docker-compose based applications
fix(ui): Fix service layout refresh on compose change
* fix(service): graceful shutdown of old container (#5731)
* refactor(Database): streamline container shutdown process and reduce timeout duration
* fix(ServerCheck): enhance proxy container check to ensure it is running before proceeding
* chore(seeder): update git branch from 'main' to 'v4.x' for multiple examples in ApplicationSeeder
* fix(applications): include pull_request_id in deployment queue check to prevent duplicate deployments
* refactor(core): streamline container stopping process and reduce timeout duration; update related methods for consistency
* fix(database): update label for image input field to improve clarity
* feat(migration): add 'is_migrated' and 'custom_type' columns to service_applications and service_databases tables
* feat(backup): implement custom database type selection and enhance scheduled backups management
* fix(ServerCheck): set default proxy status to 'exited' to handle missing container state
* fix(database): reduce container stop timeout from 300 to 30 seconds for improved responsiveness
* refactor(database): update DB facade usage for consistency across service files
* Update app/Livewire/Project/Service/Database.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* refactor(database): enhance application conversion logic and add existence checks for databases and applications
* refactor(actions): standardize method naming for network and configuration deletion across application and service classes
* refactor(logdrain): consolidate log drain stopping logic to reduce redundancy
* refactor(StandaloneMariadb): add type hint for destination method to improve code clarity
* refactor(DeleteResourceJob): streamline resource deletion logic and improve conditional checks for database types
* refactor(jobs): update middleware to prevent job release after expiration for CleanupInstanceStuffsJob, RestartProxyJob, and ServerCheckJob
* fix(ui): system theming for charts (#5740)
* chore(deps-dev): bump vite from 6.2.6 to 6.3.4 (#5743)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.6 to 6.3.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.4/packages/vite)
---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.4
  dependency-type: direct:development
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* fix(dev): mount points?!
* fix(dev): proxy mount point
* fix(ui): allow adding scheduled backups for non-migrated databases
* fix(DatabaseBackupJob): escape PostgreSQL password in backup command (#5759)
* fix(ui): correct closing div tag in service index view
* Revert "fix(dev): mount points?!"
This reverts commit 365bf3cbf0.
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Jérémy <jeremy.derdaele@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Best Codes <106822363+The-Best-Codes@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: busybox <29630035+busybox11@users.noreply.github.com>
			
			
This commit is contained in:
		| @@ -5,10 +5,7 @@ namespace App\Livewire\Project\Application; | ||||
| use App\Actions\Docker\GetContainersStatus; | ||||
| use App\Models\Application; | ||||
| use App\Models\ApplicationPreview; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Process\InvokedProcess; | ||||
| use Illuminate\Support\Collection; | ||||
| use Illuminate\Support\Facades\Process; | ||||
| use Livewire\Component; | ||||
| use Spatie\Url\Url; | ||||
| use Visus\Cuid2\Cuid2; | ||||
| @@ -193,13 +190,12 @@ class Previews extends Component | ||||
|     { | ||||
|         try { | ||||
|             $server = $this->application->destination->server; | ||||
|             $timeout = 300; | ||||
| 
 | ||||
|             if ($this->application->destination->server->isSwarm()) { | ||||
|                 instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server); | ||||
|             } else { | ||||
|                 $containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray(); | ||||
|                 $this->stopContainers($containers, $server, $timeout); | ||||
|                 $this->stopContainers($containers, $server); | ||||
|             } | ||||
| 
 | ||||
|             GetContainersStatus::run($server); | ||||
| @@ -215,13 +211,12 @@ class Previews extends Component | ||||
|     { | ||||
|         try { | ||||
|             $server = $this->application->destination->server; | ||||
|             $timeout = 300; | ||||
| 
 | ||||
|             if ($this->application->destination->server->isSwarm()) { | ||||
|                 instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server); | ||||
|             } else { | ||||
|                 $containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray(); | ||||
|                 $this->stopContainers($containers, $server, $timeout); | ||||
|                 $this->stopContainers($containers, $server); | ||||
|             } | ||||
| 
 | ||||
|             ApplicationPreview::where('application_id', $this->application->id) | ||||
| @@ -237,48 +232,14 @@ class Previews extends Component | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function stopContainers(array $containers, $server, int $timeout) | ||||
|     private function stopContainers(array $containers, $server, int $timeout = 30) | ||||
|     { | ||||
|         $processes = []; | ||||
|         foreach ($containers as $container) { | ||||
|             $containerName = str_replace('/', '', $container['Names']); | ||||
|             $processes[$containerName] = $this->stopContainer($containerName, $timeout); | ||||
|         } | ||||
| 
 | ||||
|         $startTime = Carbon::now()->getTimestamp(); | ||||
|         while (count($processes) > 0) { | ||||
|             $finishedProcesses = array_filter($processes, function ($process) { | ||||
|                 return ! $process->running(); | ||||
|             }); | ||||
|             foreach (array_keys($finishedProcesses) as $containerName) { | ||||
|                 unset($processes[$containerName]); | ||||
|                 $this->removeContainer($containerName, $server); | ||||
|             } | ||||
| 
 | ||||
|             if (Carbon::now()->getTimestamp() - $startTime >= $timeout) { | ||||
|                 $this->forceStopRemainingContainers(array_keys($processes), $server); | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             usleep(100000); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function stopContainer(string $containerName, int $timeout): InvokedProcess | ||||
|     { | ||||
|         return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); | ||||
|     } | ||||
| 
 | ||||
|     private function removeContainer(string $containerName, $server) | ||||
|     { | ||||
|         instant_remote_process(["docker rm -f $containerName"], $server, throwError: false); | ||||
|     } | ||||
| 
 | ||||
|     private function forceStopRemainingContainers(array $containerNames, $server) | ||||
|     { | ||||
|         foreach ($containerNames as $containerName) { | ||||
|             instant_remote_process(["docker kill $containerName"], $server, throwError: false); | ||||
|             $this->removeContainer($containerName, $server); | ||||
|             instant_remote_process(command: [ | ||||
|                 "docker stop --time=$timeout $containerName", | ||||
|                 "docker rm -f $containerName", | ||||
|             ], server: $server, throwError: false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -120,6 +120,8 @@ class General extends Component | ||||
|         try { | ||||
|             $this->database->save(); | ||||
|             $this->dispatch('success', 'SSL configuration updated.'); | ||||
|             $this->db_url = $this->database->internal_db_url; | ||||
|             $this->db_url_public = $this->database->external_db_url; | ||||
|         } catch (Exception $e) { | ||||
|             return handleError($e, $this); | ||||
|         } | ||||
|   | ||||
| @@ -19,6 +19,8 @@ class ScheduledBackups extends Component | ||||
| 
 | ||||
|     public $s3s; | ||||
| 
 | ||||
|     public string $custom_type = 'mysql'; | ||||
| 
 | ||||
|     protected $listeners = ['refreshScheduledBackups']; | ||||
| 
 | ||||
|     protected $queryString = ['selectedBackupId']; | ||||
| @@ -49,6 +51,14 @@ class ScheduledBackups extends Component | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function setCustomType() | ||||
|     { | ||||
|         $this->database->custom_type = $this->custom_type; | ||||
|         $this->database->save(); | ||||
|         $this->dispatch('success', 'Database type set.'); | ||||
|         $this->refreshScheduledBackups(); | ||||
|     } | ||||
| 
 | ||||
|     public function delete($scheduled_backup_id): void | ||||
|     { | ||||
|         $this->database->scheduledBackups->find($scheduled_backup_id)->delete(); | ||||
| @@ -62,5 +72,6 @@ class ScheduledBackups extends Component | ||||
|         if ($id) { | ||||
|             $this->setSelectedBackup($id); | ||||
|         } | ||||
|         $this->dispatch('refreshScheduledBackups'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,8 +31,9 @@ class Configuration extends Component | ||||
| 
 | ||||
|         return [ | ||||
|             "echo-private:user.{$userId},ServiceStatusChanged" => 'check_status', | ||||
|             'check_status', | ||||
|             'refreshStatus' => '$refresh', | ||||
|             'check_status', | ||||
|             'refreshServices', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @@ -63,6 +64,13 @@ class Configuration extends Component | ||||
|         $this->databases = $this->service->databases->sort(); | ||||
|     } | ||||
| 
 | ||||
|     public function refreshServices() | ||||
|     { | ||||
|         $this->service->refresh(); | ||||
|         $this->applications = $this->service->applications->sort(); | ||||
|         $this->databases = $this->service->databases->sort(); | ||||
|     } | ||||
| 
 | ||||
|     public function restartApplication($id) | ||||
|     { | ||||
|         try { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ use App\Actions\Database\StopDatabaseProxy; | ||||
| use App\Models\InstanceSettings; | ||||
| use App\Models\ServiceDatabase; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| use Illuminate\Support\Facades\Hash; | ||||
| use Livewire\Component; | ||||
| 
 | ||||
| @@ -83,6 +84,42 @@ class Database extends Component | ||||
|         $this->dispatch('success', 'You need to restart the service for the changes to take effect.'); | ||||
|     } | ||||
| 
 | ||||
|     public function convertToApplication() | ||||
|     { | ||||
|         try { | ||||
|             $service = $this->database->service; | ||||
|             $serviceDatabase = $this->database; | ||||
| 
 | ||||
|             // Check if application with same name already exists
 | ||||
|             if ($service->applications()->where('name', $serviceDatabase->name)->exists()) { | ||||
|                 throw new \Exception('An application with this name already exists.'); | ||||
|             } | ||||
| 
 | ||||
|             // Create new parameters removing database_uuid
 | ||||
|             $redirectParams = collect($this->parameters) | ||||
|                 ->except('database_uuid') | ||||
|                 ->all(); | ||||
| 
 | ||||
|             DB::transaction(function () use ($service, $serviceDatabase) { | ||||
|                 $service->applications()->create([ | ||||
|                     'name' => $serviceDatabase->name, | ||||
|                     'human_name' => $serviceDatabase->human_name, | ||||
|                     'description' => $serviceDatabase->description, | ||||
|                     'exclude_from_status' => $serviceDatabase->exclude_from_status, | ||||
|                     'is_log_drain_enabled' => $serviceDatabase->is_log_drain_enabled, | ||||
|                     'image' => $serviceDatabase->image, | ||||
|                     'service_id' => $service->id, | ||||
|                     'is_migrated' => true, | ||||
|                 ]); | ||||
|                 $serviceDatabase->delete(); | ||||
|             }); | ||||
| 
 | ||||
|             return redirect()->route('project.service.configuration', $redirectParams); | ||||
|         } catch (\Throwable $e) { | ||||
|             return handleError($e, $this); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function instantSave() | ||||
|     { | ||||
|         if ($this->database->is_public && ! $this->database->public_port) { | ||||
|   | ||||
| @@ -24,7 +24,7 @@ class Index extends Component | ||||
| 
 | ||||
|     public $s3s; | ||||
| 
 | ||||
|     protected $listeners = ['generateDockerCompose']; | ||||
|     protected $listeners = ['generateDockerCompose', 'refreshScheduledBackups' => '$refresh']; | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|   | ||||
| @@ -5,6 +5,7 @@ namespace App\Livewire\Project\Service; | ||||
| use App\Models\InstanceSettings; | ||||
| use App\Models\ServiceApplication; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| use Illuminate\Support\Facades\Hash; | ||||
| use Livewire\Component; | ||||
| use Spatie\Url\Url; | ||||
| @@ -73,6 +74,40 @@ class ServiceApplicationView extends Component | ||||
|         $this->parameters = get_route_parameters(); | ||||
|     } | ||||
| 
 | ||||
|     public function convertToDatabase() | ||||
|     { | ||||
|         try { | ||||
|             $service = $this->application->service; | ||||
|             $serviceApplication = $this->application; | ||||
| 
 | ||||
|             // Check if database with same name already exists
 | ||||
|             if ($service->databases()->where('name', $serviceApplication->name)->exists()) { | ||||
|                 throw new \Exception('A database with this name already exists.'); | ||||
|             } | ||||
| 
 | ||||
|             $redirectParams = collect($this->parameters) | ||||
|                 ->except('database_uuid') | ||||
|                 ->all(); | ||||
|             DB::transaction(function () use ($service, $serviceApplication) { | ||||
|                 $service->databases()->create([ | ||||
|                     'name' => $serviceApplication->name, | ||||
|                     'human_name' => $serviceApplication->human_name, | ||||
|                     'description' => $serviceApplication->description, | ||||
|                     'exclude_from_status' => $serviceApplication->exclude_from_status, | ||||
|                     'is_log_drain_enabled' => $serviceApplication->is_log_drain_enabled, | ||||
|                     'image' => $serviceApplication->image, | ||||
|                     'service_id' => $service->id, | ||||
|                     'is_migrated' => true, | ||||
|                 ]); | ||||
|                 $serviceApplication->delete(); | ||||
|             }); | ||||
| 
 | ||||
|             return redirect()->route('project.service.configuration', $redirectParams); | ||||
|         } catch (\Throwable $e) { | ||||
|             return handleError($e, $this); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function submit() | ||||
|     { | ||||
|         try { | ||||
|   | ||||
| @@ -82,6 +82,7 @@ class StackForm extends Component | ||||
|             $this->service->refresh(); | ||||
|             $this->service->saveComposeConfigs(); | ||||
|             $this->dispatch('refreshEnvs'); | ||||
|             $this->dispatch('refreshServices'); | ||||
|             $notify && $this->dispatch('success', 'Service saved.'); | ||||
|         } catch (\Throwable $e) { | ||||
|             return handleError($e, $this); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Andras Bacsai
					Andras Bacsai