diff --git a/app/Livewire/Source/Gitlab/Change.php b/app/Livewire/Source/Gitlab/Change.php index 5a386b429..bab6a41d1 100644 --- a/app/Livewire/Source/Gitlab/Change.php +++ b/app/Livewire/Source/Gitlab/Change.php @@ -17,6 +17,10 @@ class Change extends Component public string $webhook_endpoint = ''; + public string $custom_webhook_endpoint = ''; + + public bool $use_custom_webhook_endpoint = false; + public ?string $ipv4 = null; public ?string $ipv6 = null; @@ -75,6 +79,9 @@ class Change extends Component 'groupName' => 'nullable|string', 'isSystemWide' => 'required|bool', 'privateKeyId' => 'nullable|int', + 'webhook_endpoint' => ['required', 'string', 'url'], + 'custom_webhook_endpoint' => ['nullable', 'string', 'url'], + 'use_custom_webhook_endpoint' => ['required', 'bool'], ]; } @@ -91,6 +98,40 @@ class Change extends Component } } + public function updatedWebhookEndpoint(): void + { + $this->persistRedirectUriFromEndpoint(); + } + + public function updatedUseCustomWebhookEndpoint(): void + { + $this->persistRedirectUriFromEndpoint(); + } + + public function updatedCustomWebhookEndpoint(): void + { + $this->persistRedirectUriFromEndpoint(); + } + + private function persistRedirectUriFromEndpoint(): void + { + $this->refreshRedirectUri(); + + if (! $this->gitlab_app || blank($this->redirectUri)) { + return; + } + + try { + $this->authorize('update', $this->gitlab_app); + if ($this->gitlab_app->redirect_uri !== $this->redirectUri) { + $this->gitlab_app->redirect_uri = $this->redirectUri; + $this->gitlab_app->save(); + } + } catch (\Throwable) { + // Keep the live redirect URI even if the user cannot persist yet. + } + } + public function mount() { try { @@ -124,7 +165,25 @@ class Change extends Component $this->webhook_endpoint = $this->fqdn ?? $this->ipv4 ?? $this->ipv6 ?? config('app.url') ?? ''; } - $this->redirectUri = $this->webhook_endpoint.'/webhooks/source/gitlab/redirect'; + // Prefer a previously saved redirect base when it matches one of the selectable endpoints + // or when it differs (restore custom mode for self-hosted / tunnel setups). + $savedRedirect = $this->gitlab_app->redirect_uri; + if (filled($savedRedirect)) { + $savedBase = rtrim(str($savedRedirect)->before('/webhooks/source/gitlab/redirect')->toString(), '/'); + $known = collect([$this->fqdn, $this->ipv4, $this->ipv6, config('app.url')]) + ->filter() + ->map(fn ($url) => rtrim((string) $url, '/')); + + if ($known->contains($savedBase)) { + $this->webhook_endpoint = $savedBase; + $this->use_custom_webhook_endpoint = false; + } elseif (! (isCloud() && ! isDev()) && filled($savedBase)) { + $this->use_custom_webhook_endpoint = true; + $this->custom_webhook_endpoint = $savedBase; + } + } + + $this->refreshRedirectUri(); $this->oauthState = $this->createOAuthState(); } catch (\Throwable $e) { @@ -132,6 +191,23 @@ class Change extends Component } } + public function refreshRedirectUri(): void + { + $base = $this->resolvePublicBaseUrl(); + $this->redirectUri = $base === '' + ? '' + : $base.'/webhooks/source/gitlab/redirect'; + } + + public function resolvePublicBaseUrl(): string + { + if ($this->use_custom_webhook_endpoint && filled($this->custom_webhook_endpoint)) { + return rtrim($this->custom_webhook_endpoint, '/'); + } + + return rtrim($this->webhook_endpoint ?: (config('app.url') ?? ''), '/'); + } + public static function oauthStateCacheKey(string $state): string { return 'gitlab-app-oauth-state:'.hash('sha256', $state); @@ -165,6 +241,7 @@ class Change extends Component $this->gitlab_app->group_name = $this->groupName; $this->gitlab_app->is_system_wide = $this->isSystemWide; $this->gitlab_app->private_key_id = $this->privateKeyId; + $this->refreshRedirectUri(); $this->gitlab_app->redirect_uri = $this->redirectUri; } else { $this->name = $this->gitlab_app->name; @@ -280,6 +357,7 @@ class Change extends Component public function getOAuthUrl(): string { + $this->refreshRedirectUri(); $baseUrl = rtrim($this->htmlUrl, '/'); $query = http_build_query([ diff --git a/resources/views/livewire/source/gitlab/change.blade.php b/resources/views/livewire/source/gitlab/change.blade.php index 5cb859ec5..cd219f7d3 100644 --- a/resources/views/livewire/source/gitlab/change.blade.php +++ b/resources/views/livewire/source/gitlab/change.blade.php @@ -98,7 +98,7 @@ (Settings > Webhooks): + value="{{ rtrim($this->resolvePublicBaseUrl(), '/') }}/webhooks/source/gitlab/events" /> @@ -148,7 +148,51 @@ You must complete this step before you can use this source! -
+
+ @if (!isCloud() || isDev()) +
+

Public endpoint

+
+ GitLab will redirect back to this Coolify URL. It must match the Callback URL on your GitLab OAuth Application exactly. +
+ +
+ + @if ($fqdn) + + @endif + @if ($ipv4) + + @endif + @if ($ipv6) + + @endif + @if (config('app.url')) + + @endif + +
+
+ +
+
+ @endif +

Step 1: Create an OAuth Application on GitLab

Go to your GitLab instance and create a new OAuth Application:

@@ -158,7 +202,9 @@
    -
  • Set Redirect URI to: {{ $redirectUri }}
  • +
  • Set Redirect URI to: + {{ $redirectUri }} +
  • Enable scopes: api, read_user, read_repository
  • Uncheck Confidential if you run into issues
@@ -221,8 +267,8 @@ @if ($clientId)

Step 3: Authorize with GitLab

-
Click the button below to authorize Coolify with your GitLab instance.
- +
Click the button below to authorize Coolify with your GitLab instance. The redirect URI must match the Callback URL configured in GitLab.
+
Connect to GitLab diff --git a/tests/Feature/GitlabSourceChangeViewTest.php b/tests/Feature/GitlabSourceChangeViewTest.php index a5e43a1cc..78fb3bf50 100644 --- a/tests/Feature/GitlabSourceChangeViewTest.php +++ b/tests/Feature/GitlabSourceChangeViewTest.php @@ -73,4 +73,17 @@ describe('GitLab source setup view', function () { ->assertSet('clientId', 'gitlab-app-id') ->assertSet('clientSecretInput', 'super-secret-value'); }); + + test('supports github-style custom public endpoint for oauth redirect uri', function () { + Livewire::withQueryParams(['gitlab_app_uuid' => $this->gitlabApp->uuid]) + ->test(Change::class) + ->assertSee('Use custom webhook endpoint') + ->assertSee('Selected endpoint') + ->set('use_custom_webhook_endpoint', true) + ->set('custom_webhook_endpoint', 'http://100.75.155.70:8000') + ->assertSet('redirectUri', 'http://100.75.155.70:8000/webhooks/source/gitlab/redirect'); + + expect($this->gitlabApp->refresh()->redirect_uri) + ->toBe('http://100.75.155.70:8000/webhooks/source/gitlab/redirect'); + }); });