From 61facbb871eca4cd7d4786b7f00a2c9378ad6fa9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 15:17:05 +0200 Subject: [PATCH 01/22] update version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index 25d09437e..2efda3c64 100644 --- a/versions.json +++ b/versions.json @@ -4,7 +4,7 @@ "version": "3.12.36" }, "v4": { - "version": "4.0.0-beta.18" + "version": "4.0.0-beta.19" } } } From 3d432d025af7231eab3d217055118c775844fc86 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 15:39:15 +0200 Subject: [PATCH 02/22] fix: make coolify-db backups unique dir --- app/Jobs/DatabaseBackupJob.php | 8 ++++++-- bootstrap/helpers/shared.php | 10 ++++++++-- config/version.php | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 3fdbea216..a859b3510 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -18,6 +18,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Throwable; +use Illuminate\Support\Str; class DatabaseBackupJob implements ShouldQueue { @@ -68,11 +69,13 @@ class DatabaseBackupJob implements ShouldQueue return; } $this->container_name = $this->database->uuid; + $this->backup_dir = backup_dir() . "/" . $this->container_name; + if ($this->database->name === 'coolify-db') { $this->container_name = "coolify-db"; + $ip = Str::slug($this->server->ip); + $this->backup_dir = backup_dir() . "/coolify-db-$ip"; } - - $this->backup_dir = backup_dir() . "/" . $this->container_name; $this->backup_file = "/dumpall-" . Carbon::now()->timestamp . ".sql"; $this->backup_location = $this->backup_dir . $this->backup_file; @@ -95,6 +98,7 @@ class DatabaseBackupJob implements ShouldQueue private function backup_standalone_postgresql(): void { try { + ray($this->backup_dir); $commands[] = "mkdir -p " . $this->backup_dir; $commands[] = "docker exec $this->container_name pg_dumpall -U {$this->database->postgres_user} > $this->backup_location"; diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index ff0e99679..65fe454ab 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -130,10 +130,16 @@ function set_transanctional_email_settings(InstanceSettings|null $settings = nul function base_ip(): string { if (is_dev()) { - return "http://localhost"; + return "localhost"; } $settings = InstanceSettings::get(); - return "http://$settings->public_ipv4"; + if ($settings->public_ipv4) { + return "$settings->public_ipv4"; + } + if ($settings->public_ipv6) { + return "$settings->public_ipv6"; + } + return "localhost"; } /** diff --git a/config/version.php b/config/version.php index 8ecc24277..7764582fa 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Tue, 15 Aug 2023 16:05:31 +0200 Subject: [PATCH 03/22] update docker-compose.prod --- docker-compose.prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 063d1e257..4a9e0c192 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -33,6 +33,7 @@ services: - PHP_PM_MIN_SPARE_SERVERS=1 - PHP_PM_MAX_SPARE_SERVERS=10 - SELF_HOSTED + - WAITLIST - LEMON_SQUEEZY_WEBHOOK_SECRET - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_BASIC - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_PRO From 6eff24369be2307fc9ecbfc842bd3f7a000c6e30 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 16:09:40 +0200 Subject: [PATCH 04/22] fix --- app/Http/Livewire/Waitlist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Livewire/Waitlist.php b/app/Http/Livewire/Waitlist.php index a0d4284b6..29a29f7f2 100644 --- a/app/Http/Livewire/Waitlist.php +++ b/app/Http/Livewire/Waitlist.php @@ -44,7 +44,7 @@ class Waitlist extends Component 'type' => 'registration', ]); - $this->emit('success', 'You have been added to the waitlist.'); + $this->emit('success', 'Check your email to verify your email address.'); dispatch(new SendConfirmationForWaitlistJob($this->email, $waitlist->uuid)); } catch (\Exception $e) { return general_error_handler(err: $e, that: $this); From 9c33689c1179de06152d6ca6e0463c4ce4b0e293 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 16:15:30 +0200 Subject: [PATCH 05/22] udpate layout --- resources/views/components/layout-simple.blade.php | 5 +++-- resources/views/components/layout-subscription.blade.php | 5 +++-- resources/views/components/layout.blade.php | 5 ++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/resources/views/components/layout-simple.blade.php b/resources/views/components/layout-simple.blade.php index cda1667a3..543c826ef 100644 --- a/resources/views/components/layout-simple.blade.php +++ b/resources/views/components/layout-simple.blade.php @@ -8,9 +8,10 @@ @env('local') Coolify - localhost - @endenv - @env('production') + + @else {{ $title ?? 'Coolify' }} + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) diff --git a/resources/views/components/layout-subscription.blade.php b/resources/views/components/layout-subscription.blade.php index b479b29e2..74fdd4669 100644 --- a/resources/views/components/layout-subscription.blade.php +++ b/resources/views/components/layout-subscription.blade.php @@ -8,9 +8,10 @@ @env('local') Coolify - localhost - @endenv - @env('production') + + @else {{ $title ?? 'Coolify' }} + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) diff --git a/resources/views/components/layout.blade.php b/resources/views/components/layout.blade.php index 33f409653..029d6e2fa 100644 --- a/resources/views/components/layout.blade.php +++ b/resources/views/components/layout.blade.php @@ -9,10 +9,9 @@ @env('local') Coolify - localhost - @endenv - @env('production') - + @else {{ $title ?? 'Coolify' }} + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) From 56161e8e0dcff45a4ac5accfb23c245b57a22ac0 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 16:25:09 +0200 Subject: [PATCH 06/22] test webhook --- app/Http/Livewire/Waitlist.php | 1 - resources/views/emails/waitlist-confirmation.blade.php | 2 +- routes/webhooks.php | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Http/Livewire/Waitlist.php b/app/Http/Livewire/Waitlist.php index 29a29f7f2..ffa1b1f5c 100644 --- a/app/Http/Livewire/Waitlist.php +++ b/app/Http/Livewire/Waitlist.php @@ -49,6 +49,5 @@ class Waitlist extends Component } catch (\Exception $e) { return general_error_handler(err: $e, that: $this); } - } } diff --git a/resources/views/emails/waitlist-confirmation.blade.php b/resources/views/emails/waitlist-confirmation.blade.php index 44aac5f74..922610e91 100644 --- a/resources/views/emails/waitlist-confirmation.blade.php +++ b/resources/views/emails/waitlist-confirmation.blade.php @@ -1,4 +1,4 @@ Someone added this email to the Coolify Cloud's waitlist.
Click here to confirm! The link will expire in {{config('constants.waitlist.confirmation_valid_for_minutes')}} minutes.

-You have no idea what Coolify Cloud is or this waitlist? Click here to remove you from the waitlist. +You have no idea what Coolify Cloud is or this waitlist? Click here to remove you from the waitlist. diff --git a/routes/webhooks.php b/routes/webhooks.php index 0739c1c38..c6f8a5576 100644 --- a/routes/webhooks.php +++ b/routes/webhooks.php @@ -176,6 +176,9 @@ Route::post('/source/github/events', function () { }); if (is_cloud()) { + Route::get('/test', function () { + return 'OK'; + }); Route::get('/waitlist/confirm', function () { $email = request()->get('email'); $confirmation_code = request()->get('confirmation_code'); From 878db648789cb38209b84b868780d131717b8a90 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 16:28:38 +0200 Subject: [PATCH 07/22] hmm --- routes/webhooks.php | 223 ++++++++++++++++++++++---------------------- 1 file changed, 109 insertions(+), 114 deletions(-) diff --git a/routes/webhooks.php b/routes/webhooks.php index c6f8a5576..77167caee 100644 --- a/routes/webhooks.php +++ b/routes/webhooks.php @@ -175,127 +175,122 @@ Route::post('/source/github/events', function () { } }); -if (is_cloud()) { - Route::get('/test', function () { - return 'OK'; - }); - Route::get('/waitlist/confirm', function () { - $email = request()->get('email'); - $confirmation_code = request()->get('confirmation_code'); - ray($email, $confirmation_code); - try { - $found = Waitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); - if ($found && !$found->verified && $found->created_at > now()->subMinutes(config('constants.waitlist.confirmation_valid_for_minutes'))) { - $found->verified = true; - $found->save(); - return 'Thank you for confirming your email address. We will notify you when you are next in line.'; - } - return redirect()->route('dashboard'); - } catch (error) { - return redirect()->route('dashboard'); +Route::get('/waitlist/confirm', function () { + $email = request()->get('email'); + $confirmation_code = request()->get('confirmation_code'); + ray($email, $confirmation_code); + try { + $found = Waitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); + if ($found && !$found->verified && $found->created_at > now()->subMinutes(config('constants.waitlist.confirmation_valid_for_minutes'))) { + $found->verified = true; + $found->save(); + return 'Thank you for confirming your email address. We will notify you when you are next in line.'; + } + return redirect()->route('dashboard'); + } catch (error) { + return redirect()->route('dashboard'); + } + +})->name('webhooks.waitlist.confirm'); +Route::get('/waitlist/cancel', function () { + $email = request()->get('email'); + $confirmation_code = request()->get('confirmation_code'); + try { + $found = Waitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); + if ($found && !$found->verified) { + $found->delete(); + return 'Your email address has been removed from the waitlist.'; + } + return redirect()->route('dashboard'); + } catch (error) { + return redirect()->route('dashboard'); + } + +})->name('webhooks.waitlist.cancel'); +Route::post('/payments/events', function () { + try { + $secret = config('coolify.lemon_squeezy_webhook_secret'); + $payload = request()->collect(); + $hash = hash_hmac('sha256', $payload, $secret); + $signature = request()->header('X-Signature'); + + if (!hash_equals($hash, $signature)) { + return response('Invalid signature.', 400); } - })->name('webhooks.waitlist.confirm'); - Route::get('/waitlist/cancel', function () { - $email = request()->get('email'); - $confirmation_code = request()->get('confirmation_code'); - try { - $found = Waitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); - if ($found && !$found->verified) { - $found->delete(); - return 'Your email address has been removed from the waitlist.'; - } - return redirect()->route('dashboard'); - } catch (error) { - return redirect()->route('dashboard'); + $webhook = Webhook::create([ + 'type' => 'lemonsqueezy', + 'payload' => $payload, + ]); + $event = data_get($payload, 'meta.event_name'); + ray('Subscription event: ' . $event); + $email = data_get($payload, 'data.attributes.user_email'); + $team_id = data_get($payload, 'meta.custom_data.team_id'); + if (is_null($team_id) || empty($team_id)) { + throw new Exception('No team_id found in webhook payload.'); } - - })->name('webhooks.waitlist.cancel'); - Route::post('/payments/events', function () { - try { - $secret = config('coolify.lemon_squeezy_webhook_secret'); - $payload = request()->collect(); - $hash = hash_hmac('sha256', $payload, $secret); - $signature = request()->header('X-Signature'); - - if (!hash_equals($hash, $signature)) { - return response('Invalid signature.', 400); - } - - $webhook = Webhook::create([ - 'type' => 'lemonsqueezy', - 'payload' => $payload, - ]); - $event = data_get($payload, 'meta.event_name'); - ray('Subscription event: ' . $event); - $email = data_get($payload, 'data.attributes.user_email'); - $team_id = data_get($payload, 'meta.custom_data.team_id'); - if (is_null($team_id) || empty($team_id)) { - throw new Exception('No team_id found in webhook payload.'); - } - $subscription_id = data_get($payload, 'data.id'); - $order_id = data_get($payload, 'data.attributes.order_id'); - $product_id = data_get($payload, 'data.attributes.product_id'); - $variant_id = data_get($payload, 'data.attributes.variant_id'); - $variant_name = data_get($payload, 'data.attributes.variant_name'); - $customer_id = data_get($payload, 'data.attributes.customer_id'); - $status = data_get($payload, 'data.attributes.status'); - $trial_ends_at = data_get($payload, 'data.attributes.trial_ends_at'); - $renews_at = data_get($payload, 'data.attributes.renews_at'); - $ends_at = data_get($payload, 'data.attributes.ends_at'); - $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method'); - $team = Team::find($team_id); - $found = $team->members->where('email', $email)->first(); - if (!$found->isAdmin()) { - throw new Exception("User {$email} is not an admin or owner of team {$team->id}."); - } - switch ($event) { - case 'subscription_created': - case 'subscription_updated': - case 'subscription_resumed': - case 'subscription_unpaused': - $subscription = Subscription::updateOrCreate([ - 'team_id' => $team_id, - ], [ - 'lemon_subscription_id' => $subscription_id, - 'lemon_customer_id' => $customer_id, - 'lemon_order_id' => $order_id, - 'lemon_product_id' => $product_id, - 'lemon_variant_id' => $variant_id, + $subscription_id = data_get($payload, 'data.id'); + $order_id = data_get($payload, 'data.attributes.order_id'); + $product_id = data_get($payload, 'data.attributes.product_id'); + $variant_id = data_get($payload, 'data.attributes.variant_id'); + $variant_name = data_get($payload, 'data.attributes.variant_name'); + $customer_id = data_get($payload, 'data.attributes.customer_id'); + $status = data_get($payload, 'data.attributes.status'); + $trial_ends_at = data_get($payload, 'data.attributes.trial_ends_at'); + $renews_at = data_get($payload, 'data.attributes.renews_at'); + $ends_at = data_get($payload, 'data.attributes.ends_at'); + $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method'); + $team = Team::find($team_id); + $found = $team->members->where('email', $email)->first(); + if (!$found->isAdmin()) { + throw new Exception("User {$email} is not an admin or owner of team {$team->id}."); + } + switch ($event) { + case 'subscription_created': + case 'subscription_updated': + case 'subscription_resumed': + case 'subscription_unpaused': + $subscription = Subscription::updateOrCreate([ + 'team_id' => $team_id, + ], [ + 'lemon_subscription_id' => $subscription_id, + 'lemon_customer_id' => $customer_id, + 'lemon_order_id' => $order_id, + 'lemon_product_id' => $product_id, + 'lemon_variant_id' => $variant_id, + 'lemon_status' => $status, + 'lemon_variant_name' => $variant_name, + 'lemon_trial_ends_at' => $trial_ends_at, + 'lemon_renews_at' => $renews_at, + 'lemon_ends_at' => $ends_at, + 'lemon_update_payment_menthod_url' => $update_payment_method, + ]); + break; + case 'subscription_cancelled': + case 'subscription_paused': + case 'subscription_expired': + $subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first(); + if ($subscription) { + $subscription->update([ 'lemon_status' => $status, - 'lemon_variant_name' => $variant_name, 'lemon_trial_ends_at' => $trial_ends_at, 'lemon_renews_at' => $renews_at, 'lemon_ends_at' => $ends_at, 'lemon_update_payment_menthod_url' => $update_payment_method, ]); - break; - case 'subscription_cancelled': - case 'subscription_paused': - case 'subscription_expired': - $subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first(); - if ($subscription) { - $subscription->update([ - 'lemon_status' => $status, - 'lemon_trial_ends_at' => $trial_ends_at, - 'lemon_renews_at' => $renews_at, - 'lemon_ends_at' => $ends_at, - 'lemon_update_payment_menthod_url' => $update_payment_method, - ]); - } - break; - } - $webhook->update([ - 'status' => 'success', - ]); - } catch (Exception $e) { - ray($e->getMessage()); - $webhook->update([ - 'status' => 'failed', - 'failure_reason' => $e->getMessage(), - ]); - } finally { - return response('OK'); + } + break; } - }); -} + $webhook->update([ + 'status' => 'success', + ]); + } catch (Exception $e) { + ray($e->getMessage()); + $webhook->update([ + 'status' => 'failed', + 'failure_reason' => $e->getMessage(), + ]); + } finally { + return response('OK'); + } +}); From 9940aa68e720946f99774df8c562b05e29db4e8c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 16:45:44 +0200 Subject: [PATCH 08/22] fix error --- app/Providers/FortifyServiceProvider.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index da9282e13..e2bdffd26 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -8,6 +8,7 @@ use App\Actions\Fortify\UpdateUserPassword; use App\Actions\Fortify\UpdateUserProfileInformation; use App\Models\InstanceSettings; use App\Models\User; +use App\Models\Waitlist; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; @@ -45,15 +46,19 @@ class FortifyServiceProvider extends ServiceProvider Fortify::createUsersUsing(CreateNewUser::class); Fortify::registerView(function () { - ray('asd'); $settings = InstanceSettings::get(); + $waiting_in_line = Waitlist::whereVerified(true)->count(); if (!$settings->is_registration_enabled) { return redirect()->route('login'); } if (config('coolify.waitlist')) { - return view('auth.waitlist'); + return view('auth.waitlist',[ + 'waiting_in_line' => $waiting_in_line, + ]); } else { - return view('auth.register'); + return view('auth.register',[ + 'waiting_in_line' => $waiting_in_line, + ]); } }); From eb8f760dca24c5f7ae793fe8a4cf81a75890c7d5 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 16 Aug 2023 08:15:03 +0200 Subject: [PATCH 09/22] update waitlist --- .../views/components/layout-simple.blade.php | 2 +- .../views/components/pricing-plans.blade.php | 18 +++++++++++++----- resources/views/livewire/waitlist.blade.php | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/resources/views/components/layout-simple.blade.php b/resources/views/components/layout-simple.blade.php index 543c826ef..f98a37c25 100644 --- a/resources/views/components/layout-simple.blade.php +++ b/resources/views/components/layout-simple.blade.php @@ -26,7 +26,7 @@ @livewireScripts -
+
{{ $slot }}
diff --git a/resources/views/components/pricing-plans.blade.php b/resources/views/components/pricing-plans.blade.php index 5ccde5e23..6bfcf5ff7 100644 --- a/resources/views/components/pricing-plans.blade.php +++ b/resources/views/components/pricing-plans.blade.php @@ -1,4 +1,7 @@ -
+@props([ + 'showSubscribeButtons' => true, +]) +

(save $6) - + @if(!$showSubscribeButtons) Subscribe Subscribe + @endif

Start self-hosting in the cloud with a @@ -128,7 +132,7 @@ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" /> - 1 server + 1 server

  • (save $29) + @if(!$showSubscribeButtons) Subscribe Subscribe + @endif

    Scale your business or self-hosting environment.

      @@ -198,7 +204,7 @@ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" /> - 5 servers + 1 server
    • (save $69) + @if(!$showSubscribeButtons) Subscribe Subscribe + @endif

      Deploy complex infrastuctures and manage them easily in one place.

        @@ -268,7 +276,7 @@ d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" /> - Unlimited servers + Unlimited servers
      • Join Waitlist - Waiting in the line: {{$waiting_in_line}} + Waiting in the line: {{ $waiting_in_line }} +
  • From 7712a9afacd5c93d7cf43abc9f73b7a26457eb30 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 16 Aug 2023 12:23:43 +0200 Subject: [PATCH 10/22] remove pricing from waitlist --- resources/views/livewire/waitlist.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/waitlist.blade.php b/resources/views/livewire/waitlist.blade.php index a9e42cad8..a872b98ec 100644 --- a/resources/views/livewire/waitlist.blade.php +++ b/resources/views/livewire/waitlist.blade.php @@ -24,6 +24,6 @@ Join Waitlist Waiting in the line: {{ $waiting_in_line }} - + {{-- --}}
    From 701df4b1ad9263dae625584adf0c24c89aabc84f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 16 Aug 2023 14:45:40 +0200 Subject: [PATCH 11/22] new favicon --- public/coolify-transparent.png | Bin 0 -> 4072 bytes public/coolify.png | Bin 0 -> 4238 bytes .../views/components/layout-simple.blade.php | 2 +- .../components/layout-subscription.blade.php | 2 +- resources/views/components/layout.blade.php | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 public/coolify-transparent.png create mode 100644 public/coolify.png diff --git a/public/coolify-transparent.png b/public/coolify-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..22d337a1e97ab8b8b349e695f95fba5c9e3f3606 GIT binary patch literal 4072 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 literal 0 HcmV?d00001 diff --git a/resources/views/components/layout-simple.blade.php b/resources/views/components/layout-simple.blade.php index f98a37c25..39d90813c 100644 --- a/resources/views/components/layout-simple.blade.php +++ b/resources/views/components/layout-simple.blade.php @@ -11,7 +11,7 @@ @else {{ $title ?? 'Coolify' }} - + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) diff --git a/resources/views/components/layout-subscription.blade.php b/resources/views/components/layout-subscription.blade.php index 74fdd4669..26b8fc011 100644 --- a/resources/views/components/layout-subscription.blade.php +++ b/resources/views/components/layout-subscription.blade.php @@ -11,7 +11,7 @@ @else {{ $title ?? 'Coolify' }} - + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) diff --git a/resources/views/components/layout.blade.php b/resources/views/components/layout.blade.php index 029d6e2fa..43d124230 100644 --- a/resources/views/components/layout.blade.php +++ b/resources/views/components/layout.blade.php @@ -11,7 +11,7 @@ @else {{ $title ?? 'Coolify' }} - + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) From d15e1bcc7d1a5e1a841c180d8eeb443c014a3ec5 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 16 Aug 2023 15:47:04 +0200 Subject: [PATCH 12/22] change favicon again --- resources/views/components/layout-simple.blade.php | 2 +- resources/views/components/layout-subscription.blade.php | 2 +- resources/views/components/layout.blade.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/components/layout-simple.blade.php b/resources/views/components/layout-simple.blade.php index 39d90813c..3cfe9bd8e 100644 --- a/resources/views/components/layout-simple.blade.php +++ b/resources/views/components/layout-simple.blade.php @@ -11,7 +11,7 @@ @else {{ $title ?? 'Coolify' }} - + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) diff --git a/resources/views/components/layout-subscription.blade.php b/resources/views/components/layout-subscription.blade.php index 26b8fc011..3ee4d7513 100644 --- a/resources/views/components/layout-subscription.blade.php +++ b/resources/views/components/layout-subscription.blade.php @@ -11,7 +11,7 @@ @else {{ $title ?? 'Coolify' }} - + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) diff --git a/resources/views/components/layout.blade.php b/resources/views/components/layout.blade.php index 43d124230..42444ba05 100644 --- a/resources/views/components/layout.blade.php +++ b/resources/views/components/layout.blade.php @@ -11,7 +11,7 @@ @else {{ $title ?? 'Coolify' }} - + @endenv @vite(['resources/js/app.js', 'resources/css/app.css']) From 3ab38e69fcb8dc859e428fe2225821c157901cf8 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 16 Aug 2023 16:03:30 +0200 Subject: [PATCH 13/22] feat: send internal notification to discord --- app/Jobs/CleanupInstanceStuffsJob.php | 1 + app/Jobs/SendConfirmationForWaitlistJob.php | 1 + .../Internal/GeneralNotification.php | 27 +++++++++++++++++++ bootstrap/helpers/shared.php | 21 +++++++++++---- routes/webhooks.php | 7 ++++- 5 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 app/Notifications/Internal/GeneralNotification.php diff --git a/app/Jobs/CleanupInstanceStuffsJob.php b/app/Jobs/CleanupInstanceStuffsJob.php index 7b3be1a44..e67a562bd 100644 --- a/app/Jobs/CleanupInstanceStuffsJob.php +++ b/app/Jobs/CleanupInstanceStuffsJob.php @@ -29,6 +29,7 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique try { $this->cleanup_waitlist(); } catch (\Exception $e) { + send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage()); ray($e->getMessage()); } } diff --git a/app/Jobs/SendConfirmationForWaitlistJob.php b/app/Jobs/SendConfirmationForWaitlistJob.php index 31098e180..5c1518fc6 100755 --- a/app/Jobs/SendConfirmationForWaitlistJob.php +++ b/app/Jobs/SendConfirmationForWaitlistJob.php @@ -52,6 +52,7 @@ class SendConfirmationForWaitlistJob implements ShouldQueue ->html((string) $mail->render()) ); } catch (\Throwable $th) { + send_internal_notification('SendConfirmationForWaitlistJob failed with error: ' . $th->getMessage()); ray($th->getMessage()); throw $th; } diff --git a/app/Notifications/Internal/GeneralNotification.php b/app/Notifications/Internal/GeneralNotification.php new file mode 100644 index 000000000..ee13a6cc2 --- /dev/null +++ b/app/Notifications/Internal/GeneralNotification.php @@ -0,0 +1,27 @@ +message; + } +} diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 65fe454ab..2d315020f 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1,6 +1,9 @@ user()?->isInstanceAdmin(); } -function general_error_handler(Throwable|null $err = null, $that = null, $isJson = false, $customErrorMessage = null): mixed +function general_error_handler(Throwable | null $err = null, $that = null, $isJson = false, $customErrorMessage = null): mixed { try { ray('ERROR OCCURRED: ' . $err->getMessage()); @@ -47,9 +49,9 @@ function general_error_handler(Throwable|null $err = null, $that = null, $isJson } else { throw new Exception($customErrorMessage ?? $err->errorInfo[2]); } - } elseif($err instanceof TooManyRequestsException){ + } elseif ($err instanceof TooManyRequestsException) { throw new Exception($customErrorMessage ?? "Too many requests. Please try again in {$err->secondsUntilAvailable} seconds."); - }else { + } else { throw new Exception($customErrorMessage ?? $err->getMessage()); } } catch (Throwable $error) { @@ -104,7 +106,7 @@ function is_transactional_emails_active(): bool return data_get(InstanceSettings::get(), 'smtp_enabled'); } -function set_transanctional_email_settings(InstanceSettings|null $settings = null): void +function set_transanctional_email_settings(InstanceSettings | null $settings = null): void { if (!$settings) { $settings = InstanceSettings::get(); @@ -194,3 +196,12 @@ function validate_cron_expression($expression_to_validate): bool } return $isValid; } +function send_internal_notification(string $message): void +{ + try { + $team = Team::find(0); + $team->notify(new GeneralNotification('👀 Internal notifications: ' . $message)); + } catch (\Throwable $th) { + ray($th->getMessage()); + } +} diff --git a/routes/webhooks.php b/routes/webhooks.php index 77167caee..6c66ed706 100644 --- a/routes/webhooks.php +++ b/routes/webhooks.php @@ -174,7 +174,6 @@ Route::post('/source/github/events', function () { return general_error_handler(err: $e); } }); - Route::get('/waitlist/confirm', function () { $email = request()->get('email'); $confirmation_code = request()->get('confirmation_code'); @@ -184,6 +183,7 @@ Route::get('/waitlist/confirm', function () { if ($found && !$found->verified && $found->created_at > now()->subMinutes(config('constants.waitlist.confirmation_valid_for_minutes'))) { $found->verified = true; $found->save(); + send_internal_notification('Waitlist confirmed: ' . $email); return 'Thank you for confirming your email address. We will notify you when you are next in line.'; } return redirect()->route('dashboard'); @@ -199,6 +199,7 @@ Route::get('/waitlist/cancel', function () { $found = Waitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); if ($found && !$found->verified) { $found->delete(); + send_internal_notification('Waitlist cancelled: ' . $email); return 'Your email address has been removed from the waitlist.'; } return redirect()->route('dashboard'); @@ -250,6 +251,7 @@ Route::post('/payments/events', function () { case 'subscription_updated': case 'subscription_resumed': case 'subscription_unpaused': + send_internal_notification('Subscription created or updated: ' . $subscription_id . ' for team ' . $team_id . ' with status ' . $status); $subscription = Subscription::updateOrCreate([ 'team_id' => $team_id, ], [ @@ -271,6 +273,7 @@ Route::post('/payments/events', function () { case 'subscription_expired': $subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first(); if ($subscription) { + send_internal_notification('Subscription cancelled or paused: ' . $subscription_id . ' for team ' . $team_id . ' with status ' . $status); $subscription->update([ 'lemon_status' => $status, 'lemon_trial_ends_at' => $trial_ends_at, @@ -281,11 +284,13 @@ Route::post('/payments/events', function () { } break; } + $webhook->update([ 'status' => 'success', ]); } catch (Exception $e) { ray($e->getMessage()); + send_internal_notification('Subscription webhook failed: ' . $e->getMessage()); $webhook->update([ 'status' => 'failed', 'failure_reason' => $e->getMessage(), From fd74e07fc85936a03e54b06d881366063be0be15 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 16 Aug 2023 16:09:08 +0200 Subject: [PATCH 14/22] update pricing link on waitlist --- resources/views/livewire/waitlist.blade.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/views/livewire/waitlist.blade.php b/resources/views/livewire/waitlist.blade.php index a872b98ec..60b807872 100644 --- a/resources/views/livewire/waitlist.blade.php +++ b/resources/views/livewire/waitlist.blade.php @@ -24,6 +24,8 @@ Join Waitlist Waiting in the line: {{ $waiting_in_line }} - {{-- --}} +
    + See the pricing here. +
    From b34ab8a128fc33c11e835dc00fe83df409cc3332 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 16 Aug 2023 17:18:50 +0200 Subject: [PATCH 15/22] feat: monitor server connection --- app/Console/Kernel.php | 12 ++- app/Http/Livewire/PrivateKey/Change.php | 2 +- app/Http/Livewire/Server/PrivateKey.php | 2 +- app/Jobs/ContainerStatusJob.php | 2 +- ...onsStatusJob.php => ResourceStatusJob.php} | 2 +- app/Notifications/Server/NotReachable.php | 58 +++++++++++ bootstrap/helpers/docker.php | 7 +- bootstrap/helpers/remoteProcess.php | 36 +++++-- ...te_servers_add_unreachable_count_table.php | 28 ++++++ .../views/components/server/navbar.blade.php | 4 +- .../views/livewire/server/form.blade.php | 97 ++++++++++--------- 11 files changed, 184 insertions(+), 66 deletions(-) rename app/Jobs/{InstanceApplicationsStatusJob.php => ResourceStatusJob.php} (92%) create mode 100644 app/Notifications/Server/NotReachable.php create mode 100644 database/migrations/2023_08_15_111126_update_servers_add_unreachable_count_table.php diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 21c4de1d0..047f037d7 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -3,13 +3,12 @@ namespace App\Console; use App\Jobs\CheckResaleLicenseJob; -use App\Jobs\CheckResaleLicenseKeys; use App\Jobs\CleanupInstanceStuffsJob; use App\Jobs\DatabaseBackupJob; use App\Jobs\DockerCleanupJob; -use App\Jobs\InstanceApplicationsStatusJob; use App\Jobs\InstanceAutoUpdateJob; use App\Jobs\ProxyCheckJob; +use App\Jobs\ResourceStatusJob; use App\Models\ScheduledDatabaseBackup; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -21,7 +20,7 @@ class Kernel extends ConsoleKernel // $schedule->call(fn() => $this->check_scheduled_backups($schedule))->everyTenSeconds(); if (is_dev()) { $schedule->command('horizon:snapshot')->everyMinute(); - $schedule->job(new InstanceApplicationsStatusJob)->everyMinute(); + $schedule->job(new ResourceStatusJob)->everyMinute(); $schedule->job(new ProxyCheckJob)->everyFiveMinutes(); $schedule->job(new CleanupInstanceStuffsJob)->everyMinute(); @@ -31,7 +30,7 @@ class Kernel extends ConsoleKernel } else { $schedule->command('horizon:snapshot')->everyFiveMinutes(); $schedule->job(new CleanupInstanceStuffsJob)->everyMinute(); - $schedule->job(new InstanceApplicationsStatusJob)->everyMinute(); + $schedule->job(new ResourceStatusJob)->everyMinute(); $schedule->job(new CheckResaleLicenseJob)->hourly(); $schedule->job(new ProxyCheckJob)->everyFiveMinutes(); $schedule->job(new DockerCleanupJob)->everyTenMinutes(); @@ -49,7 +48,10 @@ class Kernel extends ConsoleKernel return; } foreach ($scheduled_backups as $scheduled_backup) { - if (!$scheduled_backup->enabled) continue; + if (!$scheduled_backup->enabled) { + continue; + } + if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) { $scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency]; } diff --git a/app/Http/Livewire/PrivateKey/Change.php b/app/Http/Livewire/PrivateKey/Change.php index d40e70e34..91d42bd7a 100644 --- a/app/Http/Livewire/PrivateKey/Change.php +++ b/app/Http/Livewire/PrivateKey/Change.php @@ -43,7 +43,7 @@ class Change extends Component $this->private_key->private_key .= "\n"; } $this->private_key->save(); - refreshPrivateKey($this->private_key); + refresh_server_connection($this->private_key); } catch (\Exception $e) { return general_error_handler(err: $e, that: $this); } diff --git a/app/Http/Livewire/Server/PrivateKey.php b/app/Http/Livewire/Server/PrivateKey.php index 366aec85f..8f433f73d 100644 --- a/app/Http/Livewire/Server/PrivateKey.php +++ b/app/Http/Livewire/Server/PrivateKey.php @@ -17,7 +17,7 @@ class PrivateKey extends Component $this->server->update([ 'private_key_id' => $private_key_id ]); - refreshPrivateKey($this->server->privateKey); + refresh_server_connection($this->server->privateKey); $this->server->refresh(); $this->checkConnection(); } diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php index 083023929..f2be8b4d7 100644 --- a/app/Jobs/ContainerStatusJob.php +++ b/app/Jobs/ContainerStatusJob.php @@ -35,7 +35,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeUnique { try { $status = get_container_status(server: $this->resource->destination->server, container_id: $this->container_name, throwError: false); - if ($this->resource->status === 'running' && $status === 'stopped') { + if ($this->resource->status === 'running' && $status !== 'running') { $this->resource->environment->project->team->notify(new StatusChanged($this->resource)); } diff --git a/app/Jobs/InstanceApplicationsStatusJob.php b/app/Jobs/ResourceStatusJob.php similarity index 92% rename from app/Jobs/InstanceApplicationsStatusJob.php rename to app/Jobs/ResourceStatusJob.php index fd173215b..5ac162d3f 100644 --- a/app/Jobs/InstanceApplicationsStatusJob.php +++ b/app/Jobs/ResourceStatusJob.php @@ -10,7 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class InstanceApplicationsStatusJob implements ShouldQueue, ShouldBeUnique +class ResourceStatusJob implements ShouldQueue, ShouldBeUnique { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; diff --git a/app/Notifications/Server/NotReachable.php b/app/Notifications/Server/NotReachable.php new file mode 100644 index 000000000..0b5f71569 --- /dev/null +++ b/app/Notifications/Server/NotReachable.php @@ -0,0 +1,58 @@ +fqdn; + $mail->subject("â›” Server '{$this->server->name}' is unreachable"); + // $mail->view('emails.application-status-changes', [ + // 'name' => $this->application_name, + // 'fqdn' => $fqdn, + // 'application_url' => $this->application_url, + // ]); + return $mail; + } + + public function toDiscord(): string + { + $message = 'â›” Server \'' . $this->server->name . '\' is unreachable (could be a temporary issue). If you receive this more than twice in a row, please check your server.'; + return $message; + } +} diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 2787f153e..760f520e9 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -8,8 +8,8 @@ function format_docker_command_output_to_json($rawOutput): Collection $outputLines = explode(PHP_EOL, $rawOutput); return collect($outputLines) - ->reject(fn ($line) => empty($line)) - ->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR)); + ->reject(fn($line) => empty($line)) + ->map(fn($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR)); } function format_docker_labels_to_json($rawOutput): Collection @@ -17,7 +17,7 @@ function format_docker_labels_to_json($rawOutput): Collection $outputLines = explode(PHP_EOL, $rawOutput); return collect($outputLines) - ->reject(fn ($line) => empty($line)) + ->reject(fn($line) => empty($line)) ->map(function ($outputLine) { $outputArray = explode(',', $outputLine); return collect($outputArray) @@ -45,6 +45,7 @@ function format_docker_envs_to_json($rawOutput) function get_container_status(Server $server, string $container_id, bool $all_data = false, bool $throwError = false) { + check_server_connection($server); $container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError); if (!$container) { return 'exited'; diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index 3f8f567d8..f5bb61d75 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -7,6 +7,7 @@ use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\PrivateKey; use App\Models\Server; +use App\Notifications\Server\NotReachable; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Collection; @@ -109,8 +110,8 @@ function instant_remote_process(array $command, Server $server, $throwError = tr $exitCode = $process->exitCode(); if ($exitCode !== 0) { if ($repeat > 1) { + ray("repeat: ", $repeat); Sleep::for(200)->milliseconds(); - ray('executing again'); return instant_remote_process($command, $server, $throwError, $repeat - 1); } // ray('ERROR OCCURED: ' . $process->errorOutput()); @@ -152,21 +153,22 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d return $formatted; } -function refreshPrivateKey(PrivateKey $private_key) +function refresh_server_connection(PrivateKey $private_key) { foreach ($private_key->servers as $server) { // Delete the old ssh mux file to force a new one to be created Storage::disk('ssh-mux')->delete($server->muxFilename()); - if (auth()->user()->currentTeam()->id) { - auth()->user()->currentTeam()->privateKeys = PrivateKey::where('team_id', auth()->user()->currentTeam()->id)->get(); - } + // check if user is authenticated + if (auth()?->user()?->currentTeam()->id) { + auth()->user()->currentTeam()->privateKeys = PrivateKey::where('team_id', auth()->user()->currentTeam()->id)->get(); + } } } function validateServer(Server $server) { try { - refreshPrivateKey($server->privateKey); + refresh_server_connection($server->privateKey); $uptime = instant_remote_process(['uptime'], $server); if (!$uptime) { $uptime = 'Server not reachable.'; @@ -192,3 +194,25 @@ function validateServer(Server $server) $server->settings->save(); } } + +function check_server_connection(Server $server) { + try { + refresh_server_connection($server->privateKey); + instant_remote_process(['uptime'], $server); + $server->unreachable_count = 0; + $server->settings->is_reachable = true; + } catch (\Exception $e) { + if ($server->unreachable_count == 2) { + $server->team->notify(new NotReachable($server)); + $server->settings->is_reachable = false; + $server->settings->save(); + } else { + $server->unreachable_count += 1; + } + + throw $e; + } finally { + $server->settings->save(); + $server->save(); + } +} diff --git a/database/migrations/2023_08_15_111126_update_servers_add_unreachable_count_table.php b/database/migrations/2023_08_15_111126_update_servers_add_unreachable_count_table.php new file mode 100644 index 000000000..b6a0d710a --- /dev/null +++ b/database/migrations/2023_08_15_111126_update_servers_add_unreachable_count_table.php @@ -0,0 +1,28 @@ +integer('unreachable_count')->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('unreachable_count'); + }); + } +}; diff --git a/resources/views/components/server/navbar.blade.php b/resources/views/components/server/navbar.blade.php index 32691c44d..382e968c3 100644 --- a/resources/views/components/server/navbar.blade.php +++ b/resources/views/components/server/navbar.blade.php @@ -1,7 +1,9 @@

    Server

    - + @if ($server->settings->is_reachable) + + @endif
    {{ data_get($server, 'name') }}