From 77a01798226a2636ebe3264b2c42a71d69e8ca58 Mon Sep 17 00:00:00 2001 From: Stuart Rowlands Date: Thu, 8 Feb 2024 19:27:43 +1000 Subject: [PATCH 01/74] Added basic support for post-deployment commands. --- app/Jobs/ApplicationDeploymentJob.php | 25 ++++++++++++++++ app/Livewire/Project/Application/General.php | 2 ++ ...23_add_post_deployment_to_applications.php | 30 +++++++++++++++++++ .../project/application/general.blade.php | 8 +++++ 4 files changed, 65 insertions(+) create mode 100644 database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 9d0e069ce..ca19a3b23 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -257,6 +257,7 @@ public function handle(): void ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED); } } + $this->run_post_deployment_command(); $this->application->isConfigurationChanged(true); } catch (Exception $e) { if ($this->pull_request_id !== 0 && $this->application->is_github_based()) { @@ -1515,6 +1516,30 @@ private function add_build_env_variables_to_dockerfile() ]); } + private function run_post_deployment_command() + { + if (empty($this->application->post_deployment_command)) { + return; + } + $this->application_deployment_queue->addLogEntry("Executing post deployment command: {$this->application->post_deployment_command}"); + + $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); + foreach ($containers as $container) { + $containerName = data_get($container, 'Names'); + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container. '-' . $this->application->uuid)) { + $cmd = 'sh -c "' . str_replace('"', '\"', $this->application->post_deployment_command) . '"'; + $exec = "docker exec {$containerName} {$cmd}"; + $this->execute_remote_command( + [ + executeInDocker($this->deployment_uuid, $exec), 'hidden' => true + ], + ); + return; + } + } + throw new RuntimeException('Post deployment command: Could not find a valid container. Is the container name correct?'); + } + private function next(string $status) { queue_next_deployment($this->application); diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 0f6d61957..277ec23d3 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -66,6 +66,8 @@ class General extends Component 'application.docker_compose_custom_build_command' => 'nullable', 'application.custom_labels' => 'nullable', 'application.custom_docker_run_options' => 'nullable', + 'application.post_deployment_command' => 'nullable', + 'application.post_deployment_command_container' => 'nullable', 'application.settings.is_static' => 'boolean|required', 'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required', 'application.settings.is_build_server_enabled' => 'boolean|required', diff --git a/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php new file mode 100644 index 000000000..ccc86684a --- /dev/null +++ b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php @@ -0,0 +1,30 @@ +string('post_deployment_command')->nullable(); + $table->string('post_deployment_command_container')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('post_deployment_command'); + $table->dropColumn('post_deployment_command_container'); + }); + } +}; diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 3e5e4f137..189265834 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -236,6 +236,14 @@ class="underline" href="https://coolify.io/docs/docker/registry" Reset to Coolify Generated Labels @endif + +

Deployment scripts

+
+ + +
From 0538c2f4789b4615919c12ae1e705f750e978604 Mon Sep 17 00:00:00 2001 From: Stuart Rowlands Date: Thu, 8 Feb 2024 20:02:30 +1000 Subject: [PATCH 02/74] Added pre-deployment support. --- app/Jobs/ApplicationDeploymentJob.php | 29 ++++++++++++++++++- app/Livewire/Project/Application/General.php | 2 ++ ...23_add_post_deployment_to_applications.php | 4 +++ .../project/application/general.blade.php | 8 ++++- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index ca19a3b23..3532f5c84 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -783,8 +783,8 @@ private function prepare_builder_image() [ "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}") ], - ); + $this->run_pre_deployment_command(); } private function deploy_to_additional_destinations() { @@ -1516,6 +1516,33 @@ private function add_build_env_variables_to_dockerfile() ]); } + private function run_pre_deployment_command() + { + if (empty($this->application->pre_deployment_command)) { + return; + } + $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); + if ($containers->count() == 0) { + return; + } + $this->application_deployment_queue->addLogEntry("Executing pre deployment command: {$this->application->post_deployment_command}"); + + foreach ($containers as $container) { + $containerName = data_get($container, 'Names'); + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container. '-' . $this->application->uuid)) { + $cmd = 'sh -c "' . str_replace('"', '\"', $this->application->pre_deployment_command) . '"'; + $exec = "docker exec {$containerName} {$cmd}"; + $this->execute_remote_command( + [ + executeInDocker($this->deployment_uuid, $exec), 'hidden' => true + ], + ); + return; + } + } + throw new RuntimeException('Pre deployment command: Could not find a valid container. Is the container name correct?'); + } + private function run_post_deployment_command() { if (empty($this->application->post_deployment_command)) { diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 277ec23d3..f018e3b9d 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -66,6 +66,8 @@ class General extends Component 'application.docker_compose_custom_build_command' => 'nullable', 'application.custom_labels' => 'nullable', 'application.custom_docker_run_options' => 'nullable', + 'application.pre_deployment_command' => 'nullable', + 'application.pre_deployment_command_container' => 'nullable', 'application.post_deployment_command' => 'nullable', 'application.post_deployment_command_container' => 'nullable', 'application.settings.is_static' => 'boolean|required', diff --git a/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php index ccc86684a..6d3a74896 100644 --- a/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php +++ b/database/migrations/2024_02_08_075523_add_post_deployment_to_applications.php @@ -14,6 +14,8 @@ public function up(): void Schema::table('applications', function (Blueprint $table) { $table->string('post_deployment_command')->nullable(); $table->string('post_deployment_command_container')->nullable(); + $table->string('pre_deployment_command')->nullable(); + $table->string('pre_deployment_command_container')->nullable(); }); } @@ -25,6 +27,8 @@ public function down(): void Schema::table('applications', function (Blueprint $table) { $table->dropColumn('post_deployment_command'); $table->dropColumn('post_deployment_command_container'); + $table->dropColumn('pre_deployment_command'); + $table->dropColumn('pre_deployment_command_container'); }); } }; diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 189265834..f17f80af7 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -238,9 +238,15 @@ class="underline" href="https://coolify.io/docs/docker/registry" @endif

Deployment scripts

+
+ + +
+ helper="An optional script or command to execute in the newly built container after the deployment completes." />
From 365850d922d2ffa3d61123e585ebf95f0c35ce28 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 6 Mar 2024 10:57:39 +0100 Subject: [PATCH 03/74] Update version numbers + add next-image-transformation service --- config/sentry.php | 2 +- config/version.php | 2 +- .../compose/next-image-transformation.yaml | 28 +++++++++++++++++++ templates/service-templates.json | 13 +++++++++ versions.json | 2 +- 5 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 templates/compose/next-image-transformation.yaml diff --git a/config/sentry.php b/config/sentry.php index 9414c5afe..74eb9ba6a 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.236', + 'release' => '4.0.0-beta.237', // 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 e47e67853..73a72d6ad 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Wed, 6 Mar 2024 10:58:08 +0100 Subject: [PATCH 04/74] Update slogans and healthcheck command in next-image-transformation.yaml and service-templates.json --- templates/compose/next-image-transformation.yaml | 4 ++-- templates/service-templates.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/compose/next-image-transformation.yaml b/templates/compose/next-image-transformation.yaml index 3e99245ee..5cefb7b45 100644 --- a/templates/compose/next-image-transformation.yaml +++ b/templates/compose/next-image-transformation.yaml @@ -1,5 +1,5 @@ # documentation: https://github.com/coollabsio/next-image-transformation -# slogan: Self-hosted Next.js Image Transformation Service +# slogan: Drop-in replacement for Vercel's Nextjs image optimization service. # tags: nextjs,image,transformation,service services: @@ -22,7 +22,7 @@ services: - IMGPROXY_JPEG_PROGRESSIVE=true - IMGPROXY_USE_ETAG=true healthcheck: - test: [ "CMD", "imgproxy", "health" ] + test: ["CMD", "imgproxy", "health"] interval: 2s timeout: 10s retries: 5 diff --git a/templates/service-templates.json b/templates/service-templates.json index 47efa21e5..2c0ee7754 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -514,7 +514,7 @@ }, "next-image-transformation": { "documentation": "https:\/\/github.com\/coollabsio\/next-image-transformation", - "slogan": "Self-hosted Next.js Image Transformation Service", + "slogan": "Drop-in replacement for Vercel's Nextjs image optimization service.", "compose": "c2VydmljZXM6CiAgbmV4dC1pbWFnZS10cmFuc2Zvcm1hdGlvbjoKICAgIGltYWdlOiAnZ2hjci5pby9jb29sbGFic2lvL25leHQtaW1hZ2UtdHJhbnNmb3JtYXRpb246bGF0ZXN0JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1RSQU5TRk9STUFUSU9OCiAgICAgIC0gTk9ERV9FTlY9cHJvZHVjdGlvbgogICAgICAtICdBTExPV0VEX1JFTU9URV9ET01BSU5TPSR7QUxMT1dFRF9SRU1PVEVfRE9NQUlOUzotKn0nCiAgICAgIC0gJ0lNR1BST1hZX1VSTD0ke0lNR1BST1hZX1VSTDotaHR0cDovL2ltZ3Byb3h5OjgwODB9JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6ICd3Z2V0IC1xTy0gaHR0cDovL2xvY2FsaG9zdDozMDAwL2hlYWx0aCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQogIGltZ3Byb3h5OgogICAgaW1hZ2U6IGRhcnRoc2ltL2ltZ3Byb3h5CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBJTUdQUk9YWV9FTkFCTEVfV0VCUF9ERVRFQ1RJT049dHJ1ZQogICAgICAtIElNR1BST1hZX0pQRUdfUFJPR1JFU1NJVkU9dHJ1ZQogICAgICAtIElNR1BST1hZX1VTRV9FVEFHPXRydWUKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBpbWdwcm94eQogICAgICAgIC0gaGVhbHRoCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogNQo=", "tags": [ "nextjs", From e699103d3e5fdaad5f9f3d4a8814aa807949b734 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 6 Mar 2024 15:34:21 +0100 Subject: [PATCH 05/74] Update application names and base directories --- database/seeders/ApplicationSeeder.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php index 276c5d53c..34a54c8eb 100644 --- a/database/seeders/ApplicationSeeder.php +++ b/database/seeders/ApplicationSeeder.php @@ -15,12 +15,12 @@ class ApplicationSeeder extends Seeder public function run(): void { Application::create([ - 'name' => 'coollabsio/coolify-examples:nodejs-fastify', - 'description' => 'NodeJS Fastify Example', + 'name' => 'NodeJS Fastify Example', 'fqdn' => 'http://nodejs.127.0.0.1.sslip.io', 'repository_project_id' => 603035348, 'git_repository' => 'coollabsio/coolify-examples', - 'git_branch' => 'nodejs-fastify', + 'git_branch' => 'main', + 'base_directory' => '/nodejs', 'build_pack' => 'nixpacks', 'ports_exposes' => '3000', 'environment_id' => 1, @@ -30,12 +30,12 @@ public function run(): void 'source_type' => GithubApp::class ]); Application::create([ - 'name' => 'coollabsio/coolify-examples:dockerfile', - 'description' => 'Dockerfile Example', + 'name' => 'Dockerfile Example', 'fqdn' => 'http://dockerfile.127.0.0.1.sslip.io', 'repository_project_id' => 603035348, 'git_repository' => 'coollabsio/coolify-examples', - 'git_branch' => 'dockerfile', + 'git_branch' => 'main', + 'base_directory' => '/dockerfile', 'build_pack' => 'dockerfile', 'ports_exposes' => '80', 'environment_id' => 1, @@ -45,8 +45,7 @@ public function run(): void 'source_type' => GithubApp::class ]); Application::create([ - 'name' => 'pure-dockerfile', - 'description' => 'Pure Dockerfile Example', + 'name' => 'Pure Dockerfile Example', 'fqdn' => 'http://pure-dockerfile.127.0.0.1.sslip.io', 'git_repository' => 'coollabsio/coolify', 'git_branch' => 'main', From f1b00436aae35bd511f87f1202e611055a8f47de Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 09:56:09 +0100 Subject: [PATCH 06/74] Update link target in stack-form.blade.php --- resources/views/livewire/project/service/stack-form.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/project/service/stack-form.blade.php b/resources/views/livewire/project/service/stack-form.blade.php index b9593087b..8e4535d90 100644 --- a/resources/views/livewire/project/service/stack-form.blade.php +++ b/resources/views/livewire/project/service/stack-form.blade.php @@ -16,7 +16,7 @@
+ helper="By default, you do not reach the Coolify defined networks.
Starting a docker compose based resource will have an internal network.
If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.

For more information, check this." />
@if ($fields)
From a6669ed87634bc326994f684f3a3e91960441f4f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 09:59:19 +0100 Subject: [PATCH 07/74] Update version and add check for Docker installed via snap --- scripts/install.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/install.sh b/scripts/install.sh index 79e71596a..ad2a8de22 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -6,7 +6,7 @@ set -e # Exit immediately if a command exits with a non-zero status #set -u # Treat unset variables as an error and exit set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status -VERSION="1.2.2" +VERSION="1.2.3" DOCKER_VERSION="24.0" CDN="https://cdn.coollabs.io/coolify" @@ -122,6 +122,16 @@ if [ "$SSH_PERMIT_ROOT_LOGIN" != "true" ]; then echo "###############################################################################" fi +# Detect if docker is installed via snap +if [ -x "$(command -v snap)" ]; then + if snap list | grep -q docker; then + echo "Docker is installed via snap." + echo "Please note that Coolify does not support Docker installed via snap." + echo "Please remove Docker with snap (snap remove docker) and reexecute this script." + exit 1 + fi +fi + if ! [ -x "$(command -v docker)" ]; then if [ "$OS_TYPE" == 'almalinux' ]; then dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo From 051a1405e7150dcb5ef9523b42bafb226f5434b6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 10:27:21 +0100 Subject: [PATCH 08/74] Refactor backup execution and cleanup functionality --- app/Livewire/Project/Database/Backup/Execution.php | 8 +++++++- app/Livewire/Project/Database/BackupExecutions.php | 4 ++-- .../livewire/project/database/backup/execution.blade.php | 5 ++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/Livewire/Project/Database/Backup/Execution.php b/app/Livewire/Project/Database/Backup/Execution.php index 07f7db03c..1f790d643 100644 --- a/app/Livewire/Project/Database/Backup/Execution.php +++ b/app/Livewire/Project/Database/Backup/Execution.php @@ -10,7 +10,8 @@ class Execution extends Component public $backup; public $executions; public $s3s; - public function mount() { + public function mount() + { $backup_uuid = request()->route('backup_uuid'); $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { @@ -34,6 +35,11 @@ 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 c78a8cbee..5484dfdc8 100644 --- a/app/Livewire/Project/Database/BackupExecutions.php +++ b/app/Livewire/Project/Database/BackupExecutions.php @@ -34,7 +34,7 @@ public function deleteBackup($exeuctionId) } $execution->delete(); $this->dispatch('success', 'Backup deleted.'); - $this->dispatch('refreshBackupExecutions'); + $this->refreshBackupExecutions(); } public function download($exeuctionId) { @@ -65,6 +65,6 @@ public function download($exeuctionId) } public function refreshBackupExecutions(): void { - $this->executions = data_get($this->backup, 'executions', []); + $this->executions = $this->backup->executions()->get()->sortByDesc('created_at'); } } diff --git a/resources/views/livewire/project/database/backup/execution.blade.php b/resources/views/livewire/project/database/backup/execution.blade.php index 437186b60..f63ed2be7 100644 --- a/resources/views/livewire/project/database/backup/execution.blade.php +++ b/resources/views/livewire/project/database/backup/execution.blade.php @@ -13,7 +13,10 @@
-

Executions

+
+

Executions

+ Cleanup Failed Backups +
From 7a21312daf04522b6a1c2d0d1b7b6461b77b5e17 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 11:14:03 +0100 Subject: [PATCH 09/74] feat: domains api endpoint --- app/Http/Controllers/Api/Domains.php | 55 ++++++++++++++++++++++++++++ app/Models/Project.php | 4 ++ routes/api.php | 2 + 3 files changed, 61 insertions(+) create mode 100644 app/Http/Controllers/Api/Domains.php diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php new file mode 100644 index 000000000..080f5aa4f --- /dev/null +++ b/app/Http/Controllers/Api/Domains.php @@ -0,0 +1,55 @@ +json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + } + $projects = ModelsProject::where('team_id', $teamId)->get(); + $domains = collect(); + $applications = $projects->pluck('applications')->flatten(); + if ($applications->count() > 0) { + foreach ($applications as $application) { + $ip = $application->destination->server->ip; + $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) { + return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', ''); + }); + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); + } + } + $services = $projects->pluck('services')->flatten(); + if ($services->count() > 0) { + foreach ($services as $service) { + $service_applications = $service->applications; + if ($service_applications->count() > 0) { + foreach ($service_applications as $application) { + $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) { + return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', ''); + }); + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); + } + } + } + } + $domains = $domains->groupBy('ip')->map(function ($domain) { + return $domain->pluck('domain')->flatten(); + }); + + return response()->json($domains); + } +} diff --git a/app/Models/Project.php b/app/Models/Project.php index b9afc7426..6416971f6 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -45,6 +45,10 @@ public function team() return $this->belongsTo(Team::class); } + public function services() + { + return $this->hasManyThrough(Service::class, Environment::class); + } public function applications() { return $this->hasManyThrough(Application::class, Environment::class); diff --git a/routes/api.php b/routes/api.php index 4ef5500f8..a1ddd666e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,7 @@ Date: Thu, 7 Mar 2024 11:25:15 +0100 Subject: [PATCH 10/74] Refactor domain grouping in domains API controller --- app/Http/Controllers/Api/Domains.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php index 080f5aa4f..8151a6c87 100644 --- a/app/Http/Controllers/Api/Domains.php +++ b/app/Http/Controllers/Api/Domains.php @@ -48,7 +48,12 @@ public function domains(Request $request) } $domains = $domains->groupBy('ip')->map(function ($domain) { return $domain->pluck('domain')->flatten(); - }); + })->map(function ($domain, $ip) { + return [ + 'ip' => $ip, + 'domains' => $domain, + ]; + })->values(); return response()->json($domains); } From c7693d0ec34de1e942cacfa5b7ab5d87a430af38 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 11:35:00 +0100 Subject: [PATCH 11/74] feat: resources api endpoint --- app/Http/Controllers/Api/Resources.php | 30 ++++++++++++++++++++++++++ app/Models/Project.php | 6 ++++-- routes/api.php | 8 ++++--- 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 app/Http/Controllers/Api/Resources.php diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php new file mode 100644 index 000000000..a6e25758c --- /dev/null +++ b/app/Http/Controllers/Api/Resources.php @@ -0,0 +1,30 @@ +json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + } + $projects = Project::where('team_id', $teamId)->get(); + $resources = collect(); + $resources->push($projects->pluck('applications')->flatten()); + $resources->push($projects->pluck('services')->flatten()); + $resources->push($projects->pluck('postgresqls')->flatten()); + $resources->push($projects->pluck('redis')->flatten()); + $resources->push($projects->pluck('mongodbs')->flatten()); + $resources->push($projects->pluck('mysqls')->flatten()); + $resources->push($projects->pluck('mariadbs')->flatten()); + $resources = $resources->flatten(); + return response()->json($resources); + } + +} diff --git a/app/Models/Project.php b/app/Models/Project.php index 6416971f6..27ae10778 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -27,7 +27,8 @@ protected static function booted() $project->settings()->delete(); }); } - public function environment_variables() { + public function environment_variables() + { return $this->hasMany(SharedEnvironmentVariable::class); } public function environments() @@ -74,7 +75,8 @@ public function mariadbs() { return $this->hasManyThrough(StandaloneMariadb::class, Environment::class); } - public function resource_count() { + public function resource_count() + { return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count(); } } diff --git a/routes/api.php b/routes/api.php index a1ddd666e..ae60e5b7b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Api\Deploy; use App\Http\Controllers\Api\Domains; use App\Http\Controllers\Api\Project; +use App\Http\Controllers\Api\Resources; use App\Http\Controllers\Api\Server; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; @@ -32,10 +33,11 @@ Route::get('/deploy', [Deploy::class, 'deploy']); Route::get('/servers', [Server::class, 'servers']); Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']); - Route::get('/projects', [Project::class, 'projects']); - Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']); - Route::get('/project/{uuid}/{environment_name}', [Project::class, 'environment_details']); + Route::get('/resources', [Resources::class, 'resources']); Route::get('/domains', [Domains::class, 'domains']); + //Route::get('/projects', [Project::class, 'projects']); + //Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']); + //Route::get('/project/{uuid}/{environment_name}', [Project::class, 'environment_details']); }); Route::get('/{any}', function () { From db24828a5a1dae83a94c671974251b3b13aecbbe Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 11:37:56 +0100 Subject: [PATCH 12/74] Refactor resource retrieval in API controller --- app/Http/Controllers/Api/Resources.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php index a6e25758c..78ea854c7 100644 --- a/app/Http/Controllers/Api/Resources.php +++ b/app/Http/Controllers/Api/Resources.php @@ -18,11 +18,9 @@ public function resources(Request $request) $resources = collect(); $resources->push($projects->pluck('applications')->flatten()); $resources->push($projects->pluck('services')->flatten()); - $resources->push($projects->pluck('postgresqls')->flatten()); - $resources->push($projects->pluck('redis')->flatten()); - $resources->push($projects->pluck('mongodbs')->flatten()); - $resources->push($projects->pluck('mysqls')->flatten()); - $resources->push($projects->pluck('mariadbs')->flatten()); + foreach (collect(DATABASE_TYPES) as $db) { + $resources->push($projects->pluck(str($db)->plural(2))->flatten()); + } $resources = $resources->flatten(); return response()->json($resources); } From 038f65aae6980c893c0d642b59fee2b276925ba6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 11:42:16 +0100 Subject: [PATCH 13/74] Add InstanceSettings model and update IP handling in domains controller --- app/Http/Controllers/Api/Domains.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php index 8151a6c87..f96765397 100644 --- a/app/Http/Controllers/Api/Domains.php +++ b/app/Http/Controllers/Api/Domains.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; +use App\Models\InstanceSettings; use App\Models\Project as ModelsProject; use Illuminate\Http\Request; @@ -17,9 +18,13 @@ public function domains(Request $request) $projects = ModelsProject::where('team_id', $teamId)->get(); $domains = collect(); $applications = $projects->pluck('applications')->flatten(); + $settings = InstanceSettings::get(); if ($applications->count() > 0) { foreach ($applications as $application) { $ip = $application->destination->server->ip; + if ($ip === 'host.docker.internal') { + $ip = $settings->ipv4 || $settings->ipv6 || 'host.docker.internal'; + } $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) { return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', ''); }); From 7aa8c765f6563b781db0baa22bdbf39fcee1931c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 11:49:15 +0100 Subject: [PATCH 14/74] Refactor domain IP handling in Domains controller --- app/Http/Controllers/Api/Domains.php | 65 +++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php index f96765397..4b8a026fb 100644 --- a/app/Http/Controllers/Api/Domains.php +++ b/app/Http/Controllers/Api/Domains.php @@ -22,16 +22,36 @@ public function domains(Request $request) if ($applications->count() > 0) { foreach ($applications as $application) { $ip = $application->destination->server->ip; - if ($ip === 'host.docker.internal') { - $ip = $settings->ipv4 || $settings->ipv6 || 'host.docker.internal'; - } $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) { return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', ''); }); - $domains->push([ - 'domain' => $fqdn, - 'ip' => $ip, - ]); + if ($ip === 'host.docker.internal') { + if ($settings->public_ipv4) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $settings->public_ipv4, + ]); + } + if ($settings->public_ipv6) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $settings->public_ipv6, + ]); + } + if (!$settings->public_ipv4 && !$settings->public_ipv6) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); + } + } else { + if (!$settings->public_ipv4 && !$settings->public_ipv6) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); + } + } } } $services = $projects->pluck('services')->flatten(); @@ -43,10 +63,33 @@ public function domains(Request $request) $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) { return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', ''); }); - $domains->push([ - 'domain' => $fqdn, - 'ip' => $ip, - ]); + if ($ip === 'host.docker.internal') { + if ($settings->public_ipv4) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $settings->public_ipv4, + ]); + } + if ($settings->public_ipv6) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $settings->public_ipv6, + ]); + } + if (!$settings->public_ipv4 && !$settings->public_ipv6) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); + } + } else { + if (!$settings->public_ipv4 && !$settings->public_ipv6) { + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); + } + } } } } From 2a03b452d3d0fc90f5708470ff1548dc92928e02 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 12:01:21 +0100 Subject: [PATCH 15/74] feat: team api endpoint --- app/Http/Controllers/Api/Team.php | 64 +++++++++++++++++++++++++++++++ routes/api.php | 8 ++++ 2 files changed, 72 insertions(+) create mode 100644 app/Http/Controllers/Api/Team.php diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php new file mode 100644 index 000000000..d99ea8ea7 --- /dev/null +++ b/app/Http/Controllers/Api/Team.php @@ -0,0 +1,64 @@ +json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + } + $teams = auth()->user()->teams; + return response()->json($teams); + } + public function team_by_id(Request $request) + { + $id = $request->id; + $teamId = get_team_id_from_token(); + if (is_null($teamId)) { + return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + } + $teams = auth()->user()->teams; + $team = $teams->where('id', $id)->first(); + if (is_null($team)) { + return response()->json(['error' => 'Team not found.'], 404); + } + return response()->json($team); + } + public function members_by_id(Request $request) + { + $id = $request->id; + $teamId = get_team_id_from_token(); + if (is_null($teamId)) { + return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + } + $teams = auth()->user()->teams; + $team = $teams->where('id', $id)->first(); + if (is_null($team)) { + return response()->json(['error' => 'Team not found.'], 404); + } + return response()->json($team->members); + } + public function current_team(Request $request) + { + $teamId = get_team_id_from_token(); + if (is_null($teamId)) { + return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + } + $team = auth()->user()->currentTeam(); + return response()->json($team); + } + public function current_team_members(Request $request) { + $teamId = get_team_id_from_token(); + if (is_null($teamId)) { + return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + } + $team = auth()->user()->currentTeam(); + return response()->json($team->members); + } +} diff --git a/routes/api.php b/routes/api.php index ae60e5b7b..b27d1fb28 100644 --- a/routes/api.php +++ b/routes/api.php @@ -5,6 +5,7 @@ use App\Http\Controllers\Api\Project; use App\Http\Controllers\Api\Resources; use App\Http\Controllers\Api\Server; +use App\Http\Controllers\Api\Team; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Route; @@ -35,6 +36,13 @@ Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']); Route::get('/resources', [Resources::class, 'resources']); Route::get('/domains', [Domains::class, 'domains']); + Route::get('/teams', [Team::class, 'teams']); + Route::get('/team/current', [Team::class, 'current_team']); + Route::get('/team/current/members', [Team::class, 'current_team_members']); + Route::get('/team/{id}', [Team::class, 'team_by_id']); + Route::get('/team/{id}/members', [Team::class, 'members_by_id']); + + //Route::get('/projects', [Project::class, 'projects']); //Route::get('/project/{uuid}', [Project::class, 'project_by_uuid']); //Route::get('/project/{uuid}/{environment_name}', [Project::class, 'environment_details']); From 515d40174685901d986d027a673fcaf80bb24bc1 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 12:22:18 +0100 Subject: [PATCH 16/74] feat: add deployment details to deploy endpoint --- app/Http/Controllers/Api/Deploy.php | 35 ++++++++++++++++++++++------- routes/api.php | 3 +++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php index 21da51d66..1173bd5d2 100644 --- a/app/Http/Controllers/Api/Deploy.php +++ b/app/Http/Controllers/Api/Deploy.php @@ -45,15 +45,24 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400); } $message = collect([]); + $deployments = collect(); + $payload = collect(); foreach ($uuids as $uuid) { $resource = getResourceByUuid($uuid, $teamId); if ($resource) { - $return_message = $this->deploy_resource($resource, $force); + ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force); + if ($deployment_uuid) { + $deployments->push(['resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]); + } $message = $message->merge($return_message); } } if ($message->count() > 0) { - return response()->json(['message' => $message->toArray()], 200); + $payload->put('message', $message->toArray()); + if ($deployments->count() > 0) { + $payload->put('details', $deployments->toArray()); + } + return response()->json($payload->toArray(), 200); } return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404); } @@ -66,6 +75,8 @@ public function by_tags(string $tags, int $team_id, bool $force = false) return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400); } $message = collect([]); + $deployments = collect(); + $payload = collect(); foreach ($tags as $tag) { $found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first(); if (!$found_tag) { @@ -79,21 +90,28 @@ public function by_tags(string $tags, int $team_id, bool $force = false) continue; } foreach ($applications as $resource) { - $return_message = $this->deploy_resource($resource, $force); + ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force); + if ($deployment_uuid) { + $deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]); + } $message = $message->merge($return_message); } foreach ($services as $resource) { - $return_message = $this->deploy_resource($resource, $force); + ['message' => $return_message] = $this->deploy_resource($resource, $force); $message = $message->merge($return_message); } } if ($message->count() > 0) { - return response()->json(['message' => $message->toArray()], 200); + $payload->put('message', $message->toArray()); + if ($deployments->count() > 0) { + $payload->put('details', $deployments->toArray()); + } + return response()->json($payload->toArray(), 200); } return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404); } - public function deploy_resource($resource, bool $force = false): Collection + public function deploy_resource($resource, bool $force = false): array { $message = collect([]); if (gettype($resource) !== 'object') { @@ -101,9 +119,10 @@ public function deploy_resource($resource, bool $force = false): Collection } $type = $resource?->getMorphClass(); if ($type === 'App\Models\Application') { + $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $resource, - deployment_uuid: new Cuid2(7), + deployment_uuid: $deployment_uuid, force_rebuild: $force, ); $message->push("Application {$resource->name} deployment queued."); @@ -156,6 +175,6 @@ public function deploy_resource($resource, bool $force = false): Collection StartService::run($resource); $message->push("Service {$resource->name} started. It could take a while, be patient."); } - return $message; + return ['message' => $message, 'deployment_uuid' => $deployment_uuid]; } } diff --git a/routes/api.php b/routes/api.php index b27d1fb28..25f4f948f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -32,10 +32,13 @@ return response(config('version')); }); Route::get('/deploy', [Deploy::class, 'deploy']); + Route::get('/servers', [Server::class, 'servers']); Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']); + Route::get('/resources', [Resources::class, 'resources']); Route::get('/domains', [Domains::class, 'domains']); + Route::get('/teams', [Team::class, 'teams']); Route::get('/team/current', [Team::class, 'current_team']); Route::get('/team/current/members', [Team::class, 'current_team_members']); From c7f15c42fa33320e333003c67b13b283e06e3f98 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 12:27:23 +0100 Subject: [PATCH 17/74] feat: add deployments api --- app/Http/Controllers/Api/Deploy.php | 23 +++++++++++++++++++++-- app/Http/Controllers/Api/Domains.php | 2 +- app/Http/Controllers/Api/Project.php | 6 +++--- app/Http/Controllers/Api/Resources.php | 2 +- app/Http/Controllers/Api/Server.php | 4 ++-- app/Http/Controllers/Api/Team.php | 10 +++++----- bootstrap/helpers/api.php | 4 ++++ routes/api.php | 1 + 8 files changed, 38 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php index 1173bd5d2..70753f328 100644 --- a/app/Http/Controllers/Api/Deploy.php +++ b/app/Http/Controllers/Api/Deploy.php @@ -9,13 +9,32 @@ use App\Actions\Database\StartRedis; use App\Actions\Service\StartService; use App\Http\Controllers\Controller; +use App\Models\ApplicationDeploymentQueue; +use App\Models\Server; use App\Models\Tag; use Illuminate\Http\Request; -use Illuminate\Support\Collection; use Visus\Cuid2\Cuid2; class Deploy extends Controller { + public function deployments(Request $request) { + $teamId = get_team_id_from_token(); + if (is_null($teamId)) { + return invalid_token(); + } + $servers = Server::whereTeamId($teamId)->get(); + $deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $servers->pluck("id"))->get([ + "id", + "application_id", + "application_name", + "deployment_url", + "pull_request_id", + "server_name", + "server_id", + "status" + ])->sortBy('id')->toArray(); + return response()->json($deployments_per_server, 200); + } public function deploy(Request $request) { $teamId = get_team_id_from_token(); @@ -27,7 +46,7 @@ public function deploy(Request $request) return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400); } if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } if ($tags) { return $this->by_tags($tags, $teamId, $force); diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php index 4b8a026fb..15db5b6ef 100644 --- a/app/Http/Controllers/Api/Domains.php +++ b/app/Http/Controllers/Api/Domains.php @@ -13,7 +13,7 @@ public function domains(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $projects = ModelsProject::where('team_id', $teamId)->get(); $domains = collect(); diff --git a/app/Http/Controllers/Api/Project.php b/app/Http/Controllers/Api/Project.php index fa2ba34bb..45d6b4059 100644 --- a/app/Http/Controllers/Api/Project.php +++ b/app/Http/Controllers/Api/Project.php @@ -12,7 +12,7 @@ public function projects(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get(); return response()->json($projects); @@ -21,7 +21,7 @@ public function project_by_uuid(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']); return response()->json($project); @@ -30,7 +30,7 @@ public function environment_details(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); $environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']); diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php index 78ea854c7..d3d313f3a 100644 --- a/app/Http/Controllers/Api/Resources.php +++ b/app/Http/Controllers/Api/Resources.php @@ -12,7 +12,7 @@ public function resources(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $projects = Project::where('team_id', $teamId)->get(); $resources = collect(); diff --git a/app/Http/Controllers/Api/Server.php b/app/Http/Controllers/Api/Server.php index 2cfec183e..bb5ef255b 100644 --- a/app/Http/Controllers/Api/Server.php +++ b/app/Http/Controllers/Api/Server.php @@ -12,7 +12,7 @@ public function servers(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) { $server['is_reachable'] = $server->settings->is_reachable; @@ -26,7 +26,7 @@ public function server_by_uuid(Request $request) $with_resources = $request->query('resources'); $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $server = ModelsServer::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); if (is_null($server)) { diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php index d99ea8ea7..862d2e185 100644 --- a/app/Http/Controllers/Api/Team.php +++ b/app/Http/Controllers/Api/Team.php @@ -11,7 +11,7 @@ public function teams(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $teams = auth()->user()->teams; return response()->json($teams); @@ -21,7 +21,7 @@ public function team_by_id(Request $request) $id = $request->id; $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $teams = auth()->user()->teams; $team = $teams->where('id', $id)->first(); @@ -35,7 +35,7 @@ public function members_by_id(Request $request) $id = $request->id; $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $teams = auth()->user()->teams; $team = $teams->where('id', $id)->first(); @@ -48,7 +48,7 @@ public function current_team(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $team = auth()->user()->currentTeam(); return response()->json($team); @@ -56,7 +56,7 @@ public function current_team(Request $request) public function current_team_members(Request $request) { $teamId = get_team_id_from_token(); if (is_null($teamId)) { - return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); + return invalid_token(); } $team = auth()->user()->currentTeam(); return response()->json($team->members); diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index 94e9242cb..4fcdbac4f 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -5,3 +5,7 @@ function get_team_id_from_token() $token = auth()->user()->currentAccessToken(); return data_get($token, 'team_id'); } +function invalid_token() +{ + return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400); +} diff --git a/routes/api.php b/routes/api.php index 25f4f948f..c0ea836a6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -32,6 +32,7 @@ return response(config('version')); }); Route::get('/deploy', [Deploy::class, 'deploy']); + Route::get('/deployments', [Deploy::class, 'deployments']); Route::get('/servers', [Server::class, 'servers']); Route::get('/server/{uuid}', [Server::class, 'server_by_uuid']); From 9d31d990fc8b5f140d344fc6dde86db1d385b0df Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 12:35:38 +0100 Subject: [PATCH 18/74] Update error message for missing resources --- app/Http/Controllers/Api/Deploy.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php index 70753f328..bdea30029 100644 --- a/app/Http/Controllers/Api/Deploy.php +++ b/app/Http/Controllers/Api/Deploy.php @@ -99,7 +99,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) foreach ($tags as $tag) { $found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first(); if (!$found_tag) { - $message->push("Tag {$tag} not found."); + // $message->push("Tag {$tag} not found."); continue; } $applications = $found_tag->applications()->get(); @@ -120,6 +120,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) $message = $message->merge($return_message); } } + ray($message); if ($message->count() > 0) { $payload->put('message', $message->toArray()); if ($deployments->count() > 0) { @@ -128,7 +129,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) return response()->json($payload->toArray(), 200); } - return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404); + return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404); } public function deploy_resource($resource, bool $force = false): array { From bbfbd4a1055b928ce0626118ddcc989acf81fbe5 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 12:36:49 +0100 Subject: [PATCH 19/74] Refactor domain handling in API controller --- app/Http/Controllers/Api/Domains.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php index 15db5b6ef..f6468cdf0 100644 --- a/app/Http/Controllers/Api/Domains.php +++ b/app/Http/Controllers/Api/Domains.php @@ -45,12 +45,10 @@ public function domains(Request $request) ]); } } else { - if (!$settings->public_ipv4 && !$settings->public_ipv6) { - $domains->push([ - 'domain' => $fqdn, - 'ip' => $ip, - ]); - } + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); } } } @@ -83,12 +81,10 @@ public function domains(Request $request) ]); } } else { - if (!$settings->public_ipv4 && !$settings->public_ipv6) { - $domains->push([ - 'domain' => $fqdn, - 'ip' => $ip, - ]); - } + $domains->push([ + 'domain' => $fqdn, + 'ip' => $ip, + ]); } } } From 129a644781fe56682a8072e8fbee9647c7249dd9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 12:58:04 +0100 Subject: [PATCH 20/74] ui: make notifications separate view fix: popup if no notifications are set --- app/Livewire/LayoutPopups.php | 6 +++- app/Models/Team.php | 19 +++++++++++-- ...add_notifications_notification_disable.php | 28 +++++++++++++++++++ resources/views/components/navbar.blade.php | 12 +++++++- .../components/notifications/navbar.blade.php | 25 +++++++++++++++++ .../views/components/team/navbar.blade.php | 11 +++----- .../views/livewire/layout-popups.blade.php | 15 +++++++++- .../team/notification/index.blade.php | 2 +- routes/web.php | 4 ++- 9 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 database/migrations/2024_03_07_115054_add_notifications_notification_disable.php create mode 100644 resources/views/components/notifications/navbar.blade.php diff --git a/app/Livewire/LayoutPopups.php b/app/Livewire/LayoutPopups.php index dd7f14678..b6f06f808 100644 --- a/app/Livewire/LayoutPopups.php +++ b/app/Livewire/LayoutPopups.php @@ -17,10 +17,14 @@ public function testEvent() { $this->dispatch('success', 'Realtime events configured!'); } - public function disable() + public function disableSponsorship() { auth()->user()->update(['is_notification_sponsorship_enabled' => false]); } + public function disableNotifications() + { + auth()->user()->update(['is_notification_notifications_enabled' => false]); + } public function render() { return view('livewire.layout-popups'); diff --git a/app/Models/Team.php b/app/Models/Team.php index 656c4009b..7cb1601de 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -48,13 +48,15 @@ public function getRecepients($notification) } return explode(',', $recipients); } - static public function serverLimitReached() { + static public function serverLimitReached() + { $serverLimit = Team::serverLimit(); $team = currentTeam(); $servers = $team->servers->count(); return $servers >= $serverLimit; } - public function serverOverflow() { + public function serverOverflow() + { if ($this->serverLimit() < $this->servers->count()) { return true; } @@ -170,4 +172,17 @@ public function trialEndedButSubscribed() ]); } } + public function isAnyNotificationEnabled() + { + if (isCloud()) { + return true; + } + if (!data_get(auth()->user(), 'is_notification_notifications_enabled')) { + return true; + } + if ($this->smtp_enabled || $this->resend_enabled || $this->discord_enabled || $this->telegram_enabled || $this->use_instance_email_settings) { + return true; + } + return false; + } } diff --git a/database/migrations/2024_03_07_115054_add_notifications_notification_disable.php b/database/migrations/2024_03_07_115054_add_notifications_notification_disable.php new file mode 100644 index 000000000..8633b971e --- /dev/null +++ b/database/migrations/2024_03_07_115054_add_notifications_notification_disable.php @@ -0,0 +1,28 @@ +boolean('is_notification_notifications_enabled')->default(true); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('is_notification_notifications_enabled'); + }); + } +}; diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index 3bd1730be..65d2f0736 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -150,7 +150,17 @@ class="{{ request()->is('subscription*') ? 'text-warning icon' : 'icon' }}" @endif - +
  • + + + + + Notifications + +
  • @if (isInstanceAdmin())
  • diff --git a/resources/views/components/notifications/navbar.blade.php b/resources/views/components/notifications/navbar.blade.php new file mode 100644 index 000000000..65b374249 --- /dev/null +++ b/resources/views/components/notifications/navbar.blade.php @@ -0,0 +1,25 @@ + diff --git a/resources/views/components/team/navbar.blade.php b/resources/views/components/team/navbar.blade.php index 971b9de85..a3f13b4af 100644 --- a/resources/views/components/team/navbar.blade.php +++ b/resources/views/components/team/navbar.blade.php @@ -1,7 +1,7 @@
    @endif @@ -20,4 +21,16 @@ class="text-white underline">/subscription to update your subscription or re @endif + @if (!currentTeam()->isAnyNotificationEnabled()) +
    +
    + WARNING: No notifications enabled.

    It is highly recommended to enable at least + one + notification channel to receive important alerts.
    Visit /notification to enable notifications.
    + Disable This + Popup +
    +
    + @endif diff --git a/resources/views/livewire/team/notification/index.blade.php b/resources/views/livewire/team/notification/index.blade.php index ed961deb0..e5eaffe17 100644 --- a/resources/views/livewire/team/notification/index.blade.php +++ b/resources/views/livewire/team/notification/index.blade.php @@ -1,5 +1,5 @@
    - +

    Notifications

    diff --git a/routes/web.php b/routes/web.php index f49d1b278..c442f6c75 100644 --- a/routes/web.php +++ b/routes/web.php @@ -121,11 +121,13 @@ Route::get('/', TagsIndex::class)->name('tags.index'); Route::get('/{tag_name}', TagsShow::class)->name('tags.show'); }); + Route::prefix('notifications')->group(function () { + Route::get('/', TeamNotificationIndex::class)->name('notification.index'); + }); Route::prefix('team')->group(function () { Route::get('/', TeamIndex::class)->name('team.index'); Route::get('/new', TeamCreate::class)->name('team.create'); Route::get('/members', TeamMemberIndex::class)->name('team.member.index'); - Route::get('/notifications', TeamNotificationIndex::class)->name('team.notification.index'); Route::get('/shared-variables', TeamSharedVariablesIndex::class)->name('team.shared-variables.index'); Route::get('/storages', TeamStorageIndex::class)->name('team.storage.index'); Route::get('/storages/new', TeamStorageCreate::class)->name('team.storage.create'); From 8b74e50c50d9ea112cd8b497ae48eacfa318e906 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 13:05:04 +0100 Subject: [PATCH 21/74] Add two-factor authentication fields to hidden array in User model --- app/Models/User.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Models/User.php b/app/Models/User.php index d6389d678..e2ecae56a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -26,6 +26,8 @@ class User extends Authenticatable implements SendsEmail protected $hidden = [ 'password', 'remember_token', + 'two_factor_recovery_codes', + 'two_factor_secret', ]; protected $casts = [ 'email_verified_at' => 'datetime', From 93e4e723fab8b3d5679a39bd89db6ce4b09afb6c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 15:45:49 +0100 Subject: [PATCH 22/74] Add documentation links to error responses in Team controller --- app/Http/Controllers/Api/Team.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php index 862d2e185..540ac282a 100644 --- a/app/Http/Controllers/Api/Team.php +++ b/app/Http/Controllers/Api/Team.php @@ -26,7 +26,7 @@ public function team_by_id(Request $request) $teams = auth()->user()->teams; $team = $teams->where('id', $id)->first(); if (is_null($team)) { - return response()->json(['error' => 'Team not found.'], 404); + return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id"], 404); } return response()->json($team); } @@ -40,7 +40,7 @@ public function members_by_id(Request $request) $teams = auth()->user()->teams; $team = $teams->where('id', $id)->first(); if (is_null($team)) { - return response()->json(['error' => 'Team not found.'], 404); + return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-members"], 404); } return response()->json($team->members); } @@ -53,7 +53,8 @@ public function current_team(Request $request) $team = auth()->user()->currentTeam(); return response()->json($team); } - public function current_team_members(Request $request) { + public function current_team_members(Request $request) + { $teamId = get_team_id_from_token(); if (is_null($teamId)) { return invalid_token(); From 6688120aeebb7a2316763623854c85335f9d7f46 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 7 Mar 2024 15:46:27 +0100 Subject: [PATCH 23/74] Update team-by-id-members API documentation link --- app/Http/Controllers/Api/Team.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php index 540ac282a..453c2590f 100644 --- a/app/Http/Controllers/Api/Team.php +++ b/app/Http/Controllers/Api/Team.php @@ -40,7 +40,7 @@ public function members_by_id(Request $request) $teams = auth()->user()->teams; $team = $teams->where('id', $id)->first(); if (is_null($team)) { - return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-members"], 404); + return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id-members"], 404); } return response()->json($team->members); } From 8b9548a463dbd9e0ac72a5bb307bb79c189d4d47 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 8 Mar 2024 15:16:58 +0100 Subject: [PATCH 24/74] Refactor resource mapping in resources() method --- app/Http/Controllers/Api/Resources.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php index d3d313f3a..4032d26e2 100644 --- a/app/Http/Controllers/Api/Resources.php +++ b/app/Http/Controllers/Api/Resources.php @@ -22,6 +22,16 @@ public function resources(Request $request) $resources->push($projects->pluck(str($db)->plural(2))->flatten()); } $resources = $resources->flatten(); + $resources = $resources->map(function ($resource) { + $payload = $resource->toArray(); + if ($resource->getMorphClass() === 'App\Models\Service') { + $payload['status'] = $resource->status(); + } else { + $payload['status'] = $resource->status; + } + $payload['type'] = $resource->type(); + return $payload; + }); return response()->json($resources); } From 15ca68f7e16d06c38ae901e2895306f379783fce Mon Sep 17 00:00:00 2001 From: Fabian Gigler Date: Fri, 8 Mar 2024 18:44:42 +0100 Subject: [PATCH 25/74] improve onboarding messages --- .../views/livewire/boarding/index.blade.php | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index 2777eb3ac..e32528dcc 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -3,7 +3,7 @@
    @if ($currentState === 'welcome')

    Welcome to Coolify

    -

    Let me help you to set the basics.

    +

    Let me help you set up the basics.

    Get Started @@ -24,12 +24,12 @@ -

    You do not to manage your servers too much. Coolify do +

    You don't need to manage your servers anymore. Coolify does it for you.

    -

    All configurations are stored on your server, so - everything works without Coolify (except integrations and automations).

    -

    You will get notified on your favourite platform (Discord, - Telegram, Email, etc.) when something goes wrong, or an action needed from your side.

    +

    All configurations are stored on your servers, so + everything works without a connection to Coolify (except integrations and automations).

    +

    You can get notified on your favourite platforms (Discord, + Telegram, Email, etc.) when something goes wrong, or an action is needed from your side.

    Next @@ -40,8 +40,8 @@ @if ($currentState === 'select-server-type') - Do you want to deploy your resources on your - or on a ? + Do you want to deploy your resources to your + or to a ? - Let's create a new - one! + Create new + project!
    @if (count($projects) > 0)
    @@ -319,9 +319,9 @@
    -

    Projects are bound together several resources into one virtual group. There are no - limitations on the number of projects you could have.

    -

    Each project should have at least one environment. This helps you to create a production & +

    Projects contain several resources combined into one virtual group. There are no + limitations on the number of projects you can add.

    +

    Each project should have at least one environment, this allows you to create a production & staging version of the same application, but grouped separately.

    @@ -331,7 +331,7 @@ @if ($currentState === 'create-resource') - I will redirect you to the new resource page, where you can create your first resource. + Let's go to the new resource page, where you can create your first resource.
    Let's do From 853a14c6b80f9b4d94f59ffb36d7259bf33c6fda Mon Sep 17 00:00:00 2001 From: Lee Conlin Date: Fri, 8 Mar 2024 23:54:37 +0000 Subject: [PATCH 26/74] Added PenPot service template --- templates/compose/penpot.yaml | 83 +++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 templates/compose/penpot.yaml diff --git a/templates/compose/penpot.yaml b/templates/compose/penpot.yaml new file mode 100644 index 000000000..646c1a706 --- /dev/null +++ b/templates/compose/penpot.yaml @@ -0,0 +1,83 @@ +# documentation: https://help.penpot.app/technical-guide/getting-started/#install-with-docker +# slogan: Penpot is the first Open Source design and prototyping platform for product teams. +# tags: penpot,design,prototyping,figma,open,source + +version: '3.5' +networks: + penpot: null +volumes: + penpot_postgres_v15: null + penpot_assets: null +services: + penpot-frontend: + image: 'penpotapp/frontend:latest' + restart: always + ports: + - '9001:80' + volumes: + - 'penpot_assets:/opt/data/assets' + depends_on: + - penpot-backend + - penpot-exporter + networks: + - penpot + environment: + - SERVICE_FQDN_PENPOT-FRONTEND + - 'PENPOT_FLAGS=${PENPOT_FRONTEND_FLAGS}' + penpot-backend: + image: 'penpotapp/backend:latest' + restart: always + volumes: + - 'penpot_assets:/opt/data/assets' + depends_on: + - penpot-postgres + - penpot-redis + networks: + - penpot + environment: + - SERVICE_FQDN_PENPOT-BACKEND + - 'PENPOT_FLAGS=${PENPOT_BACKEND_FLAGS}' + - 'PENPOT_SECRET_KEY=${PENPOT_SECRET_KEY}' + - 'PENPOT_PUBLIC_URI=${SERVICE_FQDN_PENPOT-FRONTEND}' + - 'PENPOT_DATABASE_URI=postgresql://penpot-postgres/penpot' + - 'PENPOT_DATABASE_USERNAME=${SERVICE_USER_POSTGRES}' + - 'PENPOT_DATABASE_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' + - 'PENPOT_REDIS_URI=redis://penpot-redis/0' + - PENPOT_ASSETS_STORAGE_BACKEND=assets-fs + - PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets + - 'PENPOT_TELEMETRY_ENABLED=${PENPOT_TELEMETRY_ENABLED}' + - 'PENPOT_SMTP_DEFAULT_FROM=${PENPOT_SMTP_DEFAULT_FROM}' + - 'PENPOT_SMTP_DEFAULT_REPLY_TO=${PENPOT_SMTP_DEFAULT_REPLY_TO}' + - 'PENPOT_SMTP_HOST=${PENPOT_SMTP_HOST}' + - 'PENPOT_SMTP_PORT=${PENPOT_SMTP_PORT}' + - 'PENPOT_SMTP_USERNAME=${PENPOT_SMTP_USERNAME}' + - 'PENPOT_SMTP_PASSWORD=${PENPOT_SMTP_PASSWORD}' + - 'PENPOT_SMTP_TLS=${PENPOT_SMTP_TLS}' + - 'PENPOT_SMTP_SSL=${PENPOT_SMTP_SSL}' + penpot-exporter: + image: 'penpotapp/exporter:latest' + restart: always + networks: + - penpot + environment: + - SERVICE_FQDN_PENPOT-EXPORTER + - 'PENPOT_PUBLIC_URI=${SERVICE_FQDN_PENPOT-FRONTEND}' + - 'PENPOT_REDIS_URI=redis://penpot-redis/0' + penpot-postgres: + image: 'postgres:15' + restart: always + stop_signal: SIGINT + volumes: + - 'penpot_postgres_v15:/var/lib/postgresql/data' + networks: + - penpot + environment: + - POSTGRES_INITDB_ARGS=--data-checksums + - POSTGRES_DB=penpot + - 'POSTGRES_USER=${SERVICE_USER_POSTGRES}' + - 'POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' + penpot-redis: + image: 'redis:7' + restart: always + networks: + - penpot \ No newline at end of file From 366d39a7a3647a4a7af51eb9c278eb2d0cac6f82 Mon Sep 17 00:00:00 2001 From: Lee Conlin Date: Sat, 9 Mar 2024 00:21:47 +0000 Subject: [PATCH 27/74] Added some default env values for penpot --- templates/compose/penpot.env | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 templates/compose/penpot.env diff --git a/templates/compose/penpot.env b/templates/compose/penpot.env new file mode 100644 index 000000000..a0b07199f --- /dev/null +++ b/templates/compose/penpot.env @@ -0,0 +1,12 @@ +PENPOT_BACKEND_FLAGS=enable-login-with-password enable-smtp enable-prepl-server +PENPOT_FRONTEND_FLAGS=enable-login-with-password +PENPOT_SECRET_KEY=$SERVICE_PASSWORD_64_PENPOT +PENPOT_SMTP_DEFAULT_FROM= +PENPOT_SMTP_DEFAULT_REPLY_TO= +PENPOT_SMTP_HOST= +PENPOT_SMTP_PASSWORD= +PENPOT_SMTP_PORT= +PENPOT_SMTP_SSL=false +PENPOT_SMTP_TLS=false +PENPOT_SMTP_USERNAME= +PENPOT_TELEMETRY_ENABLED=true \ No newline at end of file From 820099622e8f10448f88879247d85747c6a78592 Mon Sep 17 00:00:00 2001 From: "Mr. Mendez" <56850299+JustMrMendez@users.noreply.github.com> Date: Sun, 10 Mar 2024 18:14:53 +0000 Subject: [PATCH 28/74] visual feedback when container is unhealthy --- resources/views/components/status/running.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/components/status/running.blade.php b/resources/views/components/status/running.blade.php index b046266d1..4751204ce 100644 --- a/resources/views/components/status/running.blade.php +++ b/resources/views/components/status/running.blade.php @@ -8,6 +8,6 @@ {{ str($status)->before(':')->headline() }}
    @if (!str($status)->startsWith('Proxy') && !str($status)->contains('(')) -
    ({{ str($status)->after(':') }})
    +
    ({{ str($status)->after(':') }})
    @endif
    From 8b73f9da173b9e2a34c57ee60d206e66a2af77da Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 09:42:02 +0100 Subject: [PATCH 29/74] fix: deploy api messages --- app/Http/Controllers/Api/Deploy.php | 50 ++++++++++------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php index bdea30029..89a994afe 100644 --- a/app/Http/Controllers/Api/Deploy.php +++ b/app/Http/Controllers/Api/Deploy.php @@ -17,7 +17,8 @@ class Deploy extends Controller { - public function deployments(Request $request) { + public function deployments(Request $request) + { $teamId = get_team_id_from_token(); if (is_null($teamId)) { return invalid_token(); @@ -63,7 +64,6 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) if (count($uuids) === 0) { return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400); } - $message = collect([]); $deployments = collect(); $payload = collect(); foreach ($uuids as $uuid) { @@ -71,16 +71,14 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) if ($resource) { ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force); if ($deployment_uuid) { - $deployments->push(['resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]); + $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]); + } else { + $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]); } - $message = $message->merge($return_message); } } - if ($message->count() > 0) { - $payload->put('message', $message->toArray()); - if ($deployments->count() > 0) { - $payload->put('details', $deployments->toArray()); - } + if ($deployments->count() > 0) { + $payload->put('deployments', $deployments->toArray()); return response()->json($payload->toArray(), 200); } return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404); @@ -133,9 +131,10 @@ public function by_tags(string $tags, int $team_id, bool $force = false) } public function deploy_resource($resource, bool $force = false): array { - $message = collect([]); + $message = null; + $deployment_uuid = null; if (gettype($resource) !== 'object') { - return $message->push("Resource ($resource) not found."); + return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid]; } $type = $resource?->getMorphClass(); if ($type === 'App\Models\Application') { @@ -145,55 +144,40 @@ public function deploy_resource($resource, bool $force = false): array deployment_uuid: $deployment_uuid, force_rebuild: $force, ); - $message->push("Application {$resource->name} deployment queued."); + $message = "Application {$resource->name} deployment queued."; } else if ($type === 'App\Models\StandalonePostgresql') { - if (str($resource->status)->startsWith('running')) { - $message->push("Database {$resource->name} already running."); - } StartPostgresql::run($resource); $resource->update([ 'started_at' => now(), ]); - $message->push("Database {$resource->name} started."); + $message = "Database {$resource->name} started."; } else if ($type === 'App\Models\StandaloneRedis') { - if (str($resource->status)->startsWith('running')) { - $message->push("Database {$resource->name} already running."); - } StartRedis::run($resource); $resource->update([ 'started_at' => now(), ]); - $message->push("Database {$resource->name} started."); + $message = "Database {$resource->name} started."; } else if ($type === 'App\Models\StandaloneMongodb') { - if (str($resource->status)->startsWith('running')) { - $message->push("Database {$resource->name} already running."); - } StartMongodb::run($resource); $resource->update([ 'started_at' => now(), ]); - $message->push("Database {$resource->name} started."); + $message = "Database {$resource->name} started."; } else if ($type === 'App\Models\StandaloneMysql') { - if (str($resource->status)->startsWith('running')) { - $message->push("Database {$resource->name} already running."); - } StartMysql::run($resource); $resource->update([ 'started_at' => now(), ]); - $message->push("Database {$resource->name} started."); + $message = "Database {$resource->name} started."; } else if ($type === 'App\Models\StandaloneMariadb') { - if (str($resource->status)->startsWith('running')) { - $message->push("Database {$resource->name} already running."); - } StartMariadb::run($resource); $resource->update([ 'started_at' => now(), ]); - $message->push("Database {$resource->name} started."); + $message = "Database {$resource->name} started."; } else if ($type === 'App\Models\Service') { StartService::run($resource); - $message->push("Service {$resource->name} started. It could take a while, be patient."); + $message = "Service {$resource->name} started. It could take a while, be patient."; } return ['message' => $message, 'deployment_uuid' => $deployment_uuid]; } From 5d3de967f027e88aed971dafd0d9b04426163732 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 09:42:16 +0100 Subject: [PATCH 30/74] fix: fqdn null in case docker compose bp --- app/Livewire/Project/Application/General.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 3279ff2d5..274e198a8 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -112,6 +112,10 @@ public function mount() } catch (\Throwable $e) { $this->dispatch('error', $e->getMessage()); } + if ($this->application->build_pack === 'dockercompose') { + $this->application->fqdn = null; + $this->application->settings->save(); + } $this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : []; $this->ports_exposes = $this->application->ports_exposes; @@ -163,7 +167,12 @@ public function generateDomain(string $serviceName) } return $domain; } - + public function updatedApplicationBaseDirectory() { + raY('asdf'); + if ($this->application->build_pack === 'dockercompose') { + $this->loadComposeFile(); + } + } public function updatedApplicationBuildPack() { if ($this->application->build_pack !== 'nixpacks') { @@ -211,7 +220,6 @@ public function updatedApplicationFqdn() $this->application->fqdn = $this->application->fqdn->unique()->implode(','); $this->application->save(); $this->resetDefaultLabels(false); - // $this->dispatch('success', 'Labels reset to default!'); } public function submit($showToaster = true) { From 34d6a12d9575afe440df0800a5d57fb381ec38c4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 15:08:05 +0100 Subject: [PATCH 31/74] feat: experimental caddy support --- app/Actions/Proxy/CheckConfiguration.php | 7 +- app/Actions/Proxy/CheckProxy.php | 3 + app/Actions/Proxy/SaveConfiguration.php | 2 +- app/Actions/Proxy/StartProxy.php | 4 +- app/Livewire/Boarding/Index.php | 1 + app/Livewire/Project/Application/General.php | 4 +- .../Project/Shared/ResourceOperations.php | 2 +- app/Livewire/Server/New/ByIp.php | 1 + .../Proxy/DynamicConfigurationNavbar.php | 2 +- .../Server/Proxy/DynamicConfigurations.php | 2 +- .../Server/Proxy/NewDynamicConfiguration.php | 2 +- app/Models/Server.php | 23 ++- bootstrap/helpers/docker.php | 53 ++++- bootstrap/helpers/proxy.php | 191 ++++++++++-------- bootstrap/helpers/shared.php | 22 +- config/sentry.php | 2 +- .../views/components/server/sidebar.blade.php | 12 +- .../views/livewire/server/proxy.blade.php | 32 ++- 18 files changed, 252 insertions(+), 113 deletions(-) diff --git a/app/Actions/Proxy/CheckConfiguration.php b/app/Actions/Proxy/CheckConfiguration.php index 7a1cb04ed..208a6863a 100644 --- a/app/Actions/Proxy/CheckConfiguration.php +++ b/app/Actions/Proxy/CheckConfiguration.php @@ -11,7 +11,12 @@ class CheckConfiguration use AsAction; public function handle(Server $server, bool $reset = false) { - $proxy_path = get_proxy_path(); + $proxyType = $server->proxyType(); + if ($proxyType === 'NONE') { + return 'OK'; + } + $proxy_path = $server->proxyPath(); + $proxy_configuration = instant_remote_process([ "mkdir -p $proxy_path", "cat $proxy_path/docker-compose.yml", diff --git a/app/Actions/Proxy/CheckProxy.php b/app/Actions/Proxy/CheckProxy.php index ccefa8681..41f961b59 100644 --- a/app/Actions/Proxy/CheckProxy.php +++ b/app/Actions/Proxy/CheckProxy.php @@ -10,6 +10,9 @@ class CheckProxy use AsAction; public function handle(Server $server, $fromUI = false) { + if ($server->proxyType() === 'NONE') { + return false; + } if (!$server->isProxyShouldRun()) { if ($fromUI) { throw new \Exception("Proxy should not run. You selected the Custom Proxy."); diff --git a/app/Actions/Proxy/SaveConfiguration.php b/app/Actions/Proxy/SaveConfiguration.php index edf4f3434..5fb983d1a 100644 --- a/app/Actions/Proxy/SaveConfiguration.php +++ b/app/Actions/Proxy/SaveConfiguration.php @@ -15,7 +15,7 @@ public function handle(Server $server, ?string $proxy_settings = null) if (is_null($proxy_settings)) { $proxy_settings = CheckConfiguration::run($server, true); } - $proxy_path = get_proxy_path(); + $proxy_path = $server->proxyPath(); $docker_compose_yml_base64 = base64_encode($proxy_settings); $server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value; diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index e106c1801..46ac816b4 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -15,11 +15,11 @@ public function handle(Server $server, bool $async = true): string|Activity { try { $proxyType = $server->proxyType(); - if ($proxyType === 'NONE') { + if (is_null($proxyType) || $proxyType === 'NONE') { return 'OK'; } $commands = collect([]); - $proxy_path = get_proxy_path(); + $proxy_path = $server->proxyPath(); $configuration = CheckConfiguration::run($server); if (!$configuration) { throw new \Exception("Configuration is not synced"); diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index e80042573..52440dde7 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -126,6 +126,7 @@ public function selectExistingServer() } public function getProxyType() { + // Set Default Proxy Type $this->selectProxy(ProxyTypes::TRAEFIK_V2->value); // $proxyTypeSet = $this->createdServer->proxy->type; // if (!$proxyTypeSet) { diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 274e198a8..92b452a42 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -124,7 +124,7 @@ public function mount() } $this->isConfigurationChanged = $this->application->isConfigurationChanged(); $this->customLabels = $this->application->parseContainerLabels(); - if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') { + if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n"); $this->application->custom_labels = base64_encode($this->customLabels); $this->application->save(); @@ -224,7 +224,7 @@ public function updatedApplicationFqdn() public function submit($showToaster = true) { try { - if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') { + if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n"); $this->application->custom_labels = base64_encode($this->customLabels); $this->application->save(); diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php index e2037991a..62562179a 100644 --- a/app/Livewire/Project/Shared/ResourceOperations.php +++ b/app/Livewire/Project/Shared/ResourceOperations.php @@ -45,7 +45,7 @@ public function cloneTo($destination_id) 'destination_id' => $new_destination->id, ]); $new_resource->save(); - if ($new_resource->destination->server->proxyType() === 'TRAEFIK_V2') { + if ($new_resource->destination->server->proxyType() !== 'NONE') { $customLabels = str(implode("|", generateLabelsApplication($new_resource)))->replace("|", "\n"); $new_resource->custom_labels = base64_encode($customLabels); $new_resource->save(); diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php index 9799443c7..df3fae20f 100644 --- a/app/Livewire/Server/New/ByIp.php +++ b/app/Livewire/Server/New/ByIp.php @@ -89,6 +89,7 @@ public function submit() 'team_id' => currentTeam()->id, 'private_key_id' => $this->private_key_id, 'proxy' => [ + // set default proxy type to traefik v2 "type" => ProxyTypes::TRAEFIK_V2->value, "status" => ProxyStatus::EXITED->value, ], diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php index ee46a3fff..aee61f7de 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php @@ -14,7 +14,7 @@ class DynamicConfigurationNavbar extends Component public function delete(string $fileName) { $server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first(); - $proxy_path = get_proxy_path(); + $proxy_path = $server->proxyPath(); $file = str_replace('|', '.', $fileName); instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server); $this->dispatch('success', 'File deleted.'); diff --git a/app/Livewire/Server/Proxy/DynamicConfigurations.php b/app/Livewire/Server/Proxy/DynamicConfigurations.php index 6e52f9d4a..b9e89321c 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurations.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurations.php @@ -17,7 +17,7 @@ class DynamicConfigurations extends Component ]; public function loadDynamicConfigurations() { - $proxy_path = get_proxy_path(); + $proxy_path = $this->server->proxyPath(); $files = instant_remote_process(["mkdir -p $proxy_path/dynamic && ls -1 {$proxy_path}/dynamic"], $this->server); $files = collect(explode("\n", $files))->filter(fn ($file) => !empty($file)); $files = $files->map(fn ($file) => trim($file)); diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php index c9ceb41ee..302e603b3 100644 --- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php +++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php @@ -46,7 +46,7 @@ public function addDynamicConfiguration() $this->dispatch('error', 'File name is reserved.'); return; } - $proxy_path = get_proxy_path(); + $proxy_path = $this->proxyPath(); $file = "{$proxy_path}/dynamic/{$this->fileName}"; if ($this->newFile) { $exists = instant_remote_process(["test -f $file && echo 1 || echo 0"], $this->server); diff --git a/app/Models/Server.php b/app/Models/Server.php index 4028109e2..bc65cbdba 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -118,17 +118,30 @@ public function addInitialNetwork() } } } + public function proxyPath() { + $base_path = config('coolify.base_config_path'); + $proxyType = $this->proxyType(); + $proxy_path = "$base_path/proxy"; + if ($proxyType === ProxyTypes::TRAEFIK_V2->value) { + $proxy_path = $proxy_path; + } else if ($proxyType === ProxyTypes::CADDY->value) { + $proxy_path = $proxy_path . '/caddy'; + } else if ($proxyType === ProxyTypes::NGINX->value) { + $proxy_path = $proxy_path . '/nginx'; + } + return $proxy_path; + } public function proxyType() { $proxyType = $this->proxy->get('type'); if ($proxyType === ProxyTypes::NONE->value) { return $proxyType; } - if (is_null($proxyType)) { - $this->proxy->type = ProxyTypes::TRAEFIK_V2->value; - $this->proxy->status = ProxyStatus::EXITED->value; - $this->save(); - } + // if (is_null($proxyType)) { + // $this->proxy->type = ProxyTypes::TRAEFIK_V2->value; + // $this->proxy->status = ProxyStatus::EXITED->value; + // $this->save(); + // } return $this->proxy->get('type'); } public function scopeWithProxy(): Builder diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 5fd43daa9..53b0bbe80 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -1,5 +1,6 @@ $domain) { + $loop = $loop; + $url = Url::fromString($domain); + $host = $url->getHost(); + $path = $url->getPath(); + // $stripped_path = str($path)->replaceEnd('/', ''); + + $schema = $url->getScheme(); + $port = $url->getPort(); + if (is_null($port) && !is_null($onlyPort)) { + $port = $onlyPort; + } + $labels->push("caddy_{$loop}={$schema}://{$host}"); + $labels->push("caddy_{$loop}.header=-Server"); + + if ($serviceLabels) { + $labels->push("caddy_ingress_network={$uuid}"); + $labels->push("caddy_{$loop}.reverse_proxy={{upstreams}}"); + } else { + $labels->push("caddy_ingress_network={$network}"); + if ($port) { + $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams $port}}"); + } else { + $labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams}}"); + } + $labels->push("caddy_{$loop}.handle_path={$path}*"); + } + + if ($is_gzip_enabled) { + $labels->push("caddy_{$loop}.encode=zstd gzip"); + } + if (isDev()) { + // $labels->push("caddy_{$loop}.tls=internal"); + } + } + 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) { $labels = collect([]); @@ -395,7 +436,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview } else { $domains = Str::of(data_get($application, 'fqdn'))->explode(','); } - // Add Traefik labels no matter which proxy is selected + // Add Traefik labels $labels = $labels->merge(fqdnLabelsForTraefik( uuid: $appUuid, domains: $domains, @@ -404,6 +445,16 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview is_gzip_enabled: $application->isGzipEnabled(), is_stripprefix_enabled: $application->isStripprefixEnabled() )); + // Add Caddy labels + $labels = $labels->merge(fqdnLabelsForCaddy( + network: $application->destination->network, + uuid: $appUuid, + domains: $domains, + onlyPort: $onlyPort, + is_force_https_enabled: $application->isForceHttpsEnabled(), + is_gzip_enabled: $application->isGzipEnabled(), + is_stripprefix_enabled: $application->isStripprefixEnabled() + )); } return $labels->all(); } diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index 1bc1bdc28..1edfaed2e 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -7,12 +7,7 @@ use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; -function get_proxy_path() -{ - $base_path = config('coolify.base_config_path'); - $proxy_path = "$base_path/proxy"; - return $proxy_path; -} + function connectProxyToNetworks(Server $server) { if ($server->isSwarm()) { @@ -75,7 +70,9 @@ function connectProxyToNetworks(Server $server) } function generate_default_proxy_configuration(Server $server) { - $proxy_path = get_proxy_path(); + $proxy_path = $server->proxyPath(); + $proxy_type = $server->proxyType(); + if ($server->isSwarm()) { $networks = collect($server->swarmDockers)->map(function ($docker) { return $docker['network']; @@ -98,93 +95,129 @@ function generate_default_proxy_configuration(Server $server) "external" => true, ]; }); - $labels = [ - "traefik.enable=true", - "traefik.http.routers.traefik.entrypoints=http", - "traefik.http.routers.traefik.service=api@internal", - "traefik.http.services.traefik.loadbalancer.server.port=8080", - "coolify.managed=true", - ]; - $config = [ - "version" => "3.8", - "networks" => $array_of_networks->toArray(), - "services" => [ - "traefik" => [ - "container_name" => "coolify-proxy", - "image" => "traefik:v2.10", - "restart" => RESTART_MODE, - "extra_hosts" => [ - "host.docker.internal:host-gateway", + if ($proxy_type === 'TRAEFIK_V2') { + $labels = [ + "traefik.enable=true", + "traefik.http.routers.traefik.entrypoints=http", + "traefik.http.routers.traefik.service=api@internal", + "traefik.http.services.traefik.loadbalancer.server.port=8080", + "coolify.managed=true", + ]; + $config = [ + "version" => "3.8", + "networks" => $array_of_networks->toArray(), + "services" => [ + "traefik" => [ + "container_name" => "coolify-proxy", + "image" => "traefik:v2.10", + "restart" => RESTART_MODE, + "extra_hosts" => [ + "host.docker.internal:host-gateway", + ], + "networks" => $networks->toArray(), + "ports" => [ + "80:80", + "443:443", + "8080:8080", + ], + "healthcheck" => [ + "test" => "wget -qO- http://localhost:80/ping || exit 1", + "interval" => "4s", + "timeout" => "2s", + "retries" => 5, + ], + "volumes" => [ + "/var/run/docker.sock:/var/run/docker.sock:ro", + "{$proxy_path}:/traefik", + ], + "command" => [ + "--ping=true", + "--ping.entrypoint=http", + "--api.dashboard=true", + "--api.insecure=false", + "--entrypoints.http.address=:80", + "--entrypoints.https.address=:443", + "--entrypoints.http.http.encodequerysemicolons=true", + "--entryPoints.http.http2.maxConcurrentStreams=50", + "--entrypoints.https.http.encodequerysemicolons=true", + "--entryPoints.https.http2.maxConcurrentStreams=50", + "--providers.docker.exposedbydefault=false", + "--providers.file.directory=/traefik/dynamic/", + "--providers.file.watch=true", + "--certificatesresolvers.letsencrypt.acme.httpchallenge=true", + "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json", + "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http", + ], + "labels" => $labels, ], - "networks" => $networks->toArray(), - "ports" => [ - "80:80", - "443:443", - "8080:8080", - ], - "healthcheck" => [ - "test" => "wget -qO- http://localhost:80/ping || exit 1", - "interval" => "4s", - "timeout" => "2s", - "retries" => 5, - ], - "volumes" => [ - "/var/run/docker.sock:/var/run/docker.sock:ro", - "{$proxy_path}:/traefik", - ], - "command" => [ - "--ping=true", - "--ping.entrypoint=http", - "--api.dashboard=true", - "--api.insecure=false", - "--entrypoints.http.address=:80", - "--entrypoints.https.address=:443", - "--entrypoints.http.http.encodequerysemicolons=true", - "--entryPoints.http.http2.maxConcurrentStreams=50", - "--entrypoints.https.http.encodequerysemicolons=true", - "--entryPoints.https.http2.maxConcurrentStreams=50", - "--providers.docker.exposedbydefault=false", - "--providers.file.directory=/traefik/dynamic/", - "--providers.file.watch=true", - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true", - "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json", - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http", - ], - "labels" => $labels, ], - ], - ]; - if (isDev()) { - // $config['services']['traefik']['command'][] = "--log.level=debug"; - $config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log"; - $config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100"; - } - if ($server->isSwarm()) { - data_forget($config, 'services.traefik.container_name'); - data_forget($config, 'services.traefik.restart'); - data_forget($config, 'services.traefik.labels'); + ]; + if (isDev()) { + // $config['services']['traefik']['command'][] = "--log.level=debug"; + $config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log"; + $config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100"; + } + if ($server->isSwarm()) { + data_forget($config, 'services.traefik.container_name'); + data_forget($config, 'services.traefik.restart'); + data_forget($config, 'services.traefik.labels'); - $config['services']['traefik']['command'][] = "--providers.docker.swarmMode=true"; - $config['services']['traefik']['deploy'] = [ - "labels" => $labels, - "placement" => [ - "constraints" => [ - "node.role==manager", + $config['services']['traefik']['command'][] = "--providers.docker.swarmMode=true"; + $config['services']['traefik']['deploy'] = [ + "labels" => $labels, + "placement" => [ + "constraints" => [ + "node.role==manager", + ], + ], + ]; + } else { + $config['services']['traefik']['command'][] = "--providers.docker=true"; + } + } else if ($proxy_type === 'CADDY') { + $config = [ + "version" => "3.8", + "networks" => $array_of_networks->toArray(), + "services" => [ + "caddy" => [ + "container_name" => "coolify-proxy", + "image" => "lucaslorentz/caddy-docker-proxy:2.8-alpine", + "restart" => RESTART_MODE, + "extra_hosts" => [ + "host.docker.internal:host-gateway", + ], + "networks" => $networks->toArray(), + "ports" => [ + "80:80", + "443:443", + ], + // "healthcheck" => [ + // "test" => "wget -qO- http://localhost:80|| exit 1", + // "interval" => "4s", + // "timeout" => "2s", + // "retries" => 5, + // ], + "volumes" => [ + "/var/run/docker.sock:/var/run/docker.sock:ro", + "{$proxy_path}/config:/config", + "{$proxy_path}/data:/data", + ], ], ], ]; } else { - $config['services']['traefik']['command'][] = "--providers.docker=true"; + return null; } + $config = Yaml::dump($config, 12, 2); SaveConfiguration::run($server, $config); return $config; } function setup_dynamic_configuration() { - $dynamic_config_path = get_proxy_path() . "/dynamic"; $settings = InstanceSettings::get(); $server = Server::find(0); + $dynamic_config_path = $server->proxyPath() . "/dynamic"; if ($server) { $file = "$dynamic_config_path/coolify.yaml"; if (empty($settings->fqdn)) { @@ -308,7 +341,7 @@ function setup_dynamic_configuration() } function setup_default_redirect_404(string|null $redirect_url, Server $server) { - $traefik_dynamic_conf_path = get_proxy_path() . "/dynamic"; + $traefik_dynamic_conf_path = $server->proxyPath() . "/dynamic"; $traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml"; if (empty($redirect_url)) { instant_remote_process([ diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 621251d36..359792ddc 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1056,6 +1056,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal is_stripprefix_enabled: $savedService->isStripprefixEnabled(), service_name: $serviceName )); + $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy( + network: $resource->destination->network, + uuid: $resource->uuid, + domains: $fqdns, + is_force_https_enabled: true, + serviceLabels: $serviceLabels, + is_gzip_enabled: $savedService->isGzipEnabled(), + is_stripprefix_enabled: $savedService->isStripprefixEnabled(), + service_name: $serviceName + )); } } if ($resource->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) { @@ -1495,7 +1505,17 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal return $preview_fqdn; }); } - $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($uuid, $fqdns, serviceLabels: $serviceLabels)); + $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( + uuid: $uuid, + domains: $fqdns, + serviceLabels: $serviceLabels + )); + $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy( + network: $resource->destination->network, + uuid: $uuid, + domains: $fqdns, + serviceLabels: $serviceLabels + )); } } } diff --git a/config/sentry.php b/config/sentry.php index 74eb9ba6a..2d2d440c3 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -3,7 +3,7 @@ return [ // @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - 'dsn' => 'https://1bbc8f762199a52aee39196adb3e8d1a@o1082494.ingest.sentry.io/4505347448045568', + 'dsn' => 'https://f0b0e6be13926d4ac68d68d51d38db8f@o1082494.ingest.us.sentry.io/4505347448045568', // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) diff --git a/resources/views/components/server/sidebar.blade.php b/resources/views/components/server/sidebar.blade.php index a163bf05f..94c8c9755 100644 --- a/resources/views/components/server/sidebar.blade.php +++ b/resources/views/components/server/sidebar.blade.php @@ -4,11 +4,13 @@ href="{{ route('server.proxy', $parameters) }}"> - @if (data_get($server, 'proxy.type') !== 'NONE') - - - + @if ($server->proxyType() !== 'NONE') + @if ($server->proxyType() === 'TRAEFIK_V2') + + + + @endif diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php index e87ad573a..7c6599792 100644 --- a/resources/views/livewire/server/proxy.blade.php +++ b/resources/views/livewire/server/proxy.blade.php @@ -1,16 +1,24 @@
    @if (data_get($server, 'proxy.type'))
    - @if ($selectedProxy === 'TRAEFIK_V2') + @if ($selectedProxy !== 'NONE')

    Configuration

    - Save @if ($server->proxy->status === 'exited') Switch Proxy + @else + Switch Proxy @endif + Save +
    -
    Traefik v2
    +
    + @if ($server->proxyType() === 'TRAEFIK_V2') +
    Traefik v2
    + @elseif ($server->proxyType() === 'CADDY') +
    Caddy
    + @endif @if ( $server->proxy->last_applied_settings && $server->proxy->last_saved_settings !== $server->proxy->last_applied_settings) @@ -18,15 +26,18 @@ configurations.
    @endif - + @if ($server->proxyType() === 'TRAEFIK_V2') + + @endif
    @if ($proxy_settings)
    - Reset configuration to default @@ -40,7 +51,7 @@

    Configuration

    Switch Proxy
    -
    Custom (None) Proxy Selected
    +
    Custom (None) Proxy Selected
    @else

    Configuration

    @@ -57,14 +68,13 @@ Traefik - v2 + + + Caddy (experimental) Nginx - - Caddy -
    @endif From 1defed27a02a5b50c7afb176b9714bad1a5337be Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 15:19:04 +0100 Subject: [PATCH 32/74] Refactor compose file generation and add link to documentation --- app/Jobs/ApplicationDeploymentJob.php | 24 ++++++++++++++++++- .../views/livewire/server/proxy.blade.php | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 0dedfd596..b9c60605f 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -1077,7 +1077,10 @@ private function generate_env_variables() private function generate_compose_file() { $ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array; - + $onlyPort = null; + if (count($ports) > 0) { + $onlyPort = $ports[0]; + } $persistent_storages = $this->generate_local_persistent_volumes(); $volume_names = $this->generate_local_persistent_volumes_only_volume_names(); $environment_variables = $this->generate_environment_variables($ports); @@ -1088,6 +1091,25 @@ private function generate_compose_file() $labels = $labels->filter(function ($value, $key) { return !Str::startsWith($value, 'coolify.'); }); + $found_caddy_labels = $labels->filter(function ($value, $key) { + return Str::startsWith($value, 'caddy_'); + }); + if ($found_caddy_labels->count() === 0) { + if ($this->pull_request_id !== 0) { + $domains = str(data_get($this->preview, 'fqdn'))->explode(','); + } else { + $domains = str(data_get($this->application, 'fqdn'))->explode(','); + } + $labels = $labels->merge(fqdnLabelsForCaddy( + network: $this->application->destination->network, + uuid: $this->application->uuid, + domains: $domains, + onlyPort: $onlyPort, + is_force_https_enabled: $this->application->isForceHttpsEnabled(), + is_gzip_enabled: $this->application->isGzipEnabled(), + is_stripprefix_enabled: $this->application->isStripprefixEnabled() + )); + } $this->application->custom_labels = base64_encode($labels->implode("\n")); $this->application->save(); } else { diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php index 7c6599792..ba3909739 100644 --- a/resources/views/livewire/server/proxy.blade.php +++ b/resources/views/livewire/server/proxy.blade.php @@ -13,7 +13,7 @@ Save
    -
    Before switching proxies, please read this.
    +
    Before switching proxies, please read this.
    @if ($server->proxyType() === 'TRAEFIK_V2')
    Traefik v2
    @elseif ($server->proxyType() === 'CADDY') From f24063cfea01dc9f02a04e676e5de7fd5d469baf Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 15:36:45 +0100 Subject: [PATCH 33/74] Add CADDY_DOCKER_POLLING_INTERVAL environment variable --- bootstrap/helpers/proxy.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index 1edfaed2e..caa3e4fa0 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -186,6 +186,9 @@ function generate_default_proxy_configuration(Server $server) "extra_hosts" => [ "host.docker.internal:host-gateway", ], + "environment" => [ + "CADDY_DOCKER_POLLING_INTERVAL=5s", + ], "networks" => $networks->toArray(), "ports" => [ "80:80", From 9bdad6bb672991959237fcc8909efcb92c1d106f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 17:17:34 +0100 Subject: [PATCH 34/74] feat caddy dynamic configurations --- app/Actions/Proxy/StartProxy.php | 2 + app/Console/Commands/Init.php | 3 +- app/Livewire/Settings/Configuration.php | 6 +- app/Models/Server.php | 153 +++++++++++++++++++++++- bootstrap/helpers/proxy.php | 127 +------------------- 5 files changed, 159 insertions(+), 132 deletions(-) diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index 46ac816b4..a781ab442 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -37,8 +37,10 @@ public function handle(Server $server, bool $async = true): string|Activity "echo 'Proxy started successfully.'" ]); } else { + $caddfile = "import /dynamic/*.caddy"; $commands = $commands->merge([ "mkdir -p $proxy_path/dynamic && cd $proxy_path", + "echo '$caddfile' > $proxy_path/dynamic/Caddyfile", "echo 'Creating required Docker Compose file.'", "echo 'Pulling docker image.'", 'docker compose pull', diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index c69d411dd..66a4f1aff 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -35,7 +35,8 @@ public function handle() $this->call('cleanup:queue'); $this->call('cleanup:stucked-resources'); try { - setup_dynamic_configuration(); + $server = Server::find(0)->first(); + $server->setupDynamicProxyConfiguration(); } catch (\Throwable $e) { echo "Could not setup dynamic configuration: {$e->getMessage()}\n"; } diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php index bafc82447..f1c732a97 100644 --- a/app/Livewire/Settings/Configuration.php +++ b/app/Livewire/Settings/Configuration.php @@ -2,12 +2,9 @@ namespace App\Livewire\Settings; -use App\Jobs\ContainerStatusJob; use App\Models\InstanceSettings as ModelsInstanceSettings; use App\Models\Server; use Livewire\Component; -use Spatie\Url\Url; -use Symfony\Component\Yaml\Yaml; class Configuration extends Component { @@ -84,7 +81,6 @@ public function submit() private function setup_instance_fqdn() { - setup_dynamic_configuration(); - + $this->server->setupDynamicProxyConfiguration(); } } diff --git a/app/Models/Server.php b/app/Models/Server.php index bc65cbdba..d2c412d9e 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -15,6 +15,8 @@ use Spatie\SchemalessAttributes\SchemalessAttributesTrait; use Illuminate\Support\Str; use Illuminate\Support\Stringable; +use Spatie\Url\Url; +use Symfony\Component\Yaml\Yaml; class Server extends BaseModel { @@ -118,7 +120,156 @@ public function addInitialNetwork() } } } - public function proxyPath() { + public function setupDynamicProxyConfiguration() + { + $settings = InstanceSettings::get(); + $dynamic_config_path = $this->proxyPath() . "/dynamic"; + if ($this) { + if ($this->proxyType() === 'TRAEFIK_V2') { + $file = "$dynamic_config_path/coolify.yaml"; + if (empty($settings->fqdn)) { + instant_remote_process([ + "rm -f $file", + ], $this); + } else { + $url = Url::fromString($settings->fqdn); + $host = $url->getHost(); + $schema = $url->getScheme(); + $traefik_dynamic_conf = [ + 'http' => + [ + 'middlewares' => [ + 'redirect-to-https' => [ + 'redirectscheme' => [ + 'scheme' => 'https', + ], + ], + 'gzip' => [ + 'compress' => true, + ], + ], + 'routers' => + [ + 'coolify-http' => + [ + 'middlewares' => [ + 0 => 'gzip', + ], + 'entryPoints' => [ + 0 => 'http', + ], + 'service' => 'coolify', + 'rule' => "Host(`{$host}`)", + ], + 'coolify-realtime-ws' => + [ + 'entryPoints' => [ + 0 => 'http', + ], + 'service' => 'coolify-realtime', + 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", + ], + ], + 'services' => + [ + 'coolify' => + [ + 'loadBalancer' => + [ + 'servers' => + [ + 0 => + [ + 'url' => 'http://coolify:80', + ], + ], + ], + ], + 'coolify-realtime' => + [ + 'loadBalancer' => + [ + 'servers' => + [ + 0 => + [ + 'url' => 'http://coolify-realtime:6001', + ], + ], + ], + ], + ], + ], + ]; + + if ($schema === 'https') { + $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [ + 0 => 'redirect-to-https', + ]; + + $traefik_dynamic_conf['http']['routers']['coolify-https'] = [ + 'entryPoints' => [ + 0 => 'https', + ], + 'service' => 'coolify', + 'rule' => "Host(`{$host}`)", + 'tls' => [ + 'certresolver' => 'letsencrypt', + ], + ]; + $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [ + 'entryPoints' => [ + 0 => 'https', + ], + 'service' => 'coolify-realtime', + 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", + 'tls' => [ + 'certresolver' => 'letsencrypt', + ], + ]; + } + $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2); + $yaml = + "# This file is automatically generated by Coolify.\n" . + "# Do not edit it manually (only if you know what are you doing).\n\n" . + $yaml; + + $base64 = base64_encode($yaml); + instant_remote_process([ + "mkdir -p $dynamic_config_path", + "echo '$base64' | base64 -d > $file", + ], $this); + + if (config('app.env') == 'local') { + // ray($yaml); + } + } + } else if ($this->proxyType() === 'CADDY') { + $file = "$dynamic_config_path/coolify.caddy"; + if (empty($settings->fqdn)) { + instant_remote_process([ + "rm -f $file", + "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile", + ], $this); + } else { + $url = Url::fromString($settings->fqdn); + $host = $url->getHost(); + $schema = $url->getScheme(); + $caddy_file = " +$schema://$host { + reverse_proxy coolify:80 +}"; + $base64 = base64_encode($caddy_file); + instant_remote_process([ + "echo '$base64' | base64 -d > $file", + "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile", + ], $this); + } + } + } + } + public function proxyPath() + { $base_path = config('coolify.base_config_path'); $proxyType = $this->proxyType(); $proxy_path = "$base_path/proxy"; diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index caa3e4fa0..2bdffaf63 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -188,6 +188,7 @@ function generate_default_proxy_configuration(Server $server) ], "environment" => [ "CADDY_DOCKER_POLLING_INTERVAL=5s", + "CADDY_DOCKER_CADDYFILE_PATH=/dynamic/Caddyfile", ], "networks" => $networks->toArray(), "ports" => [ @@ -202,6 +203,7 @@ function generate_default_proxy_configuration(Server $server) // ], "volumes" => [ "/var/run/docker.sock:/var/run/docker.sock:ro", + "{$proxy_path}/dynamic:/dynamic", "{$proxy_path}/config:/config", "{$proxy_path}/data:/data", ], @@ -216,132 +218,7 @@ function generate_default_proxy_configuration(Server $server) SaveConfiguration::run($server, $config); return $config; } -function setup_dynamic_configuration() -{ - $settings = InstanceSettings::get(); - $server = Server::find(0); - $dynamic_config_path = $server->proxyPath() . "/dynamic"; - if ($server) { - $file = "$dynamic_config_path/coolify.yaml"; - if (empty($settings->fqdn)) { - instant_remote_process([ - "rm -f $file", - ], $server); - } else { - $url = Url::fromString($settings->fqdn); - $host = $url->getHost(); - $schema = $url->getScheme(); - $traefik_dynamic_conf = [ - 'http' => - [ - 'middlewares' => [ - 'redirect-to-https' => [ - 'redirectscheme' => [ - 'scheme' => 'https', - ], - ], - 'gzip' => [ - 'compress' => true, - ], - ], - 'routers' => - [ - 'coolify-http' => - [ - 'middlewares' => [ - 0 => 'gzip', - ], - 'entryPoints' => [ - 0 => 'http', - ], - 'service' => 'coolify', - 'rule' => "Host(`{$host}`)", - ], - 'coolify-realtime-ws' => - [ - 'entryPoints' => [ - 0 => 'http', - ], - 'service' => 'coolify-realtime', - 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", - ], - ], - 'services' => - [ - 'coolify' => - [ - 'loadBalancer' => - [ - 'servers' => - [ - 0 => - [ - 'url' => 'http://coolify:80', - ], - ], - ], - ], - 'coolify-realtime' => - [ - 'loadBalancer' => - [ - 'servers' => - [ - 0 => - [ - 'url' => 'http://coolify-realtime:6001', - ], - ], - ], - ], - ], - ], - ]; - if ($schema === 'https') { - $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [ - 0 => 'redirect-to-https', - ]; - - $traefik_dynamic_conf['http']['routers']['coolify-https'] = [ - 'entryPoints' => [ - 0 => 'https', - ], - 'service' => 'coolify', - 'rule' => "Host(`{$host}`)", - 'tls' => [ - 'certresolver' => 'letsencrypt', - ], - ]; - $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [ - 'entryPoints' => [ - 0 => 'https', - ], - 'service' => 'coolify-realtime', - 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", - 'tls' => [ - 'certresolver' => 'letsencrypt', - ], - ]; - } - $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2); - $yaml = - "# This file is automatically generated by Coolify.\n" . - "# Do not edit it manually (only if you know what are you doing).\n\n" . - $yaml; - - $base64 = base64_encode($yaml); - instant_remote_process([ - "mkdir -p $dynamic_config_path", - "echo '$base64' | base64 -d > $file", - ], $server); - - if (config('app.env') == 'local') { - // ray($yaml); - } - } - } -} function setup_default_redirect_404(string|null $redirect_url, Server $server) { $traefik_dynamic_conf_path = $server->proxyPath() . "/dynamic"; From 14908280691422d290eb1565557400260a0b5cf4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 17:31:28 +0100 Subject: [PATCH 35/74] feat: dynamic configuration for caddy --- .../Proxy/DynamicConfigurationNavbar.php | 8 ++++ .../Server/Proxy/NewDynamicConfiguration.php | 37 +++++++++++++------ app/Models/Server.php | 10 ++++- .../views/components/server/sidebar.blade.php | 4 +- .../proxy/dynamic-configurations.blade.php | 4 +- .../proxy/new-dynamic-configuration.blade.php | 2 +- 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php index aee61f7de..a9c01daed 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php @@ -15,8 +15,16 @@ public function delete(string $fileName) { $server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first(); $proxy_path = $server->proxyPath(); + $proxy_type = $server->proxyType(); $file = str_replace('|', '.', $fileName); + if ($proxy_type === 'CADDY' && $file === "Caddyfile") { + $this->dispatch('error', 'Cannot delete Caddyfile.'); + return; + } instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server); + if ($proxy_type === 'CADDY') { + $server->reloadCaddy(); + } $this->dispatch('success', 'File deleted.'); $this->dispatch('loadDynamicConfigurations'); $this->dispatch('refresh'); diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php index 302e603b3..06180d947 100644 --- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php +++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php @@ -29,7 +29,6 @@ public function addDynamicConfiguration() 'fileName' => 'required', 'value' => 'required', ]); - if (data_get($this->parameters, 'server_uuid')) { $this->server = Server::ownedByCurrentTeam()->whereUuid(data_get($this->parameters, 'server_uuid'))->first(); } @@ -39,14 +38,21 @@ public function addDynamicConfiguration() if (is_null($this->server)) { return redirect()->route('server.index'); } - if (!str($this->fileName)->endsWith('.yaml') && !str($this->fileName)->endsWith('.yml')) { - $this->fileName = "{$this->fileName}.yaml"; + $proxy_type = $this->server->proxyType(); + if ($proxy_type === 'TRAEFIK_V2') { + if (!str($this->fileName)->endsWith('.yaml') && !str($this->fileName)->endsWith('.yml')) { + $this->fileName = "{$this->fileName}.yaml"; + } + if ($this->fileName === 'coolify.yaml') { + $this->dispatch('error', 'File name is reserved.'); + return; + } + } else if ($proxy_type === 'CADDY') { + if (!str($this->fileName)->endsWith('.caddy')) { + $this->fileName = "{$this->fileName}.caddy"; + } } - if ($this->fileName === 'coolify.yaml') { - $this->dispatch('error', 'File name is reserved.'); - return; - } - $proxy_path = $this->proxyPath(); + $proxy_path = $this->server->proxyPath(); $file = "{$proxy_path}/dynamic/{$this->fileName}"; if ($this->newFile) { $exists = instant_remote_process(["test -f $file && echo 1 || echo 0"], $this->server); @@ -55,11 +61,18 @@ public function addDynamicConfiguration() return; } } - $yaml = Yaml::parse($this->value); - $yaml = Yaml::dump($yaml, 10, 2); - $this->value = $yaml; + if ($proxy_type === 'TRAEFIK_V2') { + $yaml = Yaml::parse($this->value); + $yaml = Yaml::dump($yaml, 10, 2); + $this->value = $yaml; + } $base64_value = base64_encode($this->value); - instant_remote_process(["echo '{$base64_value}' | base64 -d > {$file}"], $this->server); + instant_remote_process([ + "echo '{$base64_value}' | base64 -d > {$file}", + ], $this->server); + if ($proxy_type === 'CADDY') { + $this->server->reloadCaddy(); + } $this->dispatch('loadDynamicConfigurations'); $this->dispatch('dynamic-configuration-added'); $this->dispatch('success', 'Dynamic configuration saved.'); diff --git a/app/Models/Server.php b/app/Models/Server.php index d2c412d9e..3cf20d7ae 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -249,8 +249,9 @@ public function setupDynamicProxyConfiguration() if (empty($settings->fqdn)) { instant_remote_process([ "rm -f $file", - "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile", ], $this); + $this->reloadCaddy(); + } else { $url = Url::fromString($settings->fqdn); $host = $url->getHost(); @@ -262,12 +263,17 @@ public function setupDynamicProxyConfiguration() $base64 = base64_encode($caddy_file); instant_remote_process([ "echo '$base64' | base64 -d > $file", - "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile", ], $this); + $this->reloadCaddy(); } } } } + public function reloadCaddy() { + return instant_remote_process([ + "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile", + ], $this); + } public function proxyPath() { $base_path = config('coolify.base_config_path'); diff --git a/resources/views/components/server/sidebar.blade.php b/resources/views/components/server/sidebar.blade.php index 94c8c9755..ef2b4b8e7 100644 --- a/resources/views/components/server/sidebar.blade.php +++ b/resources/views/components/server/sidebar.blade.php @@ -5,12 +5,12 @@ @if ($server->proxyType() !== 'NONE') - @if ($server->proxyType() === 'TRAEFIK_V2') + {{-- @if ($server->proxyType() === 'TRAEFIK_V2') --}} - @endif + {{-- @endif --}} diff --git a/resources/views/livewire/server/proxy/dynamic-configurations.blade.php b/resources/views/livewire/server/proxy/dynamic-configurations.blade.php index cb788dda4..0fd92f41e 100644 --- a/resources/views/livewire/server/proxy/dynamic-configurations.blade.php +++ b/resources/views/livewire/server/proxy/dynamic-configurations.blade.php @@ -19,7 +19,7 @@ class="font-normal text-white normal-case border-none rounded btn btn-primary bt Add
    -
    You can add dynamic Traefik configurations here.
    +
    You can add dynamic proxy configurations here.
    @@ -29,7 +29,7 @@ class="font-normal text-white normal-case border-none rounded btn btn-primary bt @if ($contents?->isNotEmpty()) @foreach ($contents as $fileName => $value)
    - @if (str_replace('|', '.', $fileName) === 'coolify.yaml') + @if (str_replace('|', '.', $fileName) === 'coolify.yaml' ||str_replace('|', '.', $fileName) === 'Caddyfile' )

    File: {{ str_replace('|', '.', $fileName) }}

    diff --git a/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php b/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php index 9e98b0ed4..110ba289c 100644 --- a/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php +++ b/resources/views/livewire/server/proxy/new-dynamic-configuration.blade.php @@ -1,5 +1,5 @@ - + Save From 52120e7a380b2a744de6255cc510235316f60469 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 20:17:37 +0100 Subject: [PATCH 36/74] Add dynamic proxy configuration setup in StartProxy.php and update proxyPath() in Server.php --- app/Actions/Proxy/StartProxy.php | 2 ++ app/Models/Server.php | 3 +++ 2 files changed, 5 insertions(+) diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index a781ab442..705197586 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -66,6 +66,8 @@ public function handle(Server $server, bool $async = true): string|Activity } catch (\Throwable $e) { ray($e); throw $e; + } finally { + $server->setupDynamicProxyConfiguration(); } } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 3cf20d7ae..b110189bb 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -279,6 +279,9 @@ public function proxyPath() $base_path = config('coolify.base_config_path'); $proxyType = $this->proxyType(); $proxy_path = "$base_path/proxy"; + // TODO: should use /traefik for already exisiting configurations? + // Should move everything except /caddy and /nginx to /traefik + // The code needs to be modified as well, so maybe it does not worth it if ($proxyType === ProxyTypes::TRAEFIK_V2->value) { $proxy_path = $proxy_path; } else if ($proxyType === ProxyTypes::CADDY->value) { From 8eacf677254adc5b24c030a12bab89b3e68c23da Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 20:25:35 +0100 Subject: [PATCH 37/74] Remove unnecessary code in StartProxy.php --- app/Actions/Proxy/StartProxy.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index 705197586..a781ab442 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -66,8 +66,6 @@ public function handle(Server $server, bool $async = true): string|Activity } catch (\Throwable $e) { ray($e); throw $e; - } finally { - $server->setupDynamicProxyConfiguration(); } } } From 6950966b06f70b9acc0436984b56fa745a7a5f26 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Mar 2024 20:39:41 +0100 Subject: [PATCH 38/74] Commented out reloadCaddy() calls in DynamicConfigurationNavbar.php, NewDynamicConfiguration.php, and Server.php --- app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php | 2 +- app/Livewire/Server/Proxy/NewDynamicConfiguration.php | 2 +- app/Models/Server.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php index a9c01daed..d27e574d7 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php @@ -23,7 +23,7 @@ public function delete(string $fileName) } instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server); if ($proxy_type === 'CADDY') { - $server->reloadCaddy(); + // $server->reloadCaddy(); } $this->dispatch('success', 'File deleted.'); $this->dispatch('loadDynamicConfigurations'); diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php index 06180d947..c433ce4fc 100644 --- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php +++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php @@ -71,7 +71,7 @@ public function addDynamicConfiguration() "echo '{$base64_value}' | base64 -d > {$file}", ], $this->server); if ($proxy_type === 'CADDY') { - $this->server->reloadCaddy(); + // $this->server->reloadCaddy(); } $this->dispatch('loadDynamicConfigurations'); $this->dispatch('dynamic-configuration-added'); diff --git a/app/Models/Server.php b/app/Models/Server.php index b110189bb..098b19931 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -250,7 +250,7 @@ public function setupDynamicProxyConfiguration() instant_remote_process([ "rm -f $file", ], $this); - $this->reloadCaddy(); + // $this->reloadCaddy(); } else { $url = Url::fromString($settings->fqdn); @@ -264,7 +264,7 @@ public function setupDynamicProxyConfiguration() instant_remote_process([ "echo '$base64' | base64 -d > $file", ], $this); - $this->reloadCaddy(); + // $this->reloadCaddy(); } } } From b576014d07e5c7d9c7af4a94f48f5e5b31fd6081 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 10:42:56 +0100 Subject: [PATCH 39/74] fix: reload caddy issue --- app/Livewire/Server/Proxy.php | 6 +- app/Models/Server.php | 110 +++++++++++++++++- bootstrap/helpers/proxy.php | 77 ------------ .../views/livewire/server/proxy.blade.php | 10 +- 4 files changed, 113 insertions(+), 90 deletions(-) diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php index 1e23605ff..4ccbf7d64 100644 --- a/app/Livewire/Server/Proxy.php +++ b/app/Livewire/Server/Proxy.php @@ -54,8 +54,7 @@ public function submit() SaveConfiguration::run($this->server, $this->proxy_settings); $this->server->proxy->redirect_url = $this->redirect_url; $this->server->save(); - - setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server); + $this->server->setupDefault404Redirect(); $this->dispatch('success', 'Proxy configuration saved.'); } catch (\Throwable $e) { return handleError($e, $this); @@ -66,6 +65,9 @@ public function reset_proxy_configuration() { try { $this->proxy_settings = CheckConfiguration::run($this->server, true); + SaveConfiguration::run($this->server, $this->proxy_settings); + $this->server->save(); + $this->dispatch('success', 'Proxy configuration saved.'); } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Models/Server.php b/app/Models/Server.php index 098b19931..f067523df 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -120,6 +120,106 @@ public function addInitialNetwork() } } } + public function setupDefault404Redirect() + { + $dynamic_conf_path = $this->proxyPath() . "/dynamic"; + $proxy_type = $this->proxyType(); + $redirect_url = $this->proxy->redirect_url; + + if ($proxy_type === 'TRAEFIK_V2') { + $default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml"; + } else if ($proxy_type === 'CADDY') { + $default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy"; + } + if (empty($redirect_url)) { + instant_remote_process([ + "mkdir -p $dynamic_conf_path", + "rm -f $default_redirect_file", + ], $this); + return; + } + if ($proxy_type === 'TRAEFIK_V2') { + $dynamic_conf = [ + 'http' => + [ + 'routers' => + [ + 'catchall' => + [ + 'entryPoints' => [ + 0 => 'http', + 1 => 'https', + ], + 'service' => 'noop', + 'rule' => "HostRegexp(`{catchall:.*}`)", + 'priority' => 1, + 'middlewares' => [ + 0 => 'redirect-regexp@file', + ], + ], + ], + 'services' => + [ + 'noop' => + [ + 'loadBalancer' => + [ + 'servers' => + [ + 0 => + [ + 'url' => '', + ], + ], + ], + ], + ], + 'middlewares' => + [ + 'redirect-regexp' => + [ + 'redirectRegex' => + [ + 'regex' => '(.*)', + 'replacement' => $redirect_url, + 'permanent' => false, + ], + ], + ], + ], + ]; + $conf = Yaml::dump($dynamic_conf, 12, 2); + $conf = + "# This file is automatically generated by Coolify.\n" . + "# Do not edit it manually (only if you know what are you doing).\n\n" . + $conf; + + $base64 = base64_encode($conf); + } else if ($proxy_type === 'CADDY') { + $conf = ":80, :443 { + redir $redirect_url +}"; +ray($conf); + $conf = + "# This file is automatically generated by Coolify.\n" . + "# Do not edit it manually (only if you know what are you doing).\n\n" . + $conf; + $base64 = base64_encode($conf); + } + + + instant_remote_process([ + "mkdir -p $dynamic_conf_path", + "echo '$base64' | base64 -d > $default_redirect_file", + ], $this); + + if (config('app.env') == 'local') { + ray($conf); + } + if ($proxy_type === 'CADDY') { + $this->reloadCaddy(); + } + } public function setupDynamicProxyConfiguration() { $settings = InstanceSettings::get(); @@ -250,8 +350,7 @@ public function setupDynamicProxyConfiguration() instant_remote_process([ "rm -f $file", ], $this); - // $this->reloadCaddy(); - + $this->reloadCaddy(); } else { $url = Url::fromString($settings->fqdn); $host = $url->getHost(); @@ -264,14 +363,15 @@ public function setupDynamicProxyConfiguration() instant_remote_process([ "echo '$base64' | base64 -d > $file", ], $this); - // $this->reloadCaddy(); + $this->reloadCaddy(); } } } } - public function reloadCaddy() { + public function reloadCaddy() + { return instant_remote_process([ - "docker exec coolify-proxy caddy reload --config /dynamic/Caddyfile", + "docker exec coolify-proxy caddy reload --config /config/caddy/Caddyfile.autosave", ], $this); } public function proxyPath() diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index 2bdffaf63..1eea1893e 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -218,80 +218,3 @@ function generate_default_proxy_configuration(Server $server) SaveConfiguration::run($server, $config); return $config; } - -function setup_default_redirect_404(string|null $redirect_url, Server $server) -{ - $traefik_dynamic_conf_path = $server->proxyPath() . "/dynamic"; - $traefik_default_redirect_file = "$traefik_dynamic_conf_path/default_redirect_404.yaml"; - if (empty($redirect_url)) { - instant_remote_process([ - "mkdir -p $traefik_dynamic_conf_path", - "rm -f $traefik_default_redirect_file", - ], $server); - } else { - $traefik_dynamic_conf = [ - 'http' => - [ - 'routers' => - [ - 'catchall' => - [ - 'entryPoints' => [ - 0 => 'http', - 1 => 'https', - ], - 'service' => 'noop', - 'rule' => "HostRegexp(`{catchall:.*}`)", - 'priority' => 1, - 'middlewares' => [ - 0 => 'redirect-regexp@file', - ], - ], - ], - 'services' => - [ - 'noop' => - [ - 'loadBalancer' => - [ - 'servers' => - [ - 0 => - [ - 'url' => '', - ], - ], - ], - ], - ], - 'middlewares' => - [ - 'redirect-regexp' => - [ - 'redirectRegex' => - [ - 'regex' => '(.*)', - 'replacement' => $redirect_url, - 'permanent' => false, - ], - ], - ], - ], - ]; - $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2); - $yaml = - "# This file is automatically generated by Coolify.\n" . - "# Do not edit it manually (only if you know what are you doing).\n\n" . - $yaml; - - $base64 = base64_encode($yaml); - instant_remote_process([ - "mkdir -p $traefik_dynamic_conf_path", - "echo '$base64' | base64 -d > $traefik_default_redirect_file", - ], $server); - - if (config('app.env') == 'local') { - ray($yaml); - } - } -} diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php index ba3909739..60828ec3d 100644 --- a/resources/views/livewire/server/proxy.blade.php +++ b/resources/views/livewire/server/proxy.blade.php @@ -13,7 +13,8 @@ Save
    -
    +
    Before switching proxies, please read this.
    @if ($server->proxyType() === 'TRAEFIK_V2')
    Traefik v2
    @elseif ($server->proxyType() === 'CADDY') @@ -26,11 +27,8 @@ configurations.
    @endif - @if ($server->proxyType() === 'TRAEFIK_V2') - - @endif +
    From 6ef79f52138e003c033718f81cf822d830b6a062 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 10:57:07 +0100 Subject: [PATCH 40/74] Refactor database cleanup command to include dry-run mode --- app/Console/Commands/CleanupDatabase.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/CleanupDatabase.php b/app/Console/Commands/CleanupDatabase.php index 60e6ea901..110e49c49 100644 --- a/app/Console/Commands/CleanupDatabase.php +++ b/app/Console/Commands/CleanupDatabase.php @@ -12,9 +12,13 @@ class CleanupDatabase extends Command public function handle() { - echo "Running database cleanup...\n"; + if ($this->option('yes')) { + echo "Running database cleanup...\n"; + } else { + echo "Running database cleanup in dry-run mode...\n"; + } $keep_days = 60; - + echo "Keep days: $keep_days\n"; // Cleanup failed jobs table $failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(7)); $count = $failed_jobs->count(); From 85c36df2a3f5aca70b05d8fe16e637a9b5fbaa6d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 10:58:28 +0100 Subject: [PATCH 41/74] Refactor database cleanup queries to keep the last 10 entries --- app/Console/Commands/CleanupDatabase.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Console/Commands/CleanupDatabase.php b/app/Console/Commands/CleanupDatabase.php index 110e49c49..0c8f9b884 100644 --- a/app/Console/Commands/CleanupDatabase.php +++ b/app/Console/Commands/CleanupDatabase.php @@ -36,7 +36,8 @@ public function handle() } // Cleanup activity_log table - $activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days)); + // but keep the last 10 + $activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10); $count = $activity_log->count(); echo "Delete $count entries from activity_log.\n"; if ($this->option('yes')) { @@ -44,7 +45,7 @@ public function handle() } // Cleanup application_deployment_queues table - $application_deployment_queues = DB::table('application_deployment_queues')->where('created_at', '<', now()->subDays($keep_days)); + $application_deployment_queues = DB::table('application_deployment_queues')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10); $count = $application_deployment_queues->count(); echo "Delete $count entries from application_deployment_queues.\n"; if ($this->option('yes')) { @@ -52,7 +53,7 @@ public function handle() } // Cleanup webhooks table - $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days)); + $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10); $count = $webhooks->count(); echo "Delete $count entries from webhooks.\n"; if ($this->option('yes')) { From b3d15f91e49fc28820326fa53d2351a1f66f292d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 10:58:31 +0100 Subject: [PATCH 42/74] Remove skip(10) from activity_log and webhooks cleanup --- app/Console/Commands/CleanupDatabase.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Console/Commands/CleanupDatabase.php b/app/Console/Commands/CleanupDatabase.php index 0c8f9b884..495b365ee 100644 --- a/app/Console/Commands/CleanupDatabase.php +++ b/app/Console/Commands/CleanupDatabase.php @@ -36,7 +36,6 @@ public function handle() } // Cleanup activity_log table - // but keep the last 10 $activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10); $count = $activity_log->count(); echo "Delete $count entries from activity_log.\n"; @@ -53,7 +52,7 @@ public function handle() } // Cleanup webhooks table - $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10); + $webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days)); $count = $webhooks->count(); echo "Delete $count entries from webhooks.\n"; if ($this->option('yes')) { From 2509406d1c1c80a17ff87f7b2f2a4958e3e831d3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 11:22:02 +0100 Subject: [PATCH 43/74] fix: startproxy event fix: add data to async remove processes --- .../CoolifyTask/PrepareCoolifyTask.php | 2 +- app/Actions/CoolifyTask/RunRemoteProcess.php | 18 ++++++++++++---- app/Actions/Proxy/StartProxy.php | 5 +++-- app/Data/CoolifyTaskArgs.php | 1 + app/Events/ProxyStarted.php | 16 ++++++++++++++ app/Jobs/CoolifyTask.php | 6 ++++-- app/Listeners/ProxyStartedNotification.php | 21 +++++++++++++++++++ app/Providers/EventServiceProvider.php | 8 ++++--- bootstrap/helpers/remoteProcess.php | 4 +++- .../views/livewire/server/proxy.blade.php | 5 ++++- 10 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 app/Events/ProxyStarted.php create mode 100644 app/Listeners/ProxyStartedNotification.php diff --git a/app/Actions/CoolifyTask/PrepareCoolifyTask.php b/app/Actions/CoolifyTask/PrepareCoolifyTask.php index b5b5a8853..e6a549756 100644 --- a/app/Actions/CoolifyTask/PrepareCoolifyTask.php +++ b/app/Actions/CoolifyTask/PrepareCoolifyTask.php @@ -39,7 +39,7 @@ public function __construct(CoolifyTaskArgs $remoteProcessArgs) public function __invoke(): Activity { - $job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish); + $job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data); dispatch($job); $this->activity->refresh(); return $this->activity; diff --git a/app/Actions/CoolifyTask/RunRemoteProcess.php b/app/Actions/CoolifyTask/RunRemoteProcess.php index 327d46c68..737b4babe 100644 --- a/app/Actions/CoolifyTask/RunRemoteProcess.php +++ b/app/Actions/CoolifyTask/RunRemoteProcess.php @@ -21,6 +21,8 @@ class RunRemoteProcess public $call_event_on_finish = null; + public $call_event_data = null; + protected $time_start; protected $current_time; @@ -34,7 +36,7 @@ class RunRemoteProcess /** * Create a new job instance. */ - public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null) + public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null, $call_event_data = null) { if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) { @@ -45,6 +47,7 @@ public function __construct(Activity $activity, bool $hide_from_output = false, $this->hide_from_output = $hide_from_output; $this->ignore_errors = $ignore_errors; $this->call_event_on_finish = $call_event_on_finish; + $this->call_event_data = $call_event_data; } public static function decodeOutput(?Activity $activity = null): string @@ -111,9 +114,16 @@ public function __invoke(): ProcessResult } if ($this->call_event_on_finish) { try { - event(resolve("App\\Events\\$this->call_event_on_finish", [ - 'userId' => $this->activity->causer_id, - ])); + ray($this->call_event_data); + if ($this->call_event_data) { + event(resolve("App\\Events\\$this->call_event_on_finish", [ + "data" => $this->call_event_data, + ])); + } else { + event(resolve("App\\Events\\$this->call_event_on_finish", [ + 'userId' => $this->activity->causer_id, + ])); + } } catch (\Throwable $e) { ray($e); } diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index a781ab442..ab635fc65 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -2,7 +2,7 @@ namespace App\Actions\Proxy; -use App\Events\ProxyStatusChanged; +use App\Events\ProxyStarted; use App\Models\Server; use Illuminate\Support\Str; use Lorisleiva\Actions\Concerns\AsAction; @@ -54,13 +54,14 @@ public function handle(Server $server, bool $async = true): string|Activity } if ($async) { - $activity = remote_process($commands, $server); + $activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server); return $activity; } else { instant_remote_process($commands, $server); $server->proxy->set('status', 'running'); $server->proxy->set('type', $proxyType); $server->save(); + ProxyStarted::dispatch($server); return 'OK'; } } catch (\Throwable $e) { diff --git a/app/Data/CoolifyTaskArgs.php b/app/Data/CoolifyTaskArgs.php index cc717561f..e1e43f2f0 100644 --- a/app/Data/CoolifyTaskArgs.php +++ b/app/Data/CoolifyTaskArgs.php @@ -21,6 +21,7 @@ public function __construct( public ?string $status = null , public bool $ignore_errors = false, public $call_event_on_finish = null, + public $call_event_data = null ) { if(is_null($status)){ $this->status = ProcessStatus::QUEUED->value; diff --git a/app/Events/ProxyStarted.php b/app/Events/ProxyStarted.php new file mode 100644 index 000000000..c77fe08b0 --- /dev/null +++ b/app/Events/ProxyStarted.php @@ -0,0 +1,16 @@ + $this->activity, 'ignore_errors' => $this->ignore_errors, - 'call_event_on_finish' => $this->call_event_on_finish + 'call_event_on_finish' => $this->call_event_on_finish, + 'call_event_data' => $this->call_event_data ]); $remote_process(); diff --git a/app/Listeners/ProxyStartedNotification.php b/app/Listeners/ProxyStartedNotification.php new file mode 100644 index 000000000..62131c22a --- /dev/null +++ b/app/Listeners/ProxyStartedNotification.php @@ -0,0 +1,21 @@ +server = data_get($event, 'server'); + $this->server->setupDefault404Redirect(); + $this->server->setupDynamicProxyConfiguration(); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a9b4496b4..0e9be72d1 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,8 +2,10 @@ namespace App\Providers; +use App\Events\ProxyStarted; use App\Listeners\MaintenanceModeDisabledNotification; use App\Listeners\MaintenanceModeEnabledNotification; +use App\Listeners\ProxyStartedNotification; use Illuminate\Foundation\Events\MaintenanceModeDisabled; use Illuminate\Foundation\Events\MaintenanceModeEnabled; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -17,9 +19,9 @@ class EventServiceProvider extends ServiceProvider MaintenanceModeDisabled::class => [ MaintenanceModeDisabledNotification::class, ], - // Registered::class => [ - // SendEmailVerificationNotification::class, - // ], + ProxyStarted::class => [ + ProxyStartedNotification::class, + ], ]; public function boot(): void { diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index 13905391e..0aba82ea1 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -24,7 +24,8 @@ function remote_process( ?string $type_uuid = null, ?Model $model = null, bool $ignore_errors = false, - $callEventOnFinish = null + $callEventOnFinish = null, + $callEventData = null ): Activity { if (is_null($type)) { $type = ActivityTypes::INLINE->value; @@ -50,6 +51,7 @@ function remote_process( model: $model, ignore_errors: $ignore_errors, call_event_on_finish: $callEventOnFinish, + call_event_data: $callEventData, ), ])(); } diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php index 60828ec3d..49bf654a6 100644 --- a/resources/views/livewire/server/proxy.blade.php +++ b/resources/views/livewire/server/proxy.blade.php @@ -13,7 +13,10 @@ Save
    -
    Before switching proxies, please read + + Before switching proxies, please read this.
    @if ($server->proxyType() === 'TRAEFIK_V2')
    Traefik v2
    From f8055e7976d2baed68d1485fd81ff19d40ace4eb Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 11:32:40 +0100 Subject: [PATCH 44/74] fix: /realtime endpoint --- routes/api.php | 1 - routes/web.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/routes/api.php b/routes/api.php index c0ea836a6..c7e3598a3 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,7 +2,6 @@ use App\Http\Controllers\Api\Deploy; use App\Http\Controllers\Api\Domains; -use App\Http\Controllers\Api\Project; use App\Http\Controllers\Api\Resources; use App\Http\Controllers\Api\Server; use App\Http\Controllers\Api\Team; diff --git a/routes/web.php b/routes/web.php index c442f6c75..0e9d3c478 100644 --- a/routes/web.php +++ b/routes/web.php @@ -85,7 +85,7 @@ Route::get('/admin', AdminIndex::class)->name('admin.index'); Route::post('/forgot-password', [Controller::class, 'forgot_password'])->name('password.forgot'); -Route::get('/api/v1/test/realtime', [Controller::class, 'realtime_test'])->middleware('auth'); +Route::get('/realtime', [Controller::class, 'realtime_test'])->middleware('auth'); Route::get('/waitlist', WaitlistIndex::class)->name('waitlist.index'); Route::get('/verify', [Controller::class, 'verify'])->middleware('auth')->name('verify.email'); Route::get('/email/verify/{id}/{hash}', [Controller::class, 'email_verify'])->middleware(['auth'])->name('verify.verify'); From bcc61b0d8b6d2fd05121b20a4490ab78c9374886 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 11:34:57 +0100 Subject: [PATCH 45/74] Add reverse proxy configuration for coolify-realtime --- app/Models/Server.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Models/Server.php b/app/Models/Server.php index f067523df..5ad5130d7 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -357,6 +357,9 @@ public function setupDynamicProxyConfiguration() $schema = $url->getScheme(); $caddy_file = " $schema://$host { + handle /app/* { + reverse_proxy coolify-realtime:6001 + } reverse_proxy coolify:80 }"; $base64 = base64_encode($caddy_file); From 1835a914673b54527d19cbb015a07a2181a4498d Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 12:30:40 +0100 Subject: [PATCH 46/74] fix: proxy switch --- app/Actions/CoolifyTask/RunRemoteProcess.php | 1 - app/Events/ProxyStarted.php | 4 +- app/Listeners/ProxyStartedNotification.php | 2 +- .../Server/Proxy/DynamicConfigurations.php | 12 +- app/Livewire/Settings/Configuration.php | 7 +- app/Models/Server.php | 241 +++++++++--------- 6 files changed, 133 insertions(+), 134 deletions(-) diff --git a/app/Actions/CoolifyTask/RunRemoteProcess.php b/app/Actions/CoolifyTask/RunRemoteProcess.php index 737b4babe..2c90750e6 100644 --- a/app/Actions/CoolifyTask/RunRemoteProcess.php +++ b/app/Actions/CoolifyTask/RunRemoteProcess.php @@ -114,7 +114,6 @@ public function __invoke(): ProcessResult } if ($this->call_event_on_finish) { try { - ray($this->call_event_data); if ($this->call_event_data) { event(resolve("App\\Events\\$this->call_event_on_finish", [ "data" => $this->call_event_data, diff --git a/app/Events/ProxyStarted.php b/app/Events/ProxyStarted.php index c77fe08b0..a4e053171 100644 --- a/app/Events/ProxyStarted.php +++ b/app/Events/ProxyStarted.php @@ -2,7 +2,6 @@ namespace App\Events; -use App\Models\Server; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; @@ -10,7 +9,8 @@ class ProxyStarted { use Dispatchable, InteractsWithSockets, SerializesModels; - public function __construct(public Server $server) + public function __construct(public $data) { + } } diff --git a/app/Listeners/ProxyStartedNotification.php b/app/Listeners/ProxyStartedNotification.php index 62131c22a..e6be605ce 100644 --- a/app/Listeners/ProxyStartedNotification.php +++ b/app/Listeners/ProxyStartedNotification.php @@ -14,7 +14,7 @@ public function __construct() public function handle(ProxyStarted $event): void { - $this->server = data_get($event, 'server'); + $this->server = data_get($event, 'data'); $this->server->setupDefault404Redirect(); $this->server->setupDynamicProxyConfiguration(); } diff --git a/app/Livewire/Server/Proxy/DynamicConfigurations.php b/app/Livewire/Server/Proxy/DynamicConfigurations.php index b9e89321c..36219dc7e 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurations.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurations.php @@ -11,7 +11,15 @@ class DynamicConfigurations extends Component public ?Server $server = null; public $parameters = []; public Collection $contents; - protected $listeners = ['loadDynamicConfigurations', 'refresh' => '$refresh']; + public function getListeners() + { + $teamId = auth()->user()->currentTeam()->id; + return [ + "echo-private:team.{$teamId},ProxyStatusChanged" => 'loadDynamicConfigurations', + 'loadDynamicConfigurations', + 'refresh' => '$refresh' + ]; + } protected $rules = [ 'contents.*' => 'nullable|string', ]; @@ -24,6 +32,7 @@ public function loadDynamicConfigurations() $files = $files->sort(); if ($files->contains('coolify.yaml')) { $files = $files->filter(fn ($file) => $file !== 'coolify.yaml')->prepend('coolify.yaml'); + $files = $files->filter(fn ($file) => $file !== 'Caddyfile')->prepend('Caddyfile'); } $contents = collect([]); foreach ($files as $file) { @@ -31,6 +40,7 @@ public function loadDynamicConfigurations() $contents[$without_extension] = instant_remote_process(["cat {$proxy_path}/dynamic/{$file}"], $this->server); } $this->contents = $contents; + $this->dispatch('refresh'); } public function mount() { diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php index f1c732a97..79ff3bcb6 100644 --- a/app/Livewire/Settings/Configuration.php +++ b/app/Livewire/Settings/Configuration.php @@ -75,12 +75,7 @@ public function submit() $this->settings->save(); $this->server = Server::findOrFail(0); - $this->setup_instance_fqdn(); + $this->server->setupDynamicProxyConfiguration(); $this->dispatch('success', 'Instance settings updated successfully!'); } - - private function setup_instance_fqdn() - { - $this->server->setupDynamicProxyConfiguration(); - } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 5ad5130d7..f361adc7b 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -3,7 +3,6 @@ namespace App\Models; use App\Actions\Server\InstallDocker; -use App\Enums\ProxyStatus; use App\Enums\ProxyTypes; use App\Notifications\Server\Revived; use App\Notifications\Server\Unreachable; @@ -125,7 +124,6 @@ public function setupDefault404Redirect() $dynamic_conf_path = $this->proxyPath() . "/dynamic"; $proxy_type = $this->proxyType(); $redirect_url = $this->proxy->redirect_url; - if ($proxy_type === 'TRAEFIK_V2') { $default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml"; } else if ($proxy_type === 'CADDY') { @@ -199,7 +197,6 @@ public function setupDefault404Redirect() $conf = ":80, :443 { redir $redirect_url }"; -ray($conf); $conf = "# This file is automatically generated by Coolify.\n" . "# Do not edit it manually (only if you know what are you doing).\n\n" . @@ -224,150 +221,148 @@ public function setupDynamicProxyConfiguration() { $settings = InstanceSettings::get(); $dynamic_config_path = $this->proxyPath() . "/dynamic"; - if ($this) { - if ($this->proxyType() === 'TRAEFIK_V2') { - $file = "$dynamic_config_path/coolify.yaml"; - if (empty($settings->fqdn)) { - instant_remote_process([ - "rm -f $file", - ], $this); - } else { - $url = Url::fromString($settings->fqdn); - $host = $url->getHost(); - $schema = $url->getScheme(); - $traefik_dynamic_conf = [ - 'http' => + if ($this->proxyType() === 'TRAEFIK_V2') { + $file = "$dynamic_config_path/coolify.yaml"; + if (empty($settings->fqdn)) { + instant_remote_process([ + "rm -f $file", + ], $this); + } else { + $url = Url::fromString($settings->fqdn); + $host = $url->getHost(); + $schema = $url->getScheme(); + $traefik_dynamic_conf = [ + 'http' => + [ + 'middlewares' => [ + 'redirect-to-https' => [ + 'redirectscheme' => [ + 'scheme' => 'https', + ], + ], + 'gzip' => [ + 'compress' => true, + ], + ], + 'routers' => [ - 'middlewares' => [ - 'redirect-to-https' => [ - 'redirectscheme' => [ - 'scheme' => 'https', - ], - ], - 'gzip' => [ - 'compress' => true, - ], - ], - 'routers' => + 'coolify-http' => [ - 'coolify-http' => - [ - 'middlewares' => [ - 0 => 'gzip', - ], - 'entryPoints' => [ - 0 => 'http', - ], - 'service' => 'coolify', - 'rule' => "Host(`{$host}`)", + 'middlewares' => [ + 0 => 'gzip', ], - 'coolify-realtime-ws' => - [ - 'entryPoints' => [ - 0 => 'http', - ], - 'service' => 'coolify-realtime', - 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", + 'entryPoints' => [ + 0 => 'http', ], + 'service' => 'coolify', + 'rule' => "Host(`{$host}`)", ], - 'services' => + 'coolify-realtime-ws' => [ - 'coolify' => + 'entryPoints' => [ + 0 => 'http', + ], + 'service' => 'coolify-realtime', + 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", + ], + ], + 'services' => + [ + 'coolify' => + [ + 'loadBalancer' => [ - 'loadBalancer' => + 'servers' => [ - 'servers' => + 0 => [ - 0 => - [ - 'url' => 'http://coolify:80', - ], + 'url' => 'http://coolify:80', ], ], ], - 'coolify-realtime' => + ], + 'coolify-realtime' => + [ + 'loadBalancer' => [ - 'loadBalancer' => + 'servers' => [ - 'servers' => + 0 => [ - 0 => - [ - 'url' => 'http://coolify-realtime:6001', - ], + 'url' => 'http://coolify-realtime:6001', ], ], ], ], ], + ], + ]; + + if ($schema === 'https') { + $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [ + 0 => 'redirect-to-https', ]; - if ($schema === 'https') { - $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [ - 0 => 'redirect-to-https', - ]; - - $traefik_dynamic_conf['http']['routers']['coolify-https'] = [ - 'entryPoints' => [ - 0 => 'https', - ], - 'service' => 'coolify', - 'rule' => "Host(`{$host}`)", - 'tls' => [ - 'certresolver' => 'letsencrypt', - ], - ]; - $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [ - 'entryPoints' => [ - 0 => 'https', - ], - 'service' => 'coolify-realtime', - 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", - 'tls' => [ - 'certresolver' => 'letsencrypt', - ], - ]; - } - $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2); - $yaml = - "# This file is automatically generated by Coolify.\n" . - "# Do not edit it manually (only if you know what are you doing).\n\n" . - $yaml; - - $base64 = base64_encode($yaml); - instant_remote_process([ - "mkdir -p $dynamic_config_path", - "echo '$base64' | base64 -d > $file", - ], $this); - - if (config('app.env') == 'local') { - // ray($yaml); - } + $traefik_dynamic_conf['http']['routers']['coolify-https'] = [ + 'entryPoints' => [ + 0 => 'https', + ], + 'service' => 'coolify', + 'rule' => "Host(`{$host}`)", + 'tls' => [ + 'certresolver' => 'letsencrypt', + ], + ]; + $traefik_dynamic_conf['http']['routers']['coolify-realtime-wss'] = [ + 'entryPoints' => [ + 0 => 'https', + ], + 'service' => 'coolify-realtime', + 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", + 'tls' => [ + 'certresolver' => 'letsencrypt', + ], + ]; } - } else if ($this->proxyType() === 'CADDY') { - $file = "$dynamic_config_path/coolify.caddy"; - if (empty($settings->fqdn)) { - instant_remote_process([ - "rm -f $file", - ], $this); - $this->reloadCaddy(); - } else { - $url = Url::fromString($settings->fqdn); - $host = $url->getHost(); - $schema = $url->getScheme(); - $caddy_file = " + $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2); + $yaml = + "# This file is automatically generated by Coolify.\n" . + "# Do not edit it manually (only if you know what are you doing).\n\n" . + $yaml; + + $base64 = base64_encode($yaml); + instant_remote_process([ + "mkdir -p $dynamic_config_path", + "echo '$base64' | base64 -d > $file", + ], $this); + + if (config('app.env') == 'local') { + // ray($yaml); + } + } + } else if ($this->proxyType() === 'CADDY') { + $file = "$dynamic_config_path/coolify.caddy"; + if (empty($settings->fqdn)) { + instant_remote_process([ + "rm -f $file", + ], $this); + $this->reloadCaddy(); + } else { + $url = Url::fromString($settings->fqdn); + $host = $url->getHost(); + $schema = $url->getScheme(); + $caddy_file = " $schema://$host { handle /app/* { reverse_proxy coolify-realtime:6001 } reverse_proxy coolify:80 }"; - $base64 = base64_encode($caddy_file); - instant_remote_process([ - "echo '$base64' | base64 -d > $file", - ], $this); - $this->reloadCaddy(); - } + $base64 = base64_encode($caddy_file); + instant_remote_process([ + "echo '$base64' | base64 -d > $file", + ], $this); + $this->reloadCaddy(); } } } @@ -396,16 +391,16 @@ public function proxyPath() } public function proxyType() { - $proxyType = $this->proxy->get('type'); - if ($proxyType === ProxyTypes::NONE->value) { - return $proxyType; - } + // $proxyType = $this->proxy->get('type'); + // if ($proxyType === ProxyTypes::NONE->value) { + // return $proxyType; + // } // if (is_null($proxyType)) { // $this->proxy->type = ProxyTypes::TRAEFIK_V2->value; // $this->proxy->status = ProxyStatus::EXITED->value; // $this->save(); // } - return $this->proxy->get('type'); + return data_get($this->proxy, 'type.type'); } public function scopeWithProxy(): Builder { From 4d181eef8ee4216202e2ab5fe52e0a666bfeca56 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Mar 2024 12:45:55 +0100 Subject: [PATCH 47/74] Refactor proxy type retrieval in Server and Proxy classes --- app/Livewire/Server/Proxy.php | 2 +- app/Models/Server.php | 2 +- resources/views/livewire/server/proxy.blade.php | 11 ++++++----- .../views/livewire/server/proxy/deploy.blade.php | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php index 4ccbf7d64..dab7f54be 100644 --- a/app/Livewire/Server/Proxy.php +++ b/app/Livewire/Server/Proxy.php @@ -21,7 +21,7 @@ class Proxy extends Component public function mount() { - $this->selectedProxy = data_get($this->server, 'proxy.type'); + $this->selectedProxy = $this->server->proxyType(); $this->redirect_url = data_get($this->server, 'proxy.redirect_url'); } diff --git a/app/Models/Server.php b/app/Models/Server.php index f361adc7b..c632520a1 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -400,7 +400,7 @@ public function proxyType() // $this->proxy->status = ProxyStatus::EXITED->value; // $this->save(); // } - return data_get($this->proxy, 'type.type'); + return data_get($this->proxy, 'type'); } public function scopeWithProxy(): Builder { diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php index 49bf654a6..f2d9b839e 100644 --- a/resources/views/livewire/server/proxy.blade.php +++ b/resources/views/livewire/server/proxy.blade.php @@ -1,5 +1,5 @@
    - @if (data_get($server, 'proxy.type')) + @if ($server->proxyType())
    @if ($selectedProxy !== 'NONE')
    @@ -13,10 +13,11 @@ Save
    -
    - - Before switching proxies, please read + + Before switching proxies, please read this.
    @if ($server->proxyType() === 'TRAEFIK_V2')
    Traefik v2
    diff --git a/resources/views/livewire/server/proxy/deploy.blade.php b/resources/views/livewire/server/proxy/deploy.blade.php index 7a27eeab3..85652c982 100644 --- a/resources/views/livewire/server/proxy/deploy.blade.php +++ b/resources/views/livewire/server/proxy/deploy.blade.php @@ -16,10 +16,10 @@

    - @if ($server->isFunctional() && data_get($server, 'proxy.type') !== 'NONE') + @if ($server->isFunctional() && $server->proxyType() !== 'NONE') @if (data_get($server, 'proxy.status') === 'running')
    - @if ($currentRoute === 'server.proxy' && $traefikDashboardAvailable) + @if ($currentRoute === 'server.proxy' && $traefikDashboardAvailable && $server->proxyType() === 'TRAEFIK_V2')
    @endforelse
    - @if ($isDatabase) + {{-- @if ($isDatabase)
    Swarm clusters are excluded from this type of resource at the moment. It will be activated soon. Stay tuned.
    - @endif + @endif --}} @endif @if ($current_step === 'destinations')