From d1c47a4062312208a2daea037b160fe6ab67cdf9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 9 Oct 2023 15:22:51 +0200 Subject: [PATCH 01/13] version++ --- config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/sentry.php b/config/sentry.php index 747522787..a2621713d 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // 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.71', + 'release' => '4.0.0-beta.72', // 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 b255224ee..4f50ad680 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Mon, 9 Oct 2023 15:49:48 +0200 Subject: [PATCH 02/13] wip: dockerimage --- app/Http/Livewire/Project/New/DockerImage.php | 73 +++++++++++++++++++ .../project/application/general.blade.php | 1 + .../project/new/docker-image.blade.php | 11 +++ .../livewire/project/new/select.blade.php | 10 +++ resources/views/project/new.blade.php | 2 + 5 files changed, 97 insertions(+) create mode 100644 app/Http/Livewire/Project/New/DockerImage.php create mode 100644 resources/views/livewire/project/new/docker-image.blade.php diff --git a/app/Http/Livewire/Project/New/DockerImage.php b/app/Http/Livewire/Project/New/DockerImage.php new file mode 100644 index 000000000..5e1107e4d --- /dev/null +++ b/app/Http/Livewire/Project/New/DockerImage.php @@ -0,0 +1,73 @@ +parameters = get_route_parameters(); + $this->query = request()->query(); + } + public function submit() + { + $this->validate([ + 'dockerImage' => 'required' + ]); + $image = Str::of($this->dockerImage)->before(':'); + $tag = Str::of($this->dockerImage)->after(':') ?: 'latest'; + $destination_uuid = $this->query['destination']; + $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); + if (!$destination) { + $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); + } + if (!$destination) { + throw new \Exception('Destination not found. What?!'); + } + $destination_class = $destination->getMorphClass(); + + $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); + $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + $application = Application::create([ + 'name' => 'docker-image-' . new Cuid2(7), + 'repository_project_id' => 0, + 'git_repository' => "coollabsio/coolify", + 'git_branch' => 'main', + 'build_pack' => 'dockerimage', + 'ports_exposes' => 80, + 'docker_registry_image_name' => $image, + 'docker_registry_image_tag' => $tag, + 'environment_id' => $environment->id, + 'destination_id' => $destination->id, + 'destination_type' => $destination_class, + 'health_check_enabled' => false, + ]); + + $fqdn = generateFqdn($destination->server, $application->uuid); + $application->update([ + 'name' => 'docker-image-' . $application->uuid, + 'fqdn' => $fqdn + ]); + + redirect()->route('project.application.configuration', [ + 'application_uuid' => $application->uuid, + 'environment_name' => $environment->name, + 'project_uuid' => $project->uuid, + ]); + } + public function render() + { + return view('livewire.project.new.docker-image'); + } +} diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index fb525a8dd..3654bd50b 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -24,6 +24,7 @@ + @if ($application->settings->is_static) diff --git a/resources/views/livewire/project/new/docker-image.blade.php b/resources/views/livewire/project/new/docker-image.blade.php new file mode 100644 index 000000000..3927247c7 --- /dev/null +++ b/resources/views/livewire/project/new/docker-image.blade.php @@ -0,0 +1,11 @@ +
+

Create a new Application

+
You can deploy an existing Docker Image from any Registry.
+
+
+

Docker Image

+ Save +
+ + +
diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php index 376f143dc..4e96cdf76 100644 --- a/resources/views/livewire/project/new/select.blade.php +++ b/resources/views/livewire/project/new/select.blade.php @@ -62,6 +62,16 @@ +
+
+
+ Based on an existing Docker Image +
+
+ You can deploy an existing Docker Image form any Registry. +
+
+

Databases

diff --git a/resources/views/project/new.blade.php b/resources/views/project/new.blade.php index 8d69f95e0..5b6ee1029 100644 --- a/resources/views/project/new.blade.php +++ b/resources/views/project/new.blade.php @@ -9,6 +9,8 @@ @elseif ($type === 'docker-compose-empty') + @elseif ($type === 'docker-image') + @else @endif From 3dab1eb92e9ef642fb0535be6a81d35ff888e4cf Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 9 Oct 2023 20:12:03 +0200 Subject: [PATCH 03/13] fix: server saving --- app/Models/Server.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/Models/Server.php b/app/Models/Server.php index 5cbd6deb5..a53d6ffe5 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -17,10 +17,14 @@ class Server extends BaseModel protected static function booted() { static::saving(function ($server) { - $server->forceFill([ - 'ip' => Str::of($server->ip)->trim(), - 'user' => Str::of($server->user)->trim(), - ]); + $payload = []; + if ($server->user) { + $payload['user'] = Str::of($server->user)->trim(); + } + if ($server->ip) { + $payload['ip'] = Str::of($server->ip)->trim(); + } + $server->forceFill($payload); }); static::created(function ($server) { From f2c32b9aebf6344e9a57eeeb97510b3fe0079e4f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 9 Oct 2023 20:37:42 +0200 Subject: [PATCH 04/13] fixes --- app/Jobs/ContainerStatusJob.php | 4 ++-- bootstrap/helpers/proxy.php | 2 +- .../views/livewire/project/database/backup-edit.blade.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php index 4f104f2ea..e175aeb8f 100644 --- a/app/Jobs/ContainerStatusJob.php +++ b/app/Jobs/ContainerStatusJob.php @@ -44,12 +44,12 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted public function handle() { try { - ray("checking server status for {$this->server->name}"); + // ray("checking server status for {$this->server->name}"); // ray()->clearAll(); $serverUptimeCheckNumber = $this->server->unreachable_count; $serverUptimeCheckNumberMax = 3; - ray('checking # ' . $serverUptimeCheckNumber); + // ray('checking # ' . $serverUptimeCheckNumber); if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) { if ($this->server->unreachable_email_sent === false) { ray('Server unreachable, sending notification...'); diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index f3ee1fa77..316628958 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -247,7 +247,7 @@ EOF; "mkdir -p $configuration_dir", "echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf", "echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml", - "docker compose --project-directory {$configuration_dir} up -d >/dev/null", + "docker compose --project-directory {$configuration_dir} up -d --remove-orphans >/dev/null", ], $database->destination->server); diff --git a/resources/views/livewire/project/database/backup-edit.blade.php b/resources/views/livewire/project/database/backup-edit.blade.php index f7f7afa8e..4450d209a 100644 --- a/resources/views/livewire/project/database/backup-edit.blade.php +++ b/resources/views/livewire/project/database/backup-edit.blade.php @@ -16,7 +16,7 @@ @if ($backup->save_s3)
- + @foreach ($s3s as $s3) @endforeach From 46f7ae9588c187abfdb65c0d6e34915451fb667a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 10:56:11 +0200 Subject: [PATCH 05/13] ui: updated dashboard --- app/Http/Livewire/Dashboard.php | 8 +- resources/views/livewire/dashboard.blade.php | 73 +++++++++++++++++-- resources/views/livewire/server/all.blade.php | 2 +- resources/views/project/resources.blade.php | 26 ++++--- resources/views/projects.blade.php | 16 ++-- 5 files changed, 95 insertions(+), 30 deletions(-) diff --git a/app/Http/Livewire/Dashboard.php b/app/Http/Livewire/Dashboard.php index 7c0505d8a..d411312d5 100644 --- a/app/Http/Livewire/Dashboard.php +++ b/app/Http/Livewire/Dashboard.php @@ -9,21 +9,21 @@ use Livewire\Component; class Dashboard extends Component { - public int $projects = 0; - public int $servers = 0; + public $projects = []; + public $servers = []; public int $s3s = 0; public int $resources = 0; public function mount() { - $this->servers = Server::ownedByCurrentTeam()->get()->count(); + $this->servers = Server::ownedByCurrentTeam()->get(); $this->s3s = S3Storage::ownedByCurrentTeam()->get()->count(); $projects = Project::ownedByCurrentTeam()->get(); foreach ($projects as $project) { $this->resources += $project->applications->count(); $this->resources += $project->postgresqls->count(); } - $this->projects = $projects->count(); + $this->projects = $projects; } // public function getIptables() // { diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php index c61f48d65..d5b18aa0f 100644 --- a/resources/views/livewire/dashboard.blade.php +++ b/resources/views/livewire/dashboard.blade.php @@ -3,7 +3,7 @@ @endif

Dashboard

-
Something useful will be here.
+
Your self-hosted environment
@if (request()->query->get('success'))
Servers
-
{{ $servers }}
+
{{ $servers->count() }}
Projects
-
{{ $projects }}
+
{{ $projects->count() }}
@@ -35,6 +35,69 @@
{{ $s3s }}
- - {{-- Get IPTABLES --}} +

Projects

+ @if ($projects->count() === 1) +
+ @else +
+ @endif + @foreach ($projects as $project) + + @endforeach +
+

Servers

+@if ($servers->count() === 1) + diff --git a/resources/views/livewire/server/all.blade.php b/resources/views/livewire/server/all.blade.php index 8ad9c82a3..8195eb2af 100644 --- a/resources/views/livewire/server/all.blade.php +++ b/resources/views/livewire/server/all.blade.php @@ -15,7 +15,7 @@ 'border-red-500' => !$server->settings->is_reachable, ])>
-
+
{{ $server->name }}
diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php index 213fa04cd..09faf85f4 100644 --- a/resources/views/project/resources.blade.php +++ b/resources/views/project/resources.blade.php @@ -2,11 +2,12 @@

Resources

- + - Add @if ($environment->can_delete_environment()) + @else + + + New @endif
@if ($environment->can_delete_environment()) -

No resources found.

+ + Add New Resource @endif
@foreach ($environment->applications->sortBy('name') as $application) @@ -54,13 +56,13 @@ @endforeach @foreach ($environment->services->sortBy('name') as $service) - -
-
{{ $service->name }}
-
{{ $service->description }}
-
-
- @endforeach + +
+
{{ $service->name }}
+
{{ $service->description }}
+
+
+ @endforeach
diff --git a/resources/views/projects.blade.php b/resources/views/projects.blade.php index a76607b9d..6de72d451 100644 --- a/resources/views/projects.blade.php +++ b/resources/views/projects.blade.php @@ -17,17 +17,17 @@ @forelse ($projects as $project)
-
- {{ $project->name }} - + - + From 8abfaa1967da919a36b8d44b1e2160f31e6c3974 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 10:57:56 +0200 Subject: [PATCH 06/13] fix: no env goto envs from dashboard --- resources/views/livewire/dashboard.blade.php | 80 +++++++++++--------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php index d5b18aa0f..7492984b4 100644 --- a/resources/views/livewire/dashboard.blade.php +++ b/resources/views/livewire/dashboard.blade.php @@ -44,12 +44,21 @@ @foreach ($projects as $project)
- -
{{ $project->name }}
-
- {{ $project->description }}
-
+ @if (data_get($project, 'environments.0.name')) + +
{{ $project->name }}
+
+ {{ $project->description }}
+
+ @else + +
{{ $project->name }}
+
+ {{ $project->description }}
+
+ @endif

Servers

@if ($servers->count() === 1) -
-@else -
+
+ @else + From 14d9c06dcdf01178a44e5c6cb6eeb3c7c0186fc3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 11:16:38 +0200 Subject: [PATCH 07/13] feat: able to deploy docker images --- .../Livewire/Project/Application/General.php | 4 ++ app/Http/Livewire/Project/New/DockerImage.php | 7 ++- app/Jobs/ApplicationDeploymentJob.php | 25 +++++++++- app/Models/Application.php | 5 +- .../components/applications/navbar.blade.php | 4 +- .../project/application/general.blade.php | 46 +++++++++++-------- .../project/new/docker-image.blade.php | 2 +- 7 files changed, 66 insertions(+), 27 deletions(-) diff --git a/app/Http/Livewire/Project/Application/General.php b/app/Http/Livewire/Project/Application/General.php index 52212dc9e..93fcd250b 100644 --- a/app/Http/Livewire/Project/Application/General.php +++ b/app/Http/Livewire/Project/Application/General.php @@ -49,6 +49,8 @@ class General extends Component 'application.ports_exposes' => 'required', 'application.ports_mappings' => 'nullable', 'application.dockerfile' => 'nullable', + 'application.docker_registry_image_name' => 'nullable', + 'application.docker_registry_image_tag' => 'nullable', ]; protected $validationAttributes = [ 'application.name' => 'name', @@ -67,6 +69,8 @@ class General extends Component 'application.ports_exposes' => 'Ports exposes', 'application.ports_mappings' => 'Ports mappings', 'application.dockerfile' => 'Dockerfile', + 'application.docker_registry_image_name' => 'Docker registry image name', + 'application.docker_registry_image_tag' => 'Docker registry image tag', ]; diff --git a/app/Http/Livewire/Project/New/DockerImage.php b/app/Http/Livewire/Project/New/DockerImage.php index 5e1107e4d..1081f3401 100644 --- a/app/Http/Livewire/Project/New/DockerImage.php +++ b/app/Http/Livewire/Project/New/DockerImage.php @@ -26,7 +26,11 @@ class DockerImage extends Component 'dockerImage' => 'required' ]); $image = Str::of($this->dockerImage)->before(':'); - $tag = Str::of($this->dockerImage)->after(':') ?: 'latest'; + if (Str::of($this->dockerImage)->contains(':')) { + $tag = Str::of($this->dockerImage)->after(':'); + } else { + $tag = 'latest'; + } $destination_uuid = $this->query['destination']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (!$destination) { @@ -39,6 +43,7 @@ class DockerImage extends Component $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + ray($image,$tag); $application = Application::create([ 'name' => 'docker-image-' . new Cuid2(7), 'repository_project_id' => 0, diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 57d89d932..a1dff9c93 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -45,6 +45,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private string $commit; private bool $force_rebuild; + private ?string $dockerImage = null; + private ?string $dockerImageTag = null; + private GithubApp|GitlabApp|string $source = 'other'; private StandaloneDocker|SwarmDocker $destination; private Server $server; @@ -135,6 +138,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted try { if ($this->application->dockerfile) { $this->deploy_simple_dockerfile(); + } else if ($this->application->build_pack === 'dockerimage') { + $this->deploy_dockerimage(); } else { if ($this->pull_request_id !== 0) { $this->deploy_pull_request(); @@ -245,6 +250,21 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->rolling_update(); } + private function deploy_dockerimage() + { + $this->dockerImage = $this->application->docker_registry_image_name; + $this->dockerImageTag = $this->application->docker_registry_image_tag; + ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'"); + $this->execute_remote_command( + [ + "echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'" + ], + ); + $this->production_image_name = Str::lower("{$this->dockerImage}:{$this->dockerImageTag}"); + $this->prepare_builder_image(); + $this->generate_compose_file(); + $this->rolling_update(); + } private function deploy() { $this->execute_remote_command( @@ -398,7 +418,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ); } - private function set_base_dir() { + private function set_base_dir() + { $this->execute_remote_command( [ "echo -n 'Setting base directory to {$this->workdir}.'" @@ -671,7 +692,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private function generate_healthcheck_commands() { - if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile') { + if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') { // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl. return 'exit 0'; } diff --git a/app/Models/Application.php b/app/Models/Application.php index 32eb227ca..cd3b43e05 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -259,13 +259,14 @@ class Application extends BaseModel if ($this->dockerfile) { return false; } - + if ($this->build_pack === 'dockerimage'){ + return false; + } return true; } public function isHealthcheckDisabled(): bool { if (data_get($this, 'dockerfile') || data_get($this, 'build_pack') === 'dockerfile' || data_get($this, 'health_check_enabled') === false) { - ray('dockerfile'); return true; } return false; diff --git a/resources/views/components/applications/navbar.blade.php b/resources/views/components/applications/navbar.blade.php index b84b0bf13..d38790789 100644 --- a/resources/views/components/applications/navbar.blade.php +++ b/resources/views/components/applications/navbar.blade.php @@ -16,7 +16,7 @@ @if ($application->status !== 'exited') -

Advanced

- diff --git a/resources/views/livewire/project/new/docker-image.blade.php b/resources/views/livewire/project/new/docker-image.blade.php index 3927247c7..55ad3d6ac 100644 --- a/resources/views/livewire/project/new/docker-image.blade.php +++ b/resources/views/livewire/project/new/docker-image.blade.php @@ -6,6 +6,6 @@

Docker Image

Save
- +
From de6f5b11051f36a955091b59fa12d5c30f41bc3b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 11:27:39 +0200 Subject: [PATCH 08/13] fix: goto --- resources/views/livewire/dashboard.blade.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php index 7492984b4..c0d4a3575 100644 --- a/resources/views/livewire/dashboard.blade.php +++ b/resources/views/livewire/dashboard.blade.php @@ -36,6 +36,7 @@

Projects

+ @if ($projects->count() === 1)
@else @@ -43,7 +44,7 @@ @endif @foreach ($projects as $project)
+ x-on:click="gotoProject('{{ $project->uuid }}')"> @if (data_get($project, 'environments.0.name')) @@ -107,5 +108,11 @@ @endforeach
+ {{-- Get IPTABLES --}}
From be3b01472ea86960e51bd49c7d1d1d24002b7523 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 11:28:57 +0200 Subject: [PATCH 09/13] ui: fix --- resources/views/project/resources.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php index 09faf85f4..f76fea282 100644 --- a/resources/views/project/resources.blade.php +++ b/resources/views/project/resources.blade.php @@ -41,7 +41,7 @@
-
{{ $application->name }}
+
{{ $application->name }}
{{ $application->description }}
From 3d3ccc435c348e871d664c07890e67ee09b61e6b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 11:29:33 +0200 Subject: [PATCH 10/13] ui: fix --- resources/views/project/resources.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php index f76fea282..ac728fc0d 100644 --- a/resources/views/project/resources.blade.php +++ b/resources/views/project/resources.blade.php @@ -50,7 +50,7 @@
-
{{ $databases->name }}
+
{{ $databases->name }}
{{ $databases->description }}
@@ -59,7 +59,7 @@
-
{{ $service->name }}
+
{{ $service->name }}
{{ $service->description }}
From 84d8e354119f8ed53fdb170da030cdef6b1bf696 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 11:42:35 +0200 Subject: [PATCH 11/13] fix: tcp proxy for dbs --- bootstrap/helpers/proxy.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index 316628958..86034282e 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -204,17 +204,23 @@ stream { proxy_pass $database->uuid:5432; } } +EOF; + $dockerfile = <<< EOF +FROM nginx:stable-alpine + +COPY nginx.conf /etc/nginx/nginx.conf EOF; $docker_compose = [ 'version' => '3.8', 'services' => [ $containerName => [ + 'build' => [ + 'context' => $configuration_dir, + 'dockerfile' => 'Dockerfile', + ], 'image' => "nginx:stable-alpine", 'container_name' => $containerName, 'restart' => RESTART_MODE, - 'volumes' => [ - "$configuration_dir/nginx.conf:/etc/nginx/nginx.conf:ro", - ], 'ports' => [ "$database->public_port:$database->public_port", ], @@ -243,13 +249,13 @@ EOF; ]; $dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2)); $nginxconf_base64 = base64_encode($nginxconf); + $dockerfile_base64 = base64_encode($dockerfile); instant_remote_process([ "mkdir -p $configuration_dir", + "echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile", "echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf", "echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml", - "docker compose --project-directory {$configuration_dir} up -d --remove-orphans >/dev/null", - - + "docker compose --project-directory {$configuration_dir} up --build -d >/dev/null", ], $database->destination->server); } function stopPostgresProxy(StandalonePostgresql $database) From 24fa56762e5c7f7ebad8d5e85f8e2057230b48c2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 13:10:43 +0200 Subject: [PATCH 12/13] fix: database backups --- .../Livewire/Project/Database/BackupEdit.php | 1 + app/Http/Livewire/Team/Storage/Create.php | 2 +- app/Http/Livewire/Team/Storage/Form.php | 2 + app/Jobs/DatabaseBackupJob.php | 37 ++++++++-------- app/Models/S3Storage.php | 42 ++++++++++++++++++- app/Models/Team.php | 3 +- .../Application/DeploymentFailed.php | 12 +++--- .../Application/DeploymentSuccess.php | 10 ++--- .../Application/StatusChanged.php | 6 +-- .../Container/ContainerRestarted.php | 6 +-- .../Container/ContainerStopped.php | 6 +-- app/Notifications/Database/BackupFailed.php | 6 +-- app/Notifications/Database/BackupSuccess.php | 6 +-- app/Notifications/Server/Revived.php | 6 +-- app/Notifications/Server/Unreachable.php | 6 +-- app/Notifications/Test.php | 6 +-- .../TransactionalEmails/InvitationLink.php | 2 +- .../TransactionalEmails/ResetPassword.php | 2 +- .../TransactionalEmails/Test.php | 2 +- ..._10_10_100320_update_s3_storages_table.php | 30 +++++++++++++ .../emails/s3-connection-error.blade.php | 6 +++ .../project/database/backup-edit.blade.php | 4 +- .../views/livewire/settings/backup.blade.php | 2 +- .../livewire/team/storage/form.blade.php | 7 +++- .../database/backups/executions.blade.php | 2 +- 25 files changed, 149 insertions(+), 65 deletions(-) create mode 100644 database/migrations/2023_10_10_100320_update_s3_storages_table.php create mode 100644 resources/views/emails/s3-connection-error.blade.php diff --git a/app/Http/Livewire/Project/Database/BackupEdit.php b/app/Http/Livewire/Project/Database/BackupEdit.php index a8a0ef055..ea217f526 100644 --- a/app/Http/Livewire/Project/Database/BackupEdit.php +++ b/app/Http/Livewire/Project/Database/BackupEdit.php @@ -8,6 +8,7 @@ class BackupEdit extends Component { public $backup; public $s3s; + public ?string $status = null; public array $parameters; protected $rules = [ diff --git a/app/Http/Livewire/Team/Storage/Create.php b/app/Http/Livewire/Team/Storage/Create.php index bcac861f8..4d7fb4f12 100644 --- a/app/Http/Livewire/Team/Storage/Create.php +++ b/app/Http/Livewire/Team/Storage/Create.php @@ -64,7 +64,7 @@ class Create extends Component } $this->storage->team_id = currentTeam()->id; $this->storage->testConnection(); - $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.'); + $this->storage->is_usable = true; $this->storage->save(); return redirect()->route('team.storages.show', $this->storage->uuid); } catch (\Throwable $e) { diff --git a/app/Http/Livewire/Team/Storage/Form.php b/app/Http/Livewire/Team/Storage/Form.php index 223c5ac41..573c851d7 100644 --- a/app/Http/Livewire/Team/Storage/Form.php +++ b/app/Http/Livewire/Team/Storage/Form.php @@ -9,6 +9,7 @@ class Form extends Component { public S3Storage $storage; protected $rules = [ + 'storage.is_usable' => 'nullable|boolean', 'storage.name' => 'nullable|min:3|max:255', 'storage.description' => 'nullable|min:3|max:255', 'storage.region' => 'required|max:255', @@ -18,6 +19,7 @@ class Form extends Component 'storage.endpoint' => 'required|url|max:255', ]; protected $validationAttributes = [ + 'storage.is_usable' => 'Is Usable', 'storage.name' => 'Name', 'storage.description' => 'Description', 'storage.region' => 'Region', diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index a59c6494a..cc65cea9b 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -31,7 +31,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted public ?string $container_name = null; public ?ScheduledDatabaseBackupExecution $backup_log = null; - public string $backup_status; + public string $backup_status = 'failed'; public ?string $backup_location = null; public string $backup_dir; public string $backup_file; @@ -74,7 +74,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted $ip = Str::slug($this->server->ip); $this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip"; } - $this->backup_file = "/pg_dump-" . Carbon::now()->timestamp . ".dump"; + $this->backup_file = "/pg-backup-customformat-" . Carbon::now()->timestamp . ".backup"; $this->backup_location = $this->backup_dir . $this->backup_file; $this->backup_log = ScheduledDatabaseBackupExecution::create([ @@ -90,10 +90,17 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted $this->upload_to_s3(); } $this->save_backup_logs(); + $this->team->notify(new BackupSuccess($this->backup, $this->database)); + $this->backup_status = 'success'; } catch (\Throwable $e) { - ray($e->getMessage()); + $this->backup_status = 'failed'; send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage()); + $this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output)); throw $e; + } finally { + $this->backup_log->update([ + 'status' => $this->backup_status, + ]); } } @@ -103,28 +110,15 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted ray($this->backup_dir); $commands[] = "mkdir -p " . $this->backup_dir; $commands[] = "docker exec $this->container_name pg_dump -Fc -U {$this->database->postgres_user} > $this->backup_location"; - $this->backup_output = instant_remote_process($commands, $this->server); - $this->backup_output = trim($this->backup_output); - if ($this->backup_output === '') { $this->backup_output = null; } - ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location); - - $this->backup_status = 'success'; - $this->team->notify(new BackupSuccess($this->backup, $this->database)); } catch (\Throwable $e) { - $this->backup_status = 'failed'; $this->add_to_backup_output($e->getMessage()); ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage()); - $this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output)); - } finally { - $this->backup_log->update([ - 'status' => $this->backup_status, - ]); } } @@ -163,11 +157,16 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted } $key = $this->s3->key; $secret = $this->s3->secret; - // $region = $this->s3->region; + // $region = $this->s3->region; $bucket = $this->s3->bucket; $endpoint = $this->s3->endpoint; + $this->s3->testConnection(); + if (isDev()) { + $commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v coolify_coolify-data-dev:/data/coolify:ro ghcr.io/coollabsio/coolify-helper >/dev/null 2>&1"; + } else { + $commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper >/dev/null 2>&1"; + } - $commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper"; $commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret"; $commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/"; instant_remote_process($commands, $this->server); @@ -175,7 +174,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir); } catch (\Throwable $e) { $this->add_to_backup_output($e->getMessage()); - ray($e->getMessage()); + throw $e; } finally { $command = "docker rm -f backup-of-{$this->backup->uuid}"; instant_remote_process([$command], $this->server); diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php index cbcdb97a9..03953d319 100644 --- a/app/Models/S3Storage.php +++ b/app/Models/S3Storage.php @@ -3,6 +3,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Notifications\Messages\MailMessage; +use Illuminate\Support\Facades\Storage; class S3Storage extends BaseModel { @@ -10,6 +12,7 @@ class S3Storage extends BaseModel protected $guarded = []; protected $casts = [ + 'is_usable' => 'boolean', 'key' => 'encrypted', 'secret' => 'encrypted', ]; @@ -19,7 +22,15 @@ class S3Storage extends BaseModel $selectArray = collect($select)->concat(['id']); return S3Storage::whereTeamId(currentTeam()->id)->select($selectArray->all())->orderBy('name'); } + public function isUsable() + { + return $this->is_usable; + } + public function team() + { + return $this->belongsTo(Team::class); + } public function awsUrl() { return "{$this->endpoint}/{$this->bucket}"; @@ -27,7 +38,34 @@ class S3Storage extends BaseModel public function testConnection() { - set_s3_target($this); - return \Storage::disk('custom-s3')->files(); + try { + set_s3_target($this); + Storage::disk('custom-s3')->files(); + $this->unusable_email_sent = false; + $this->is_usable = true; + return; + } catch (\Throwable $e) { + $this->is_usable = false; + if ($this->unusable_email_sent === false) { + $mail = new MailMessage(); + $mail->subject('Coolify: S3 Storage Connection Error'); + $mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('team.storages.show', ['storage_uuid' => $this->uuid])]); + $users = collect([]); + $members = $this->team->members()->get(); + foreach ($members as $user) { + if ($user->isAdmin()) { + $users->push($user); + } + } + foreach ($users as $user) { + send_user_an_email($mail, $user->email); + } + $this->unusable_email_sent = true; + } + + throw $e; + } finally { + $this->save(); + } } } diff --git a/app/Models/Team.php b/app/Models/Team.php index 6ae949d08..8021b1e97 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -48,6 +48,7 @@ class Team extends Model implements SendsDiscord, SendsEmail } return explode(',', $recipients); } + public function limits(): Attribute { return Attribute::make( @@ -125,7 +126,7 @@ class Team extends Model implements SendsDiscord, SendsEmail public function s3s() { - return $this->hasMany(S3Storage::class); + return $this->hasMany(S3Storage::class)->where('is_usable', true); } public function trialEnded() { foreach ($this->servers as $server) { diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php index f97fbc77e..a379cd46f 100644 --- a/app/Notifications/Application/DeploymentFailed.php +++ b/app/Notifications/Application/DeploymentFailed.php @@ -52,10 +52,10 @@ class DeploymentFailed extends Notification implements ShouldQueue $pull_request_id = data_get($this->preview, 'pull_request_id', 0); $fqdn = $this->fqdn; if ($pull_request_id === 0) { - $mail->subject('❌ Deployment failed of ' . $this->application_name . '.'); + $mail->subject('Coolify: Deployment failed of ' . $this->application_name . '.'); } else { $fqdn = $this->preview->fqdn; - $mail->subject('❌ Deployment failed of pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . '.'); + $mail->subject('Coolify: Deployment failed of pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . '.'); } $mail->view('emails.application-deployment-failed', [ 'name' => $this->application_name, @@ -69,10 +69,10 @@ class DeploymentFailed extends Notification implements ShouldQueue public function toDiscord(): string { if ($this->preview) { - $message = '❌ Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; + $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; } else { - $message = '❌ Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; + $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; } return $message; @@ -80,9 +80,9 @@ class DeploymentFailed extends Notification implements ShouldQueue public function toTelegram(): array { if ($this->preview) { - $message = '❌ Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; + $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; } else { - $message = '❌ Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; + $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; } return [ "message" => $message, diff --git a/app/Notifications/Application/DeploymentSuccess.php b/app/Notifications/Application/DeploymentSuccess.php index 053ae7a2b..1fe01a5d7 100644 --- a/app/Notifications/Application/DeploymentSuccess.php +++ b/app/Notifications/Application/DeploymentSuccess.php @@ -52,10 +52,10 @@ class DeploymentSuccess extends Notification implements ShouldQueue $pull_request_id = data_get($this->preview, 'pull_request_id', 0); $fqdn = $this->fqdn; if ($pull_request_id === 0) { - $mail->subject("✅ New version is deployed of {$this->application_name}"); + $mail->subject("Coolify: New version is deployed of {$this->application_name}"); } else { $fqdn = $this->preview->fqdn; - $mail->subject("✅ Pull request #{$pull_request_id} of {$this->application_name} deployed successfully"); + $mail->subject("Coolify: Pull request #{$pull_request_id} of {$this->application_name} deployed successfully"); } $mail->view('emails.application-deployment-success', [ 'name' => $this->application_name, @@ -69,7 +69,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue public function toDiscord(): string { if ($this->preview) { - $message = '✅ New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . ' + $message = 'Coolify: New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . ' '; if ($this->preview->fqdn) { @@ -77,7 +77,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue } $message .= '[Deployment logs](' . $this->deployment_url . ')'; } else { - $message = '✅ New version successfully deployed of ' . $this->application_name . ' + $message = 'Coolify: New version successfully deployed of ' . $this->application_name . ' '; if ($this->fqdn) { @@ -90,7 +90,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue public function toTelegram(): array { if ($this->preview) { - $message = '✅ New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . ''; + $message = 'Coolify: New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . ''; if ($this->preview->fqdn) { $buttons[] = [ "text" => "Open Application", diff --git a/app/Notifications/Application/StatusChanged.php b/app/Notifications/Application/StatusChanged.php index 064c347d0..df12f2b0a 100644 --- a/app/Notifications/Application/StatusChanged.php +++ b/app/Notifications/Application/StatusChanged.php @@ -45,7 +45,7 @@ class StatusChanged extends Notification implements ShouldQueue { $mail = new MailMessage(); $fqdn = $this->fqdn; - $mail->subject("⛔ {$this->application_name} has been stopped"); + $mail->subject("Coolify: {$this->application_name} has been stopped"); $mail->view('emails.application-status-changes', [ 'name' => $this->application_name, 'fqdn' => $fqdn, @@ -56,7 +56,7 @@ class StatusChanged extends Notification implements ShouldQueue public function toDiscord(): string { - $message = '⛔ ' . $this->application_name . ' has been stopped. + $message = 'Coolify: ' . $this->application_name . ' has been stopped. '; $message .= '[Open Application in Coolify](' . $this->application_url . ')'; @@ -64,7 +64,7 @@ class StatusChanged extends Notification implements ShouldQueue } public function toTelegram(): array { - $message = '⛔ ' . $this->application_name . ' has been stopped.'; + $message = 'Coolify: ' . $this->application_name . ' has been stopped.'; return [ "message" => $message, "buttons" => [ diff --git a/app/Notifications/Container/ContainerRestarted.php b/app/Notifications/Container/ContainerRestarted.php index 25fd08ff8..dc55c05d2 100644 --- a/app/Notifications/Container/ContainerRestarted.php +++ b/app/Notifications/Container/ContainerRestarted.php @@ -27,7 +27,7 @@ class ContainerRestarted extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("✅ Container ({$this->name}) has been restarted automatically on {$this->server->name}"); + $mail->subject("Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}"); $mail->view('emails.container-restarted', [ 'containerName' => $this->name, 'serverName' => $this->server->name, @@ -38,12 +38,12 @@ class ContainerRestarted extends Notification implements ShouldQueue public function toDiscord(): string { - $message = "✅ Container ({$this->name}) has been restarted automatically on {$this->server->name}"; + $message = "Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}"; return $message; } public function toTelegram(): array { - $message = "✅ Container ({$this->name}) has been restarted automatically on {$this->server->name}"; + $message = "Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}"; $payload = [ "message" => $message, ]; diff --git a/app/Notifications/Container/ContainerStopped.php b/app/Notifications/Container/ContainerStopped.php index 390c9552b..4518698fb 100644 --- a/app/Notifications/Container/ContainerStopped.php +++ b/app/Notifications/Container/ContainerStopped.php @@ -26,7 +26,7 @@ class ContainerStopped extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("⛔ Container {$this->name} has been stopped on {$this->server->name}"); + $mail->subject("Coolify: Container ({$this->name}) has been stopped on {$this->server->name}"); $mail->view('emails.container-stopped', [ 'containerName' => $this->name, 'serverName' => $this->server->name, @@ -37,12 +37,12 @@ class ContainerStopped extends Notification implements ShouldQueue public function toDiscord(): string { - $message = "⛔ Container {$this->name} has been stopped on {$this->server->name}"; + $message = "Coolify: Container ({$this->name}) has been stopped on {$this->server->name}"; return $message; } public function toTelegram(): array { - $message = "⛔ Container ($this->name} has been stopped on {$this->server->name}"; + $message = "Coolify: Container ($this->name} has been stopped on {$this->server->name}"; $payload = [ "message" => $message, ]; diff --git a/app/Notifications/Database/BackupFailed.php b/app/Notifications/Database/BackupFailed.php index 482b27977..dddc1240b 100644 --- a/app/Notifications/Database/BackupFailed.php +++ b/app/Notifications/Database/BackupFailed.php @@ -30,7 +30,7 @@ class BackupFailed extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("❌ [ACTION REQUIRED] Backup FAILED for {$this->database->name}"); + $mail->subject("Coolify: [ACTION REQUIRED] Backup FAILED for {$this->database->name}"); $mail->view('emails.backup-failed', [ 'name' => $this->name, 'frequency' => $this->frequency, @@ -41,11 +41,11 @@ class BackupFailed extends Notification implements ShouldQueue public function toDiscord(): string { - return "❌ Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}"; + return "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}"; } public function toTelegram(): array { - $message = "❌ Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}"; + $message = "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}"; return [ "message" => $message, ]; diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php index 0378baf96..bbe0bc6d3 100644 --- a/app/Notifications/Database/BackupSuccess.php +++ b/app/Notifications/Database/BackupSuccess.php @@ -30,7 +30,7 @@ class BackupSuccess extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("✅ Backup successfully done for {$this->database->name}"); + $mail->subject("Coolify: Backup successfully done for {$this->database->name}"); $mail->view('emails.backup-success', [ 'name' => $this->name, 'frequency' => $this->frequency, @@ -40,11 +40,11 @@ class BackupSuccess extends Notification implements ShouldQueue public function toDiscord(): string { - return "✅ Database backup for {$this->name} with frequency of {$this->frequency} was successful."; + return "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was successful."; } public function toTelegram(): array { - $message = "✅ Database backup for {$this->name} with frequency of {$this->frequency} was successful."; + $message = "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was successful."; return [ "message" => $message, ]; diff --git a/app/Notifications/Server/Revived.php b/app/Notifications/Server/Revived.php index 07771a287..21fe6d40d 100644 --- a/app/Notifications/Server/Revived.php +++ b/app/Notifications/Server/Revived.php @@ -45,7 +45,7 @@ class Revived extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("✅ Server ({$this->server->name}) revived."); + $mail->subject("Coolify: Server ({$this->server->name}) revived."); $mail->view('emails.server-revived', [ 'name' => $this->server->name, ]); @@ -54,13 +54,13 @@ class Revived extends Notification implements ShouldQueue public function toDiscord(): string { - $message = "✅ Server '{$this->server->name}' revived. All automations & integrations are turned on again!"; + $message = "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!"; return $message; } public function toTelegram(): array { return [ - "message" => "✅ Server '{$this->server->name}' revived. All automations & integrations are turned on again!" + "message" => "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!" ]; } } diff --git a/app/Notifications/Server/Unreachable.php b/app/Notifications/Server/Unreachable.php index 705988e31..ae100b804 100644 --- a/app/Notifications/Server/Unreachable.php +++ b/app/Notifications/Server/Unreachable.php @@ -43,7 +43,7 @@ class Unreachable extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("⛔ Server ({$this->server->name}) is unreachable after trying to connect to it 5 times"); + $mail->subject("Coolify: Server ({$this->server->name}) is unreachable after trying to connect to it 5 times"); $mail->view('emails.server-lost-connection', [ 'name' => $this->server->name, ]); @@ -52,13 +52,13 @@ class Unreachable extends Notification implements ShouldQueue public function toDiscord(): string { - $message = "⛔ Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations."; + $message = "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations."; return $message; } public function toTelegram(): array { return [ - "message" => "⛔ Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations." + "message" => "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations." ]; } } diff --git a/app/Notifications/Test.php b/app/Notifications/Test.php index 098a94920..06e3adbaa 100644 --- a/app/Notifications/Test.php +++ b/app/Notifications/Test.php @@ -24,14 +24,14 @@ class Test extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("Test Email"); + $mail->subject("Coolify: Test Email"); $mail->view('emails.test'); return $mail; } public function toDiscord(): string { - $message = 'This is a test Discord notification from Coolify.'; + $message = 'Coolify: This is a test Discord notification from Coolify.'; $message .= "\n\n"; $message .= '[Go to your dashboard](' . base_url() . ')'; return $message; @@ -39,7 +39,7 @@ class Test extends Notification implements ShouldQueue public function toTelegram(): array { return [ - "message" => 'This is a test Telegram notification from Coolify.', + "message" => 'Coolify: This is a test Telegram notification from Coolify.', "buttons" => [ [ "text" => "Go to your dashboard", diff --git a/app/Notifications/TransactionalEmails/InvitationLink.php b/app/Notifications/TransactionalEmails/InvitationLink.php index ab7cfb122..dd1275c2d 100644 --- a/app/Notifications/TransactionalEmails/InvitationLink.php +++ b/app/Notifications/TransactionalEmails/InvitationLink.php @@ -30,7 +30,7 @@ class InvitationLink extends Notification implements ShouldQueue $invitation_team = Team::find($invitation->team->id); $mail = new MailMessage(); - $mail->subject('Invitation for ' . $invitation_team->name); + $mail->subject('Coolify: Invitation for ' . $invitation_team->name); $mail->view('emails.invitation-link', [ 'team' => $invitation_team->name, 'email' => $this->user->email, diff --git a/app/Notifications/TransactionalEmails/ResetPassword.php b/app/Notifications/TransactionalEmails/ResetPassword.php index 2a11051b3..cde6190e2 100644 --- a/app/Notifications/TransactionalEmails/ResetPassword.php +++ b/app/Notifications/TransactionalEmails/ResetPassword.php @@ -50,7 +50,7 @@ class ResetPassword extends Notification protected function buildMailMessage($url) { $mail = new MailMessage(); - $mail->subject('Reset Password'); + $mail->subject('Coolify: Reset Password'); $mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]); return $mail; } diff --git a/app/Notifications/TransactionalEmails/Test.php b/app/Notifications/TransactionalEmails/Test.php index 21cf87470..6a4e5533f 100644 --- a/app/Notifications/TransactionalEmails/Test.php +++ b/app/Notifications/TransactionalEmails/Test.php @@ -25,7 +25,7 @@ class Test extends Notification implements ShouldQueue public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject('Test Email'); + $mail->subject('Coolify: Test Email'); $mail->view('emails.test'); return $mail; } diff --git a/database/migrations/2023_10_10_100320_update_s3_storages_table.php b/database/migrations/2023_10_10_100320_update_s3_storages_table.php new file mode 100644 index 000000000..b9c482c82 --- /dev/null +++ b/database/migrations/2023_10_10_100320_update_s3_storages_table.php @@ -0,0 +1,30 @@ +boolean('is_usable')->default(false); + $table->boolean('unusable_email_sent')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('s3_storages', function (Blueprint $table) { + $table->dropColumn('is_usable'); + $table->dropColumn('unusable_email_sent'); + }); + } +}; diff --git a/resources/views/emails/s3-connection-error.blade.php b/resources/views/emails/s3-connection-error.blade.php new file mode 100644 index 000000000..68faa0608 --- /dev/null +++ b/resources/views/emails/s3-connection-error.blade.php @@ -0,0 +1,6 @@ + + Connection could not be establised with one of your S3 Storage ({{ $name }}). Please fix it + [here]({{ $url }}). + + {{ $reason }} + diff --git a/resources/views/livewire/project/database/backup-edit.blade.php b/resources/views/livewire/project/database/backup-edit.blade.php index 4450d209a..412595958 100644 --- a/resources/views/livewire/project/database/backup-edit.blade.php +++ b/resources/views/livewire/project/database/backup-edit.blade.php @@ -4,7 +4,9 @@ Save - + @if (Str::of($status)->startsWith('running')) + + @endif @if ($backup->database_id !== 0) Delete @endif diff --git a/resources/views/livewire/settings/backup.blade.php b/resources/views/livewire/settings/backup.blade.php index f2bb18fbe..3b050bfb5 100644 --- a/resources/views/livewire/settings/backup.blade.php +++ b/resources/views/livewire/settings/backup.blade.php @@ -22,7 +22,7 @@
- + @else To configure automatic backup for your Coolify instance, you first need to add as a database resource into Coolify. diff --git a/resources/views/livewire/team/storage/form.blade.php b/resources/views/livewire/team/storage/form.blade.php index 5c824e984..5e7c6a7d8 100644 --- a/resources/views/livewire/team/storage/form.blade.php +++ b/resources/views/livewire/team/storage/form.blade.php @@ -10,12 +10,17 @@

Storage Details

{{ $storage->name }}
+ @if ($storage->is_usable) +
Usable
+ @else +
Not Usable
+ @endif
Save - Test Connection + Validate Connection Delete diff --git a/resources/views/project/database/backups/executions.blade.php b/resources/views/project/database/backups/executions.blade.php index afcd0c061..26d4aabd0 100644 --- a/resources/views/project/database/backups/executions.blade.php +++ b/resources/views/project/database/backups/executions.blade.php @@ -12,7 +12,7 @@
- +

Executions

From 0be8ffbdc9431a2b683925b1316826d40ee82312 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 10 Oct 2023 14:02:43 +0200 Subject: [PATCH 13/13] feat: add dockerfile location --- .../Livewire/Project/Application/General.php | 2 + app/Jobs/ApplicationDeploymentJob.php | 43 ++++++++++++++++++- app/Models/Application.php | 20 +++++++-- ...dockerfile_location_applications_table.php | 28 ++++++++++++ .../project/application/general.blade.php | 5 +++ 5 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 database/migrations/2023_10_10_113144_add_dockerfile_location_applications_table.php diff --git a/app/Http/Livewire/Project/Application/General.php b/app/Http/Livewire/Project/Application/General.php index 93fcd250b..afe1b62dd 100644 --- a/app/Http/Livewire/Project/Application/General.php +++ b/app/Http/Livewire/Project/Application/General.php @@ -51,6 +51,7 @@ class General extends Component 'application.dockerfile' => 'nullable', 'application.docker_registry_image_name' => 'nullable', 'application.docker_registry_image_tag' => 'nullable', + 'application.dockerfile_location' => 'nullable', ]; protected $validationAttributes = [ 'application.name' => 'name', @@ -71,6 +72,7 @@ class General extends Component 'application.dockerfile' => 'Dockerfile', 'application.docker_registry_image_name' => 'Docker registry image name', 'application.docker_registry_image_tag' => 'Docker registry image tag', + 'application.dockerfile_location' => 'Dockerfile location', ]; diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index a1dff9c93..f4b839ec3 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -57,6 +57,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private string|null $currently_running_container_name = null; private string $basedir; private string $workdir; + private ?string $build_pack = null; private string $configuration_dir; private string $build_image_name; private string $production_image_name; @@ -65,6 +66,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private $env_args; private $docker_compose; private $docker_compose_base64; + private string $dockerfile_location = '/Dockerfile'; private $log_model; private Collection $saved_outputs; @@ -76,6 +78,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id); $this->log_model = $this->application_deployment_queue; $this->application = Application::find($this->application_deployment_queue->application_id); + $this->build_pack = data_get($this->application, 'build_pack'); $this->application_deployment_queue_id = $application_deployment_queue_id; $this->deployment_uuid = $this->application_deployment_queue->deployment_uuid; @@ -140,6 +143,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->deploy_simple_dockerfile(); } else if ($this->application->build_pack === 'dockerimage') { $this->deploy_dockerimage(); + } else if ($this->application->build_pack === 'dockerfile') { + $this->deploy_dockerfile(); } else { if ($this->pull_request_id !== 0) { $this->deploy_pull_request(); @@ -178,6 +183,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ); } } + private function deploy_docker_compose() { $dockercompose_base64 = base64_encode($this->application->dockercompose); @@ -265,6 +271,35 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->generate_compose_file(); $this->rolling_update(); } + + private function deploy_dockerfile() + { + if (data_get($this->application, 'dockerfile_location')) { + $this->dockerfile_location = $this->application->dockerfile_location; + } + $this->execute_remote_command( + [ + "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}.'" + ], + ); + $this->prepare_builder_image(); + $this->clone_repository(); + $this->set_base_dir(); + $tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}"); + if (strlen($tag) > 128) { + $tag = $tag->substr(0, 128); + } + + $this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build"); + $this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}"); + // ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green(); + $this->cleanup_git(); + $this->generate_compose_file(); + $this->generate_build_env_variables(); + $this->add_build_env_variables_to_dockerfile(); + // $this->build_image(); + $this->rolling_update(); + } private function deploy() { $this->execute_remote_command( @@ -629,6 +664,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } + if ($this->build_pack === 'dockerfile') { + $docker_compose['services'][$this->container_name]['build'] = [ + 'context' => $this->workdir, + 'dockerfile' => $this->workdir . $this->dockerfile_location, + ]; + } $this->docker_compose = Yaml::dump($docker_compose, 10); $this->docker_compose_base64 = base64_encode($this->docker_compose); $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]); @@ -785,7 +826,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); { $this->execute_remote_command( ["echo -n 'Starting application (could take a while).'"], - [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d >/dev/null"), "hidden" => true], ); } diff --git a/app/Models/Application.php b/app/Models/Application.php index cd3b43e05..634569c0f 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -101,7 +101,21 @@ class Application extends BaseModel } ); } - + public function dockerfileLocation(): Attribute + { + return Attribute::make( + set: function ($value) { + if (is_null($value) || $value === '') { + return '/Dockerfile'; + } else { + if ($value !== '/') { + return Str::start(Str::replaceEnd('/', '', $value), '/'); + } + return Str::start($value, '/'); + } + } + ); + } public function baseDirectory(): Attribute { return Attribute::make( @@ -259,14 +273,14 @@ class Application extends BaseModel if ($this->dockerfile) { return false; } - if ($this->build_pack === 'dockerimage'){ + if ($this->build_pack === 'dockerimage') { return false; } return true; } public function isHealthcheckDisabled(): bool { - if (data_get($this, 'dockerfile') || data_get($this, 'build_pack') === 'dockerfile' || data_get($this, 'health_check_enabled') === false) { + if (data_get($this, 'health_check_enabled') === false) { return true; } return false; diff --git a/database/migrations/2023_10_10_113144_add_dockerfile_location_applications_table.php b/database/migrations/2023_10_10_113144_add_dockerfile_location_applications_table.php new file mode 100644 index 000000000..39956a9ed --- /dev/null +++ b/database/migrations/2023_10_10_113144_add_dockerfile_location_applications_table.php @@ -0,0 +1,28 @@ +string('dockerfile_location')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('dockerfile_location'); + }); + } +}; diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index 89c748aed..4492c8192 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -56,6 +56,11 @@
+ @if ($application->build_pack === 'dockerfile') + + @endif @if ($application->could_set_build_commands()) @if ($application->settings->is_static)