From e2db5e0b1eb6e04a7823d9e70cc9e0dfab3e8ce1 Mon Sep 17 00:00:00 2001 From: Matheus Pratta Date: Sun, 16 Jun 2024 01:22:44 -0300 Subject: [PATCH 01/38] fix: show proper error message on invalid Git source --- app/Models/Application.php | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/app/Models/Application.php b/app/Models/Application.php index 6e55f6626..e3d05bc2c 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -704,6 +704,121 @@ class Application extends BaseModel return $git_clone_command; } + public function getGitRemoteStatus(string $deployment_uuid) + { + try { + ['commands' => $lsRemoteCommand] = $this->generateGitLsRemoteCommands(deployment_uuid: $deployment_uuid, exec_in_docker: false); + instant_remote_process([$lsRemoteCommand], $this->destination->server, true); + return [ + 'is_accessible' => true, + 'error' => null, + ]; + } catch (\RuntimeException $ex) { + return [ + 'is_accessible' => false, + 'error' => $ex->getMessage(), + ]; + } + } + + public function generateGitLsRemoteCommands(string $deployment_uuid, bool $exec_in_docker = true) + { + $branch = $this->git_branch; + ['repository' => $customRepository, 'port' => $customPort] = $this->customRepository(); + $commands = collect([]); + $base_command = "git ls-remote"; + + if ($this->deploymentType() === 'source') { + $source_html_url = data_get($this, 'source.html_url'); + $url = parse_url(filter_var($source_html_url, FILTER_SANITIZE_URL)); + $source_html_url_host = $url['host']; + $source_html_url_scheme = $url['scheme']; + + if ($this->source->getMorphClass() == 'App\Models\GithubApp') { + if ($this->source->is_public) { + $fullRepoUrl = "{$this->source->html_url}/{$customRepository}"; + $base_command = "{$base_command} {$this->source->html_url}/{$customRepository}"; + } else { + $github_access_token = generate_github_installation_token($this->source); + + if ($exec_in_docker) { + $base_command = "{$base_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git"; + $fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git"; + } else { + $base_command = "{$base_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}"; + $fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}"; + } + } + + if ($exec_in_docker) { + $commands->push(executeInDocker($deployment_uuid, $base_command)); + } else { + $commands->push($base_command); + } + + return [ + 'commands' => $commands->implode(' && '), + 'branch' => $branch, + 'fullRepoUrl' => $fullRepoUrl, + ]; + } + } + + if ($this->deploymentType() === 'deploy_key') { + $fullRepoUrl = $customRepository; + $private_key = data_get($this, 'private_key.private_key'); + if (is_null($private_key)) { + throw new RuntimeException('Private key not found. Please add a private key to the application and try again.'); + } + $private_key = base64_encode($private_key); + $base_comamnd = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$base_command} {$customRepository}"; + + if ($exec_in_docker) { + $commands = collect([ + executeInDocker($deployment_uuid, 'mkdir -p /root/.ssh'), + executeInDocker($deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"), + executeInDocker($deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'), + ]); + } else { + $commands = collect([ + 'mkdir -p /root/.ssh', + "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null", + 'chmod 600 /root/.ssh/id_rsa', + ]); + } + + if ($exec_in_docker) { + $commands->push(executeInDocker($deployment_uuid, $base_comamnd)); + } else { + $commands->push($base_comamnd); + } + + return [ + 'commands' => $commands->implode(' && '), + 'branch' => $branch, + 'fullRepoUrl' => $fullRepoUrl, + ]; + } + + if ($this->deploymentType() === 'other') { + $fullRepoUrl = $customRepository; + $base_command = "{$base_command} {$customRepository}"; + $base_command = $this->setGitImportSettings($deployment_uuid, $base_command, public: true); + + if ($exec_in_docker) { + $commands->push(executeInDocker($deployment_uuid, $base_command)); + } else { + $commands->push($base_command); + } + + return [ + 'commands' => $commands->implode(' && '), + 'branch' => $branch, + 'fullRepoUrl' => $fullRepoUrl, + ]; + } + } + public function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null, ?string $commit = null) { $branch = $this->git_branch; @@ -966,6 +1081,12 @@ class Application extends BaseModel // if ($composeFile !== $prComposeFile) { // $fileList->push(".$prComposeFile"); // } + + $gitRemoteStatus = $this->getGitRemoteStatus(deployment_uuid: $uuid); + if (! $gitRemoteStatus['is_accessible']) { + throw new \RuntimeException("Failed to read Git source:\n\n{$gitRemoteStatus['error']}"); + } + $commands = collect([ "rm -rf /tmp/{$uuid}", "mkdir -p /tmp/{$uuid}", From e5da464980de312267a7a05b5e6b3f81a8ef6fb7 Mon Sep 17 00:00:00 2001 From: Matheus Pratta Date: Sun, 16 Jun 2024 02:23:29 -0300 Subject: [PATCH 02/38] fix: convert HTTP to SSH source when using deploy key on GitHub --- app/Models/Application.php | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/app/Models/Application.php b/app/Models/Application.php index 6e55f6626..3fb7da903 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -662,6 +662,37 @@ class Application extends BaseModel public function customRepository() { + $repository = $this->git_repository; + + // Let's try and parse the string to detect if it's a valid SSH string or not + $sshMatches = []; + preg_match('/((.*?)\:\/\/)?(.*@.*:.*)/', $this->git_repository, $sshMatches); + + if ($this->deploymentType() === 'deploy_key' && empty($sshMatches) && $this->source) { + // If this happens, the user may have provided an HTTP URL when they needed an SSH one + // Let's try and fix that for known Git providers + $providerInfo = [ + 'host' => null, + 'user' => 'git', + 'port' => 22, + 'repository' => $this->git_repository, + ]; + + switch ($this->source->getMorphClass()) { + case \App\Models\GithubApp::class: + $providerInfo['host'] = Url::fromString($this->source->html_url)->getHost(); + $providerInfo['port'] = $this->source->custom_port; + $providerInfo['user'] = $this->source->custom_user; + break; + } + + if (! empty($providerInfo['host'])) { + $repository = ($providerInfo['port'] === 22) + ? "{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['repository']}" + : "ssh://{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['port']}/{$providerInfo['repository']}"; + } + } + preg_match('/(?<=:)\d+(?=\/)/', $this->git_repository, $matches); $port = 22; if (count($matches) === 1) { @@ -669,8 +700,6 @@ class Application extends BaseModel $gitHost = str($this->git_repository)->before(':'); $gitRepo = str($this->git_repository)->after('/'); $repository = "$gitHost:$gitRepo"; - } else { - $repository = $this->git_repository; } return [ From f2656e4ff22ccaea92f7f4f122694ffde055345a Mon Sep 17 00:00:00 2001 From: Marcon Neves Date: Sun, 3 Nov 2024 09:41:14 -0300 Subject: [PATCH 03/38] Update schema of private keys by id response --- openapi.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index d2616e9c6..a56a7970e 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3467,9 +3467,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/PrivateKey' + $ref: '#/components/schemas/PrivateKey' '401': $ref: '#/components/responses/401' '400': From 4a6df8b6f917aadfd3ae147bcf331857ab1202fd Mon Sep 17 00:00:00 2001 From: Marcon Neves Date: Sun, 3 Nov 2024 14:03:59 -0300 Subject: [PATCH 04/38] fix: update schema in code decorator --- app/Http/Controllers/Api/SecurityController.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/Api/SecurityController.php b/app/Http/Controllers/Api/SecurityController.php index bb474aed3..b7190ab1e 100644 --- a/app/Http/Controllers/Api/SecurityController.php +++ b/app/Http/Controllers/Api/SecurityController.php @@ -81,15 +81,8 @@ class SecurityController extends Controller new OA\Response( response: 200, description: 'Get all private keys.', - content: [ - new OA\MediaType( - mediaType: 'application/json', - schema: new OA\Schema( - type: 'array', - items: new OA\Items(ref: '#/components/schemas/PrivateKey') - ) - ), - ]), + content: new OA\JsonContent(ref: '#/components/schemas/PrivateKey') + ), new OA\Response( response: 401, ref: '#/components/responses/401', From 752ae4a110b090e73647a5508bc4a7af68415665 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Tue, 5 Nov 2024 13:23:03 +0000 Subject: [PATCH 05/38] add feedback on docker version check failure --- .../server/validate-and-install.blade.php | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/resources/views/livewire/server/validate-and-install.blade.php b/resources/views/livewire/server/validate-and-install.blade.php index fc499f98f..0acc7972e 100644 --- a/resources/views/livewire/server/validate-and-install.blade.php +++ b/resources/views/livewire/server/validate-and-install.blade.php @@ -86,16 +86,24 @@ @isset($docker_version) + @if($docker_version)
Minimum Docker version: + + + + +
+ @else +
Minimum Docker version: - - - - +
+ @endif @else
@endisset From 3fe636aab69946fafeefa9343acef3c8ef59f7a1 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 7 Nov 2024 21:50:53 +0000 Subject: [PATCH 06/38] update docker minimum version to 26 --- app/Livewire/Server/ValidateAndInstall.php | 2 +- bootstrap/helpers/docker.php | 3 ++- resources/views/livewire/boarding/index.blade.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php index 8c5bc23ed..700c31781 100644 --- a/app/Livewire/Server/ValidateAndInstall.php +++ b/app/Livewire/Server/ValidateAndInstall.php @@ -159,7 +159,7 @@ class ValidateAndInstall extends Component $this->dispatch('refreshBoardingIndex'); $this->dispatch('success', 'Server validated.'); } else { - $this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: documentation.'; + $this->error = 'Docker Engine version is not 25+. Please install Docker manually before continuing: documentation.'; $this->server->update([ 'validation_logs' => $this->error, ]); diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 2e583b94d..cd87fdb39 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -109,7 +109,8 @@ function format_docker_envs_to_json($rawOutput) function checkMinimumDockerEngineVersion($dockerVersion) { $majorDockerVersion = str($dockerVersion)->before('.')->value(); - if ($majorDockerVersion <= 22) { + $requiredDockerVersion = str(config('constants.docker_install_version'))->before('.')->value(); + if ($majorDockerVersion <= $requiredDockerVersion) { $dockerVersion = null; } diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index 5c5dc09a0..7095d6149 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -323,7 +323,7 @@

This will install the latest Docker Engine on your server, configure a few things to be able - to run optimal.

Minimum Docker Engine version is: 22

To manually install + to run optimal.

Minimum Docker Engine version is: 26

To manually install Docker Engine, check this From 88e6c04b73dac1c693c0180aebb5a29521b70486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kolja=20V=C3=B6lker?= Date: Fri, 8 Nov 2024 15:15:58 +0100 Subject: [PATCH 07/38] Fixes: https://github.com/coollabsio/coolify/issues/4186 --- app/Http/Controllers/Webhook/Gitlab.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php index ec7f51a0d..5ecc14554 100644 --- a/app/Http/Controllers/Webhook/Gitlab.php +++ b/app/Http/Controllers/Webhook/Gitlab.php @@ -34,6 +34,7 @@ class Gitlab extends Controller return; } + $return_payloads = collect([]); $payload = $request->collect(); $headers = $request->headers->all(); @@ -49,6 +50,16 @@ class Gitlab extends Controller return response($return_payloads); } + if (empty($x_gitlab_token)) { + $return_payloads->push([ + 'status' => 'failed', + 'message' => 'Invalid signature.', + ]); + ray('Invalid signature'); + + return response($return_payloads); + } + if ($x_gitlab_event === 'push') { $branch = data_get($payload, 'ref'); $full_name = data_get($payload, 'project.path_with_namespace'); From 770163d54cdfab6eaedf398d243bb23acdb562c4 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Sun, 10 Nov 2024 21:28:12 +0000 Subject: [PATCH 08/38] using docker version constant as user feedback --- app/Livewire/Boarding/Index.php | 4 ++++ app/Livewire/Server/ValidateAndInstall.php | 3 ++- resources/views/livewire/boarding/index.blade.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index c9c3092b3..3d18ee5eb 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -66,11 +66,15 @@ class Index extends Component public bool $serverReachable = true; + public ?string $minDockerVersion = null; + public function mount() { if (auth()->user()?->isMember() && auth()->user()->currentTeam()->show_boarding === true) { return redirect()->route('dashboard'); } + + $this->minDockerVersion = str(config('constants.docker_install_version'))->before('.'); $this->privateKeyName = generate_random_name(); $this->remoteServerName = generate_random_name(); if (isDev()) { diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php index 700c31781..eed368973 100644 --- a/app/Livewire/Server/ValidateAndInstall.php +++ b/app/Livewire/Server/ValidateAndInstall.php @@ -159,7 +159,8 @@ class ValidateAndInstall extends Component $this->dispatch('refreshBoardingIndex'); $this->dispatch('success', 'Server validated.'); } else { - $this->error = 'Docker Engine version is not 25+. Please install Docker manually before continuing: documentation.'; + $requiredDockerVersion = str(config('constants.docker_install_version'))->before('.'); + $this->error = 'Minimum Docker Engine version '.$requiredDockerVersion.' is not instaled. Please install Docker manually before continuing: documentation.'; $this->server->update([ 'validation_logs' => $this->error, ]); diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index 7095d6149..f0f52aa8b 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -323,7 +323,7 @@

This will install the latest Docker Engine on your server, configure a few things to be able - to run optimal.

Minimum Docker Engine version is: 26

To manually install + to run optimal.

Minimum Docker Engine version is: {{ $minDockerVersion }}

To manually install Docker Engine, check this From ee2b95733b4354e0e24955dee03430f2179b7042 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Sun, 10 Nov 2024 22:08:48 +0000 Subject: [PATCH 09/38] validating minimum docker version with less that --- bootstrap/helpers/docker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index cd87fdb39..ec6be7211 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -110,7 +110,7 @@ function checkMinimumDockerEngineVersion($dockerVersion) { $majorDockerVersion = str($dockerVersion)->before('.')->value(); $requiredDockerVersion = str(config('constants.docker_install_version'))->before('.')->value(); - if ($majorDockerVersion <= $requiredDockerVersion) { + if ($majorDockerVersion < $requiredDockerVersion) { $dockerVersion = null; } From 1faa8be615faaec58acf029165fefcd2904f3d2b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Nov 2024 22:18:12 +0100 Subject: [PATCH 10/38] fix: cloud + stripe related --- .../Commands/CloudCheckSubscription.php | 49 +++++++++++++++++++ app/Http/Controllers/Webhook/Stripe.php | 39 +-------------- app/Jobs/SubscriptionTrialEndedJob.php | 42 ---------------- app/Jobs/SubscriptionTrialEndsSoonJob.php | 42 ---------------- app/Livewire/Admin/Index.php | 12 ++--- app/Models/Team.php | 19 +++---- 6 files changed, 62 insertions(+), 141 deletions(-) create mode 100644 app/Console/Commands/CloudCheckSubscription.php delete mode 100755 app/Jobs/SubscriptionTrialEndedJob.php delete mode 100755 app/Jobs/SubscriptionTrialEndsSoonJob.php diff --git a/app/Console/Commands/CloudCheckSubscription.php b/app/Console/Commands/CloudCheckSubscription.php new file mode 100644 index 000000000..6e237e84b --- /dev/null +++ b/app/Console/Commands/CloudCheckSubscription.php @@ -0,0 +1,49 @@ +get(); + foreach ($activeSubscribers as $team) { + $stripeSubscriptionId = $team->subscription->stripe_subscription_id; + $stripeInvoicePaid = $team->subscription->stripe_invoice_paid; + $stripeCustomerId = $team->subscription->stripe_customer_id; + if (! $stripeSubscriptionId) { + echo "Team {$team->id} has no subscription, but invoice status is: {$stripeInvoicePaid}\n"; + echo "Link on Stripe: https://dashboard.stripe.com/customers/{$stripeCustomerId}\n"; + + continue; + } + $subscription = $stripe->subscriptions->retrieve($stripeSubscriptionId); + if ($subscription->status === 'active') { + continue; + } + echo "Subscription {$stripeSubscriptionId} is not active ({$subscription->status})\n"; + echo "Link on Stripe: https://dashboard.stripe.com/subscriptions/{$stripeSubscriptionId}\n"; + } + } +} diff --git a/app/Http/Controllers/Webhook/Stripe.php b/app/Http/Controllers/Webhook/Stripe.php index 5d297b242..e94209b23 100644 --- a/app/Http/Controllers/Webhook/Stripe.php +++ b/app/Http/Controllers/Webhook/Stripe.php @@ -5,8 +5,6 @@ namespace App\Http\Controllers\Webhook; use App\Http\Controllers\Controller; use App\Jobs\ServerLimitCheckJob; use App\Jobs\SubscriptionInvoiceFailedJob; -use App\Jobs\SubscriptionTrialEndedJob; -use App\Jobs\SubscriptionTrialEndsSoonJob; use App\Models\Subscription; use App\Models\Team; use App\Models\Webhook; @@ -260,42 +258,7 @@ class Stripe extends Controller $customerId = data_get($data, 'customer'); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $team = data_get($subscription, 'team'); - if ($team) { - $team->trialEnded(); - } - $subscription->update([ - 'stripe_subscription_id' => null, - 'stripe_plan_id' => null, - 'stripe_cancel_at_period_end' => false, - 'stripe_invoice_paid' => false, - 'stripe_trial_already_ended' => false, - ]); - // send_internal_notification('customer.subscription.deleted for customer: '.$customerId); - break; - case 'customer.subscription.trial_will_end': - // Not used for now - $customerId = data_get($data, 'customer'); - $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); - $team = data_get($subscription, 'team'); - if (! $team) { - return response('No team found for subscription: '.$subscription->id, 400); - } - SubscriptionTrialEndsSoonJob::dispatch($team); - break; - case 'customer.subscription.paused': - $customerId = data_get($data, 'customer'); - $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); - $team = data_get($subscription, 'team'); - if (! $team) { - return response('No team found for subscription: '.$subscription->id, 400); - } - $team->trialEnded(); - $subscription->update([ - 'stripe_trial_already_ended' => true, - 'stripe_invoice_paid' => false, - ]); - SubscriptionTrialEndedJob::dispatch($team); - // send_internal_notification('Subscription paused for customer: '.$customerId); + $team?->subscriptionEnded(); break; default: // Unhandled event type diff --git a/app/Jobs/SubscriptionTrialEndedJob.php b/app/Jobs/SubscriptionTrialEndedJob.php deleted file mode 100755 index 88a5e06be..000000000 --- a/app/Jobs/SubscriptionTrialEndedJob.php +++ /dev/null @@ -1,42 +0,0 @@ -team); - $mail = new MailMessage; - $mail->subject('Action required: You trial in Coolify Cloud ended.'); - $mail->view('emails.trial-ended', [ - 'stripeCustomerPortal' => $session->url, - ]); - $this->team->members()->each(function ($member) use ($mail) { - if ($member->isAdmin()) { - send_user_an_email($mail, $member->email); - send_internal_notification('Trial reminder email sent to '.$member->email); - } - }); - } catch (\Throwable $e) { - send_internal_notification('SubscriptionTrialEndsSoonJob failed with: '.$e->getMessage()); - throw $e; - } - } -} diff --git a/app/Jobs/SubscriptionTrialEndsSoonJob.php b/app/Jobs/SubscriptionTrialEndsSoonJob.php deleted file mode 100755 index 2a76a1097..000000000 --- a/app/Jobs/SubscriptionTrialEndsSoonJob.php +++ /dev/null @@ -1,42 +0,0 @@ -team); - $mail = new MailMessage; - $mail->subject('You trial in Coolify Cloud ends soon.'); - $mail->view('emails.trial-ends-soon', [ - 'stripeCustomerPortal' => $session->url, - ]); - $this->team->members()->each(function ($member) use ($mail) { - if ($member->isAdmin()) { - send_user_an_email($mail, $member->email); - send_internal_notification('Trial reminder email sent to '.$member->email); - } - }); - } catch (\Throwable $e) { - send_internal_notification('SubscriptionTrialEndsSoonJob failed with: '.$e->getMessage()); - throw $e; - } - } -} diff --git a/app/Livewire/Admin/Index.php b/app/Livewire/Admin/Index.php index 2579c3db2..359db6329 100644 --- a/app/Livewire/Admin/Index.php +++ b/app/Livewire/Admin/Index.php @@ -2,8 +2,8 @@ namespace App\Livewire\Admin; +use App\Models\Team; use App\Models\User; -use Illuminate\Container\Attributes\Auth as AttributesAuth; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; @@ -43,17 +43,13 @@ class Index extends Component public function getSubscribers() { - $this->inactiveSubscribers = User::whereDoesntHave('teams', function ($query) { - $query->whereRelation('subscription', 'stripe_subscription_id', '!=', null); - })->count(); - $this->activeSubscribers = User::whereHas('teams', function ($query) { - $query->whereRelation('subscription', 'stripe_subscription_id', '!=', null); - })->count(); + $this->inactiveSubscribers = Team::whereRelation('subscription', 'stripe_invoice_paid', false)->count(); + $this->activeSubscribers = Team::whereRelation('subscription', 'stripe_invoice_paid', true)->count(); } public function switchUser(int $user_id) { - if (AttributesAuth::id() !== 0) { + if (Auth::id() !== 0) { return redirect()->route('dashboard'); } $user = User::find($user_id); diff --git a/app/Models/Team.php b/app/Models/Team.php index db485054b..8996b745c 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -257,8 +257,15 @@ class Team extends Model implements SendsDiscord, SendsEmail return $this->hasMany(S3Storage::class)->where('is_usable', true); } - public function trialEnded() + public function subscriptionEnded() { + $this->subscription->update([ + 'stripe_subscription_id' => null, + 'stripe_plan_id' => null, + 'stripe_cancel_at_period_end' => false, + 'stripe_invoice_paid' => false, + 'stripe_trial_already_ended' => false, + ]); foreach ($this->servers as $server) { $server->settings()->update([ 'is_usable' => false, @@ -267,16 +274,6 @@ class Team extends Model implements SendsDiscord, SendsEmail } } - public function trialEndedButSubscribed() - { - foreach ($this->servers as $server) { - $server->settings()->update([ - 'is_usable' => true, - 'is_reachable' => true, - ]); - } - } - public function isAnyNotificationEnabled() { if (isCloud()) { From c6315f6de92cb7d5106008f9401d41117fbe7cce Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 11 Nov 2024 22:20:38 +0100 Subject: [PATCH 11/38] version++ --- config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/sentry.php b/config/sentry.php index 117a0e6d8..232070705 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.367', + 'release' => '4.0.0-beta.368', // 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 3b32ad4af..ddb1cb354 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Mon, 11 Nov 2024 23:35:48 +0100 Subject: [PATCH 12/38] feat: check local horizon scheduler deployments --- .../Application/AnyDeploymentsRunning.php | 39 +++++++++++++++++++ app/Jobs/ApplicationDeploymentJob.php | 5 +++ 2 files changed, 44 insertions(+) create mode 100644 app/Actions/Application/AnyDeploymentsRunning.php diff --git a/app/Actions/Application/AnyDeploymentsRunning.php b/app/Actions/Application/AnyDeploymentsRunning.php new file mode 100644 index 000000000..308226955 --- /dev/null +++ b/app/Actions/Application/AnyDeploymentsRunning.php @@ -0,0 +1,39 @@ +getRecent(); + if ($recent) { + $running = $recent->filter(function ($job) use ($hostname) { + $payload = json_decode($job->payload); + $tags = data_get($payload, 'tags'); + + return $job->status != 'completed' && + $job->status != 'failed' && + isset($tags) && + is_array($tags) && + in_array('server:'.$hostname, $tags); + }); + if ($running->count() > 0) { + dump($running); + echo 'true'; + + return true; + } + } + + echo 'false'; + + return false; + } +} diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 27eded4f3..27f77f7a1 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -225,6 +225,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue } } + public function tags(): array + { + return ['server:'.gethostname()]; + } + public function handle(): void { $this->application_deployment_queue->update([ From b58cc05b8aea540b0347fe54a0a1adad9343940f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 09:52:22 +0100 Subject: [PATCH 13/38] fix: terminal view loading in async --- app/Livewire/Terminal/Index.php | 14 +++++- .../views/livewire/terminal/index.blade.php | 45 ++++++++++--------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/app/Livewire/Terminal/Index.php b/app/Livewire/Terminal/Index.php index 945b25714..a24a237c5 100644 --- a/app/Livewire/Terminal/Index.php +++ b/app/Livewire/Terminal/Index.php @@ -14,13 +14,25 @@ class Index extends Component public $containers = []; + public bool $isLoadingContainers = true; + public function mount() { if (! auth()->user()->isAdmin()) { abort(403); } $this->servers = Server::isReachable()->get(); - $this->containers = $this->getAllActiveContainers(); + } + + public function loadContainers() + { + try { + $this->containers = $this->getAllActiveContainers(); + } catch (\Exception $e) { + return handleError($e, $this); + } finally { + $this->isLoadingContainers = false; + } } private function getAllActiveContainers() diff --git a/resources/views/livewire/terminal/index.blade.php b/resources/views/livewire/terminal/index.blade.php index 357295002..0471a44a4 100644 --- a/resources/views/livewire/terminal/index.blade.php +++ b/resources/views/livewire/terminal/index.blade.php @@ -8,27 +8,32 @@ -

From f14cef0651d566656430ea5c51520e9fad16cf95 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 10:01:53 +0100 Subject: [PATCH 14/38] fix: cool 500 error (thanks hugodos) --- resources/views/errors/500.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 4c64d4f8d..f9de400db 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -2,7 +2,7 @@

500

-

Something is not okay, are you okay?

+

Wait, this is not cool...

There has been an error, we are working on it.

@if ($exception->getMessage() !== '') From f0985a7e47bb147e766d0ff4efdeea65fa7e1f8c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 10:16:34 +0100 Subject: [PATCH 15/38] fix horizon command name --- ...eploymentsRunning.php => IsHorizonQueueEmpty.php} | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) rename app/Actions/Application/{AnyDeploymentsRunning.php => IsHorizonQueueEmpty.php} (83%) diff --git a/app/Actions/Application/AnyDeploymentsRunning.php b/app/Actions/Application/IsHorizonQueueEmpty.php similarity index 83% rename from app/Actions/Application/AnyDeploymentsRunning.php rename to app/Actions/Application/IsHorizonQueueEmpty.php index 308226955..17966b8a0 100644 --- a/app/Actions/Application/AnyDeploymentsRunning.php +++ b/app/Actions/Application/IsHorizonQueueEmpty.php @@ -5,7 +5,7 @@ namespace App\Actions\Application; use Laravel\Horizon\Contracts\JobRepository; use Lorisleiva\Actions\Concerns\AsAction; -class AnyDeploymentsRunning +class IsHorizonQueueEmpty { use AsAction; @@ -25,15 +25,13 @@ class AnyDeploymentsRunning in_array('server:'.$hostname, $tags); }); if ($running->count() > 0) { - dump($running); - echo 'true'; + echo 'false'; - return true; + return false; } } + echo 'true'; - echo 'false'; - - return false; + return true; } } From 8e4060375d5a34561113e6ea87e88b4f50b6ef85 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 10:23:00 +0100 Subject: [PATCH 16/38] remove ray --- app/Http/Controllers/Webhook/Gitlab.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php index 32baa660f..d8dcc0c3b 100644 --- a/app/Http/Controllers/Webhook/Gitlab.php +++ b/app/Http/Controllers/Webhook/Gitlab.php @@ -54,7 +54,6 @@ class Gitlab extends Controller 'status' => 'failed', 'message' => 'Invalid signature.', ]); - ray('Invalid signature'); return response($return_payloads); } From 99705ee9f00a705b935c0e86c00107d7019911c4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 10:25:55 +0100 Subject: [PATCH 17/38] fix description --- app/Http/Controllers/Api/ProjectController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index b69028b70..1d89c82ed 100644 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -116,7 +116,7 @@ class ProjectController extends Controller responses: [ new OA\Response( response: 200, - description: 'Project details', + description: 'Environment details', content: new OA\JsonContent(ref: '#/components/schemas/Environment')), new OA\Response( response: 401, From 0ab77c3e9eefc85b400ee78758dfe2b985b6fd07 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 10:27:36 +0100 Subject: [PATCH 18/38] fix: openapi docs --- openapi.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openapi.yaml b/openapi.yaml index a56a7970e..f5abefa1e 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3311,7 +3311,7 @@ paths: type: string responses: '200': - description: 'Project details' + description: 'Environment details' content: application/json: schema: @@ -4757,6 +4757,10 @@ components: compose_parsing_version: type: string description: 'How Coolify parse the compose file.' + custom_nginx_configuration: + type: string + nullable: true + description: 'Custom Nginx configuration base64 encoded.' type: object ApplicationDeploymentQueue: description: 'Project model' @@ -5134,6 +5138,9 @@ components: smtp_notifications_database_backups: type: boolean description: 'Whether to send database backup notifications via SMTP.' + smtp_notifications_server_disk_usage: + type: boolean + description: 'Whether to send server disk usage notifications via SMTP.' discord_enabled: type: boolean description: 'Whether Discord is enabled or not.' @@ -5155,6 +5162,9 @@ components: discord_notifications_scheduled_tasks: type: boolean description: 'Whether to send scheduled task notifications via Discord.' + discord_notifications_server_disk_usage: + type: boolean + description: 'Whether to send server disk usage notifications via Discord.' show_boarding: type: boolean description: 'Whether to show the boarding screen or not.' From 327b4308d44922b6606342e292ba7956cc4f0579 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 11:32:18 +0100 Subject: [PATCH 19/38] fix: add tests for git url converts --- app/Models/Application.php | 45 +--- bootstrap/helpers/shared.php | 51 ++++ scripts/run | 2 +- tests/Feature/ConvertingGitUrlsTest.php | 62 +++++ tests/Feature/DockerComposeParseTest.php | 316 +++++++++++------------ 5 files changed, 273 insertions(+), 203 deletions(-) create mode 100644 tests/Feature/ConvertingGitUrlsTest.php diff --git a/app/Models/Application.php b/app/Models/Application.php index 2a5fc629e..e79a5e702 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -906,50 +906,7 @@ class Application extends BaseModel public function customRepository() { - $repository = $this->git_repository; - - // Let's try and parse the string to detect if it's a valid SSH string or not - $sshMatches = []; - preg_match('/((.*?)\:\/\/)?(.*@.*:.*)/', $this->git_repository, $sshMatches); - - if ($this->deploymentType() === 'deploy_key' && empty($sshMatches) && $this->source) { - // If this happens, the user may have provided an HTTP URL when they needed an SSH one - // Let's try and fix that for known Git providers - $providerInfo = [ - 'host' => null, - 'user' => 'git', - 'port' => 22, - 'repository' => $this->git_repository, - ]; - - switch ($this->source->getMorphClass()) { - case \App\Models\GithubApp::class: - $providerInfo['host'] = Url::fromString($this->source->html_url)->getHost(); - $providerInfo['port'] = $this->source->custom_port; - $providerInfo['user'] = $this->source->custom_user; - break; - } - - if (! empty($providerInfo['host'])) { - $repository = ($providerInfo['port'] === 22) - ? "{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['repository']}" - : "ssh://{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['port']}/{$providerInfo['repository']}"; - } - } - - preg_match('/(?<=:)\d+(?=\/)/', $this->git_repository, $matches); - $port = 22; - if (count($matches) === 1) { - $port = $matches[0]; - $gitHost = str($this->git_repository)->before(':'); - $gitRepo = str($this->git_repository)->after('/'); - $repository = "$gitHost:$gitRepo"; - } - - return [ - 'repository' => $repository, - 'port' => $port, - ]; + return convertGitUrl($this->git_repository, $this->deploymentType(), $this->source); } public function generateBaseDir(string $uuid) diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 2f0a3ac2a..6e52dcde9 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -7,6 +7,7 @@ use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationPreview; use App\Models\EnvironmentVariable; +use App\Models\GithubApp; use App\Models\InstanceSettings; use App\Models\LocalFileVolume; use App\Models\LocalPersistentVolume; @@ -4092,3 +4093,53 @@ function defaultNginxConfiguration(): string } }'; } + +function convertGitUrl(string $gitRepository, string $deploymentType, ?GithubApp $source = null): array +{ + $repository = $gitRepository; + $providerInfo = [ + 'host' => null, + 'user' => 'git', + 'port' => 22, + 'repository' => $gitRepository, + ]; + $sshMatches = []; + $matches = []; + + // Let's try and parse the string to detect if it's a valid SSH string or not + preg_match('/((.*?)\:\/\/)?(.*@.*:.*)/', $gitRepository, $sshMatches); + + if ($deploymentType === 'deploy_key' && empty($sshMatches) && $source) { + // If this happens, the user may have provided an HTTP URL when they needed an SSH one + // Let's try and fix that for known Git providers + switch ($source->getMorphClass()) { + case \App\Models\GithubApp::class: + $providerInfo['host'] = Url::fromString($source->html_url)->getHost(); + $providerInfo['port'] = $source->custom_port; + $providerInfo['user'] = $source->custom_user; + break; + } + if (! empty($providerInfo['host'])) { + // Until we do not support more providers with App (like GithubApp), this will be always true, port will be 22 + if ($providerInfo['port'] === 22) { + $repository = "{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['repository']}"; + } else { + $repository = "ssh://{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['port']}/{$providerInfo['repository']}"; + } + } + } + + preg_match('/(?<=:)\d+(?=\/)/', $gitRepository, $matches); + + if (count($matches) === 1) { + $providerInfo['port'] = $matches[0]; + $gitHost = str($gitRepository)->before(':'); + $gitRepo = str($gitRepository)->after('/'); + $repository = "$gitHost:$gitRepo"; + } + + return [ + 'repository' => $repository, + 'port' => $providerInfo['port'], + ]; +} diff --git a/scripts/run b/scripts/run index f7e7b5264..9d3c4f1f4 100755 --- a/scripts/run +++ b/scripts/run @@ -24,7 +24,7 @@ function logs { docker exec -t coolify tail -f storage/logs/laravel.log } function test { - docker exec -t coolify php artisan test --testsuite=Feature + docker exec -t coolify php artisan test --testsuite=Feature -p } function sync:bunny { diff --git a/tests/Feature/ConvertingGitUrlsTest.php b/tests/Feature/ConvertingGitUrlsTest.php new file mode 100644 index 000000000..5bcdea1a1 --- /dev/null +++ b/tests/Feature/ConvertingGitUrlsTest.php @@ -0,0 +1,62 @@ +toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); + +}); + +test('convertGitUrlsForDeployKeyAndGithubAppAndSshUrl', function () { + $githubApp = GithubApp::find(0); + $result = convertGitUrl('git@github.com:andrasbacsai/coolify-examples.git', 'deploy_key', $githubApp); + expect($result)->toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForDeployKeyAndHttpUrl', function () { + $result = convertGitUrl('andrasbacsai/coolify-examples.git', 'deploy_key', null); + expect($result)->toBe([ + 'repository' => 'andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForDeployKeyAndSshUrl', function () { + $result = convertGitUrl('git@github.com:andrasbacsai/coolify-examples.git', 'deploy_key', null); + expect($result)->toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForSourceAndSshUrl', function () { + $result = convertGitUrl('git@github.com:andrasbacsai/coolify-examples.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForSourceAndHttpUrl', function () { + $result = convertGitUrl('andrasbacsai/coolify-examples.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForSourceAndSshUrlWithCustomPort', function () { + $result = convertGitUrl('git@git.domain.com:766/group/project.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'git@git.domain.com:group/project.git', + 'port' => '766', + ]); +}); diff --git a/tests/Feature/DockerComposeParseTest.php b/tests/Feature/DockerComposeParseTest.php index 8810280dc..d21adac8e 100644 --- a/tests/Feature/DockerComposeParseTest.php +++ b/tests/Feature/DockerComposeParseTest.php @@ -9,171 +9,171 @@ use App\Models\StandaloneDocker; use Illuminate\Support\Collection; use Symfony\Component\Yaml\Yaml; -beforeEach(function () { - $this->applicationYaml = ' -version: "3.8" -services: - app: - image: nginx - environment: - SERVICE_FQDN_APP: /app - APP_KEY: base64 - APP_DEBUG: "${APP_DEBUG:-false}" - APP_URL: $SERVICE_FQDN_APP - DB_URL: postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@db:5432/postgres?schema=public - volumes: - - "./nginx:/etc/nginx" - - "data:/var/www/html" - depends_on: - - db - db: - image: postgres - environment: - POSTGRES_USER: "${SERVICE_USER_POSTGRES}" - POSTGRES_PASSWORD: "${SERVICE_PASSWORD_POSTGRES}" - volumes: - - "dbdata:/var/lib/postgresql/data" - healthcheck: - test: - - CMD - - pg_isready - - "-U" - - "postgres" - interval: 2s - timeout: 10s - retries: 10 - depends_on: - app: - condition: service_healthy -networks: - default: - name: something - external: true - noinet: - driver: bridge - internal: true'; +// beforeEach(function () { +// $this->applicationYaml = ' +// version: "3.8" +// services: +// app: +// image: nginx +// environment: +// SERVICE_FQDN_APP: /app +// APP_KEY: base64 +// APP_DEBUG: "${APP_DEBUG:-false}" +// APP_URL: $SERVICE_FQDN_APP +// DB_URL: postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@db:5432/postgres?schema=public +// volumes: +// - "./nginx:/etc/nginx" +// - "data:/var/www/html" +// depends_on: +// - db +// db: +// image: postgres +// environment: +// POSTGRES_USER: "${SERVICE_USER_POSTGRES}" +// POSTGRES_PASSWORD: "${SERVICE_PASSWORD_POSTGRES}" +// volumes: +// - "dbdata:/var/lib/postgresql/data" +// healthcheck: +// test: +// - CMD +// - pg_isready +// - "-U" +// - "postgres" +// interval: 2s +// timeout: 10s +// retries: 10 +// depends_on: +// app: +// condition: service_healthy +// networks: +// default: +// name: something +// external: true +// noinet: +// driver: bridge +// internal: true'; - $this->applicationComposeFileString = Yaml::parse($this->applicationYaml); +// $this->applicationComposeFileString = Yaml::parse($this->applicationYaml); - $this->application = Application::create([ - 'name' => 'Application for tests', - 'docker_compose_domains' => json_encode([ - 'app' => [ - 'domain' => 'http://bcoowoookw0co4cok4sgc4k8.127.0.0.1.sslip.io', - ], - ]), - 'preview_url_template' => '{{pr_id}}.{{domain}}', - 'uuid' => 'bcoowoookw0co4cok4sgc4k8s', - 'repository_project_id' => 603035348, - 'git_repository' => 'coollabsio/coolify-examples', - 'git_branch' => 'main', - 'base_directory' => '/docker-compose-test', - 'docker_compose_location' => 'docker-compose.yml', - 'docker_compose_raw' => $this->applicationYaml, - 'build_pack' => 'dockercompose', - 'ports_exposes' => '3000', - 'environment_id' => 1, - 'destination_id' => 0, - 'destination_type' => StandaloneDocker::class, - 'source_id' => 1, - 'source_type' => GithubApp::class, - ]); - $this->application->environment_variables_preview()->where('key', 'APP_DEBUG')->update(['value' => 'true']); - $this->applicationPreview = ApplicationPreview::create([ - 'git_type' => 'github', - 'application_id' => $this->application->id, - 'pull_request_id' => 1, - 'pull_request_html_url' => 'https://github.com/coollabsio/coolify-examples/pull/1', - ]); - $this->serviceYaml = ' -services: - activepieces: - image: "ghcr.io/activepieces/activepieces:latest" - environment: - - SERVICE_FQDN_ACTIVEPIECES - - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY - - AP_URL=$SERVICE_URL_ACTIVEPIECES - - AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY - - AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js - - AP_ENVIRONMENT=prod - - AP_EXECUTION_MODE=${AP_EXECUTION_MODE} - - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES - - AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT - - AP_POSTGRES_DATABASE=activepieces - - AP_POSTGRES_HOST=postgres - - AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - - AP_POSTGRES_PORT=5432 - - AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES - - AP_REDIS_HOST=redis - - AP_REDIS_PORT=6379 - - AP_SANDBOX_RUN_TIME_SECONDS=600 - - AP_TELEMETRY_ENABLED=true - - "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates" - - AP_TRIGGER_DEFAULT_POLL_INTERVAL=5 - - AP_WEBHOOK_TIMEOUT_SECONDS=30 - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_started - healthcheck: - test: ["CMD", "curl", "-f", "http://127.0.0.1:80"] - interval: 5s - timeout: 20s - retries: 10 - postgres: - image: "nginx" - environment: - - SERVICE_FQDN_ACTIVEPIECES=/api - - POSTGRES_DB=activepieces - - PASSW=$AP_POSTGRES_PASSWORD - - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES - - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - - POSTGRES_USER=$SERVICE_USER_POSTGRES - volumes: - - "pg-data:/var/lib/postgresql/data" - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] - interval: 5s - timeout: 20s - retries: 10 - redis: - image: "redis:latest" - volumes: - - "redis_data:/data" - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 20s - retries: 10 +// $this->application = Application::create([ +// 'name' => 'Application for tests', +// 'docker_compose_domains' => json_encode([ +// 'app' => [ +// 'domain' => 'http://bcoowoookw0co4cok4sgc4k8.127.0.0.1.sslip.io', +// ], +// ]), +// 'preview_url_template' => '{{pr_id}}.{{domain}}', +// 'uuid' => 'bcoowoookw0co4cok4sgc4k8s', +// 'repository_project_id' => 603035348, +// 'git_repository' => 'coollabsio/coolify-examples', +// 'git_branch' => 'main', +// 'base_directory' => '/docker-compose-test', +// 'docker_compose_location' => 'docker-compose.yml', +// 'docker_compose_raw' => $this->applicationYaml, +// 'build_pack' => 'dockercompose', +// 'ports_exposes' => '3000', +// 'environment_id' => 1, +// 'destination_id' => 0, +// 'destination_type' => StandaloneDocker::class, +// 'source_id' => 1, +// 'source_type' => GithubApp::class, +// ]); +// $this->application->environment_variables_preview()->where('key', 'APP_DEBUG')->update(['value' => 'true']); +// $this->applicationPreview = ApplicationPreview::create([ +// 'git_type' => 'github', +// 'application_id' => $this->application->id, +// 'pull_request_id' => 1, +// 'pull_request_html_url' => 'https://github.com/coollabsio/coolify-examples/pull/1', +// ]); +// $this->serviceYaml = ' +// services: +// activepieces: +// image: "ghcr.io/activepieces/activepieces:latest" +// environment: +// - SERVICE_FQDN_ACTIVEPIECES +// - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY +// - AP_URL=$SERVICE_URL_ACTIVEPIECES +// - AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY +// - AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js +// - AP_ENVIRONMENT=prod +// - AP_EXECUTION_MODE=${AP_EXECUTION_MODE} +// - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES +// - AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT +// - AP_POSTGRES_DATABASE=activepieces +// - AP_POSTGRES_HOST=postgres +// - AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES +// - AP_POSTGRES_PORT=5432 +// - AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES +// - AP_REDIS_HOST=redis +// - AP_REDIS_PORT=6379 +// - AP_SANDBOX_RUN_TIME_SECONDS=600 +// - AP_TELEMETRY_ENABLED=true +// - "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates" +// - AP_TRIGGER_DEFAULT_POLL_INTERVAL=5 +// - AP_WEBHOOK_TIMEOUT_SECONDS=30 +// depends_on: +// postgres: +// condition: service_healthy +// redis: +// condition: service_started +// healthcheck: +// test: ["CMD", "curl", "-f", "http://127.0.0.1:80"] +// interval: 5s +// timeout: 20s +// retries: 10 +// postgres: +// image: "nginx" +// environment: +// - SERVICE_FQDN_ACTIVEPIECES=/api +// - POSTGRES_DB=activepieces +// - PASSW=$AP_POSTGRES_PASSWORD +// - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES +// - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES +// - POSTGRES_USER=$SERVICE_USER_POSTGRES +// volumes: +// - "pg-data:/var/lib/postgresql/data" +// healthcheck: +// test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] +// interval: 5s +// timeout: 20s +// retries: 10 +// redis: +// image: "redis:latest" +// volumes: +// - "redis_data:/data" +// healthcheck: +// test: ["CMD", "redis-cli", "ping"] +// interval: 5s +// timeout: 20s +// retries: 10 -'; +// '; - $this->serviceComposeFileString = Yaml::parse($this->serviceYaml); +// $this->serviceComposeFileString = Yaml::parse($this->serviceYaml); - $this->service = Service::create([ - 'name' => 'Service for tests', - 'uuid' => 'tgwcg8w4s844wkog8kskw44g', - 'docker_compose_raw' => $this->serviceYaml, - 'environment_id' => 1, - 'server_id' => 0, - 'destination_id' => 0, - 'destination_type' => StandaloneDocker::class, - ]); -}); +// $this->service = Service::create([ +// 'name' => 'Service for tests', +// 'uuid' => 'tgwcg8w4s844wkog8kskw44g', +// 'docker_compose_raw' => $this->serviceYaml, +// 'environment_id' => 1, +// 'server_id' => 0, +// 'destination_id' => 0, +// 'destination_type' => StandaloneDocker::class, +// ]); +// }); -afterEach(function () { - // $this->applicationPreview->forceDelete(); - $this->application->forceDelete(); - DeleteResourceJob::dispatchSync($this->service); - $this->service->forceDelete(); -}); +// afterEach(function () { +// // $this->applicationPreview->forceDelete(); +// $this->application->forceDelete(); +// DeleteResourceJob::dispatchSync($this->service); +// $this->service->forceDelete(); +// }); -test('ServiceComposeParseNew', function () { - $output = newParser($this->service); - $this->service->saveComposeConfigs(); - expect($output)->toBeInstanceOf(Collection::class); -}); +// test('ServiceComposeParseNew', function () { +// $output = newParser($this->service); +// $this->service->saveComposeConfigs(); +// expect($output)->toBeInstanceOf(Collection::class); +// }); // test('ApplicationComposeParse', function () { // expect($this->jsonapplicationComposeFile)->toBeJson()->ray(); From ff016cb2a26f6c09a112460a27676fc862aa922e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 11:50:02 +0100 Subject: [PATCH 20/38] pint --- app/Models/Application.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Models/Application.php b/app/Models/Application.php index dd7c446b5..c284528f1 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -944,6 +944,7 @@ class Application extends BaseModel try { ['commands' => $lsRemoteCommand] = $this->generateGitLsRemoteCommands(deployment_uuid: $deployment_uuid, exec_in_docker: false); instant_remote_process([$lsRemoteCommand], $this->destination->server, true); + return [ 'is_accessible' => true, 'error' => null, @@ -961,7 +962,7 @@ class Application extends BaseModel $branch = $this->git_branch; ['repository' => $customRepository, 'port' => $customPort] = $this->customRepository(); $commands = collect([]); - $base_command = "git ls-remote"; + $base_command = 'git ls-remote'; if ($this->deploymentType() === 'source') { $source_html_url = data_get($this, 'source.html_url'); From e00c692e33ccad8a51a3f740a6d820fb13a81972 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 12 Nov 2024 12:14:23 +0100 Subject: [PATCH 21/38] ui: separate resources by type in projects view --- .../livewire/project/resource/index.blade.php | 113 ++++++++++++++++-- 1 file changed, 104 insertions(+), 9 deletions(-) diff --git a/resources/views/livewire/project/resource/index.blade.php b/resources/views/livewire/project/resource/index.blade.php index 0e16b7266..a618e6db2 100644 --- a/resources/views/livewire/project/resource/index.blade.php +++ b/resources/views/livewire/project/resource/index.blade.php @@ -44,17 +44,108 @@
@if ($environment->isEmpty()) -
+ Add New Resource @else
-
- + -