feat(ui): Shadow UI redesign, domain management, and DNS autoconfigure (#11119)

Co-authored-by: ShadowArcanist <162910371+ShadowArcanist@users.noreply.github.com>
This commit is contained in:
Andras BacsaiandShadowArcanist authored and GitHub committed 2026-08-03 23:11:54 +02:00
1 parent 0b843bb07c
commit 8ae56587d5
497 files changed
+44045 -22630

No files matched your search

@@ -1,274 +1,317 @@
<div>
<x-slot:title>
{{ $gitlab_app->name ?: 'GitLab App' }} | Sources | Coolify
</x-slot>
@if ($isConnected)
<form wire:submit='submit'>
<div class="flex flex-col sm:flex-row sm:items-center gap-2">
<div class="flex items-center gap-2">
<h1>GitLab App</h1>
<x-status-badge status="Connected" type="success" />
</div>
<div class="flex gap-2">
<x-forms.button canGate="update" :canResource="$gitlab_app" type="submit">Save</x-forms.button>
<x-forms.button wire:click.prevent="testConnection">Test Connection</x-forms.button>
@if ($gitlab_app)
@can('delete', $gitlab_app)
<x-modal-confirmation title="Confirm GitLab App Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['The selected GitLab App will be permanently deleted.']"
confirmationText="{{ data_get($gitlab_app, 'name') }}"
confirmationLabel="Please confirm by entering the GitLab App Name below"
shortConfirmationLabel="GitLab App Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@endcan
@endif
<header class="mb-5 flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div class="min-w-0">
<div class="flex flex-wrap items-center gap-2">
<h1 class="truncate text-[24px]! leading-7! font-semibold! tracking-tight!">
{{ $name ?: 'GitLab App' }}
</h1>
<x-status-badge label="Connected" type="success" />
</div>
<p class="mt-1 text-[13px] text-neutral-500 dark:text-fg-dim">
{{ filled($groupName) ? 'GitLab App for '.$groupName : 'Private GitLab source' }}
</p>
</div>
<div class="subtitle">Your GitLab App for private repositories.</div>
<div class="flex flex-col gap-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="name" label="Name" />
@if (!isCloud())
<div class="w-48">
<x-forms.checkbox canGate="update" :canResource="$gitlab_app" label="System Wide"
helper="If checked, this GitLab App will be available for everyone in this Coolify instance."
instantSave id="isSystemWide" />
</div>
@if ($isSystemWide)
<x-callout type="warning" title="Not Recommended">
System-wide GitLab Apps are shared across all teams on this Coolify instance. This means any team
can use this GitLab App to deploy applications from your repositories. For better security and
isolation, it's recommended to create team-specific GitLab Apps instead.
</x-callout>
@endif
@endif
<div class="flex shrink-0 flex-wrap items-center gap-2 sm:ml-auto">
@can('view', $gitlab_app)
<x-forms.button type="button" wire:click.prevent="testConnection">
Test connection
</x-forms.button>
@endcan
@can('delete', $gitlab_app)
<x-modal-confirmation title="Confirm GitLab App Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['The selected GitLab App will be permanently deleted.']"
confirmationText="{{ data_get($gitlab_app, 'name') }}"
confirmationLabel="Please confirm by entering the GitLab App Name below"
shortConfirmationLabel="GitLab App Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@endcan
</div>
</header>
<h3 class="pt-4">OAuth Credentials</h3>
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientId" label="Application ID" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientSecretInput" label="Application Secret" type="password"
helper="Stored encrypted. Leave empty and save other fields without changing the existing secret." />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="groupName" label="Group Name"
helper="Comma-separated group names to filter visible repositories." />
<form wire:submit="submit" class="application-settings-form">
<x-unsaved-bar action="submit" />
<div x-data="{
activeAccordion: '',
setActiveAccordion(id) {
this.activeAccordion = (this.activeAccordion == id) ? '' : id
}
}" class="relative w-full py-2 mx-auto overflow-hidden text-sm font-normal rounded-md">
<div x-data="{ id: $id('accordion') }" class="cursor-pointer">
<button @click="setActiveAccordion(id)"
class="flex items-center justify-between w-full px-1 py-2 text-left select-none dark:hover:text-white hover:bg-white/5"
type="button">
<h4>Advanced / Self-hosted</h4>
<svg class="w-4 h-4 duration-200 ease-out" :class="{ 'rotate-180': activeAccordion == id }"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div x-show="activeAccordion==id" x-collapse x-cloak class="px-2">
<div class="flex flex-col gap-2 pt-0 opacity-70">
<div class="flex gap-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="htmlUrl" label="GitLab URL" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="apiUrl" label="API URL" />
</div>
<div class="flex gap-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="customUser" label="SSH User" />
<x-forms.input canGate="update" :canResource="$gitlab_app" type="number" id="customPort" label="SSH Port" />
</div>
<div class="flex gap-2">
<x-forms.select canGate="update" :canResource="$gitlab_app" id="privateKeyId" label="SSH Private Key (optional)">
<option value="">None</option>
@foreach ($privateKeys as $key)
<option value="{{ $key->id }}">{{ $key->name }}</option>
@endforeach
</x-forms.select>
</div>
</div>
<x-application.settings-section title="General"
description="Connection and authentication settings for this private GitLab source.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="name" label="Name" />
@if (! isCloud())
<div class="lg:col-span-2 max-w-xs">
<x-forms.checkbox canGate="update" :canResource="$gitlab_app" label="System wide"
helper="If checked, this GitLab App will be available for everyone in this Coolify instance."
instantSave id="isSystemWide" />
</div>
@if ($isSystemWide)
<div class="lg:col-span-2">
<x-callout type="warning" title="Shared with every team">
System-wide GitLab Apps are shared across all teams on this Coolify instance. Prefer team-specific apps when you need repository isolation.
</x-callout>
</div>
@endif
@endif
</div>
</x-application.settings-section>
<x-application.settings-section title="OAuth credentials"
description="Application credentials from your GitLab OAuth application.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientId"
label="Application ID" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="clientSecretInput"
label="Application secret" type="password"
helper="Stored encrypted. Leave empty to keep the existing secret." />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="groupName" label="Group name"
helper="Comma-separated group names to filter visible repositories." />
</div>
</x-application.settings-section>
<x-application.settings-section title="Self-hosted / advanced"
description="Override endpoints and SSH settings for self-hosted GitLab.">
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input canGate="update" :canResource="$gitlab_app" id="htmlUrl"
label="GitLab URL" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="apiUrl" label="API URL" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="customUser"
label="SSH user" />
<x-forms.input canGate="update" :canResource="$gitlab_app" type="number" id="customPort"
label="SSH port" />
<div class="lg:col-span-2">
<x-forms.select canGate="update" :canResource="$gitlab_app" id="privateKeyId"
label="SSH private key (optional)">
<option value="">None</option>
@foreach ($privateKeys as $key)
<option value="{{ $key->id }}">{{ $key->name }}</option>
@endforeach
</x-forms.select>
</div>
</div>
</x-application.settings-section>
<h3 class="pt-4">Webhook</h3>
<div class="flex flex-col gap-1">
<div class="text-sm">
Configure this webhook URL in your GitLab project settings
(<code>Settings &gt; Webhooks</code>):
<x-application.settings-section title="Webhook"
description="Configure this webhook URL in your GitLab project settings (Settings → Webhooks).">
<div class="grid gap-4 lg:grid-cols-2">
<div class="lg:col-span-2">
<x-forms.input readonly label="Webhook URL"
value="{{ rtrim($this->resolvePublicBaseUrl(), '/') }}/webhooks/source/gitlab/events" />
</div>
<x-forms.input readonly label="Webhook URL"
value="{{ rtrim($this->resolvePublicBaseUrl(), '/') }}/webhooks/source/gitlab/events" />
<x-forms.input canGate="update" :canResource="$gitlab_app" id="webhookToken" label="Webhook Secret Token" type="password"
<x-forms.input canGate="update" :canResource="$gitlab_app" id="webhookToken"
label="Webhook secret token" type="password"
helper="Set this same token in your GitLab webhook's Secret token field. Stored encrypted." />
</div>
</x-application.settings-section>
</form>
@if ($applications->count() > 0)
<h3 class="pt-4">Applications Using This Source</h3>
<div class="flex flex-col gap-2">
@foreach ($applications as $application)
<a class="coolbox group"
<div x-data="{ search: '' }" class="application-settings-form mt-6">
<x-application.settings-section title="Resources"
description="Applications currently using this GitLab App." flush>
@if ($applications->isEmpty())
<x-empty title="No resources use this source"
description="Applications will appear here after this GitLab App is selected as their source."
icon-name="sources" size="sm" />
@else
<div class="border-b border-neutral-200 p-3 dark:border-white/[0.08]">
<div class="relative w-full max-w-sm">
<x-reicon name="search"
class="pointer-events-none absolute top-1/2 left-2.5 z-10 size-3.5 -translate-y-1/2 text-neutral-400 dark:text-fg-faint" />
<input x-model.debounce.150ms="search" type="search" placeholder="Search resources"
class="h-8! w-full rounded-lg! border-neutral-200! bg-white! py-0! pr-3! pl-8! text-[12px]! shadow-none! placeholder:text-neutral-400 focus:border-accent! focus:ring-0! dark:border-white/[0.08]! dark:bg-white/[0.035]! dark:text-fg! dark:placeholder:text-fg-faint">
</div>
</div>
<div class="overflow-x-auto">
<div
class="grid min-w-[680px] grid-cols-[minmax(10rem,.8fr)_minmax(10rem,.8fr)_minmax(12rem,1fr)_8rem] border-b border-neutral-200 bg-neutral-50 px-4 py-2.5 text-[11px] font-medium text-neutral-500 dark:border-white/[0.08] dark:bg-white/[0.025] dark:text-fg-faint">
<div>Project</div>
<div>Environment</div>
<div>Resource</div>
<div>Repository</div>
</div>
@foreach ($applications->sortBy('name', SORT_NATURAL) as $application)
@php
$projectName = (string) data_get($application, 'environment.project.name');
$environmentName = (string) data_get($application, 'environment.name');
$resourceName = (string) $application->name;
$repoLabel = $application->git_repository.':'.$application->git_branch;
$searchValue = strtolower(
$projectName.' '.$environmentName.' '.$resourceName.' '.$repoLabel,
);
@endphp
<a {{ wireNavigate() }}
href="{{ route('project.application.configuration', [
'project_uuid' => data_get($application, 'environment.project.uuid'),
'environment_uuid' => data_get($application, 'environment.uuid'),
'application_uuid' => data_get($application, 'uuid'),
]) }}">
<div class="text-left dark:group-hover:text-white flex flex-col justify-center mx-6">
<div class="box-title">{{ $application->name }}</div>
<div class="box-description">{{ $application->git_repository }}:{{ $application->git_branch }}</div>
</div>
]) }}"
x-show="search === '' || '{{ addslashes($searchValue) }}'.includes(search.toLowerCase())"
class="grid min-h-13 min-w-[680px] grid-cols-[minmax(10rem,.8fr)_minmax(10rem,.8fr)_minmax(12rem,1fr)_8rem] items-center border-b border-neutral-200 px-4 py-2.5 text-[12px] transition-colors last:border-b-0 hover:bg-neutral-50 hover:no-underline dark:border-white/[0.07] dark:hover:bg-white/[0.025]">
<span class="truncate text-neutral-500 dark:text-fg-dim">{{ $projectName }}</span>
<span class="truncate text-neutral-500 dark:text-fg-dim">{{ $environmentName }}</span>
<span class="truncate font-medium text-black dark:text-fg">{{ $resourceName }}</span>
<span class="truncate text-neutral-500 dark:text-fg-dim">{{ $repoLabel }}</span>
</a>
@endforeach
</div>
@endif
</div>
</form>
</x-application.settings-section>
</div>
@else
<div class="flex flex-col sm:flex-row sm:items-center gap-2">
<h1>GitLab App</h1>
<div class="flex gap-2">
@if ($gitlab_app)
@can('delete', $gitlab_app)
<x-modal-confirmation title="Confirm GitLab App Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['The selected GitLab App will be permanently deleted.']"
confirmationText="{{ data_get($gitlab_app, 'name') }}"
confirmationLabel="Please confirm by entering the GitLab App Name below"
shortConfirmationLabel="GitLab App Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@endcan
@endif
<header class="mb-8 flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div class="min-w-0">
<h1 class="truncate text-[24px]! leading-7! font-semibold! tracking-tight!">
{{ $name ?: 'GitLab App' }}
</h1>
<p class="mt-1 text-[13px] text-neutral-500 dark:text-fg-dim">
Finish connecting this GitLab App before using it as a source
</p>
</div>
</div>
<div class="subtitle">Connect your GitLab instance to deploy private repositories.</div>
<div class="mb-6 rounded-sm alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span>You must complete this step before you can use this source!</span>
</div>
<div class="flex flex-col gap-4" x-data="{
webhookEndpoint: $wire.entangle('webhook_endpoint').live,
useCustomWebhookEndpoint: $wire.entangle('use_custom_webhook_endpoint').live,
customWebhookEndpoint: $wire.entangle('custom_webhook_endpoint').live,
redirectPath: '/webhooks/source/gitlab/redirect',
get redirectUri() {
const base = (this.useCustomWebhookEndpoint ? this.customWebhookEndpoint : this.webhookEndpoint) || '';
return base ? base.replace(/\/+$/, '') + this.redirectPath : '';
}
}">
<h3>Step 1: Create an OAuth Application on GitLab</h3>
<div class="text-sm flex flex-col gap-1">
<p>Go to your GitLab instance and create a new OAuth Application:</p>
<a href="{{ rtrim($htmlUrl, '/') }}/-/profile/applications" target="_blank"
class="inline-flex items-center gap-1 text-sm underline">
{{ rtrim($htmlUrl, '/') }}/-/profile/applications
<x-external-link />
</a>
<ul class="list-disc list-inside mt-2 space-y-1">
<li>Set <strong>Redirect URI</strong> to:
<code x-text="redirectUri || @js($redirectUri)">{{ $redirectUri }}</code>
</li>
<li>Enable scopes: <code>api</code>, <code>read_user</code>, <code>read_repository</code></li>
<li>Uncheck <strong>Confidential</strong> if you run into issues</li>
</ul>
</div>
<form wire:submit='submit' class="flex flex-col gap-2">
<div class="flex flex-col sm:flex-row sm:items-center gap-2 pt-2">
<h3>Step 2: Enter the credentials</h3>
<x-forms.button type="submit">Save</x-forms.button>
@can('delete', $gitlab_app)
<div class="shrink-0 sm:ml-auto">
<x-modal-confirmation title="Confirm GitLab App Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['The selected GitLab App will be permanently deleted.']"
confirmationText="{{ data_get($gitlab_app, 'name') }}"
confirmationLabel="Please confirm by entering the GitLab App Name below"
shortConfirmationLabel="GitLab App Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
</div>
<x-forms.input id="name" label="Name" />
<x-forms.input id="clientId" label="Application ID" required
helper="The Application ID from your GitLab OAuth Application." />
<x-forms.input id="clientSecretInput" label="Application Secret" type="password"
:required="blank($clientSecretInput) && blank(data_get($gitlab_app, 'client_secret'))"
helper="The Secret from your GitLab OAuth Application. Saved encrypted and shown again after reload." />
<x-forms.input id="groupName" label="Group Name"
helper="Optional. Comma-separated group names to filter repositories." />
@endcan
</header>
@if (!isCloud() || isDev())
<div class="flex flex-col gap-3 pt-2">
<div class="text-sm dark:text-neutral-400">
GitLab will redirect back to this Coolify URL. It must match the Callback URL on your GitLab OAuth Application exactly.
</div>
<x-forms.checkbox x-model="useCustomWebhookEndpoint" id="use_custom_webhook_endpoint"
label="Use custom webhook endpoint"
helper="Enable this when the public URL GitLab should call differs from Coolify's configured URL, for example behind Cloudflare Tunnel or when accessing via a LAN IP." />
<div x-show="!useCustomWebhookEndpoint">
<x-forms.select wire:model.live='webhook_endpoint' x-model="webhookEndpoint"
label="Selected endpoint"
helper="GitLab will use this endpoint unless custom mode is enabled.">
@if ($fqdn)
<option value="{{ $fqdn }}">Use {{ $fqdn }}</option>
@endif
@if ($ipv4)
<option value="{{ $ipv4 }}">Use {{ $ipv4 }}</option>
@endif
@if ($ipv6)
<option value="{{ $ipv6 }}">Use {{ $ipv6 }}</option>
@endif
@if (config('app.url'))
<option value="{{ config('app.url') }}">Use {{ config('app.url') }}</option>
@endif
</x-forms.select>
</div>
<div x-cloak x-show="useCustomWebhookEndpoint">
<x-forms.input x-model="customWebhookEndpoint" id="custom_webhook_endpoint" type="url"
label="Custom endpoint" placeholder="https://coolify.example.com"
helper="GitLab will use this custom public URL. Do not include /webhooks." />
</div>
<div class="application-settings-form flex flex-col gap-6"
x-data="{
webhookEndpoint: $wire.entangle('webhook_endpoint').live,
useCustomWebhookEndpoint: $wire.entangle('use_custom_webhook_endpoint').live,
customWebhookEndpoint: $wire.entangle('custom_webhook_endpoint').live,
redirectPath: '/webhooks/source/gitlab/redirect',
get redirectUri() {
const base = (this.useCustomWebhookEndpoint ? this.customWebhookEndpoint : this.webhookEndpoint) || '';
return base ? base.replace(/\/+$/, '') + this.redirectPath : '';
}
}">
<x-application.settings-section title="Step 1 · Create an OAuth application"
description="Register a new OAuth application in your GitLab instance with the redirect URI below.">
<div class="flex flex-col gap-3 text-[12px] leading-5 text-neutral-600 dark:text-fg-dim">
<a href="{{ rtrim($htmlUrl, '/') }}/-/profile/applications" target="_blank"
class="inline-flex w-fit items-center gap-1 font-medium text-black underline-offset-2 hover:underline dark:text-fg">
{{ rtrim($htmlUrl, '/') }}/-/profile/applications
<x-external-link />
</a>
<ul class="list-inside list-disc space-y-1.5">
<li>
Set <strong class="text-black dark:text-fg">Redirect URI</strong> to:
<code class="rounded bg-neutral-100 px-1.5 py-0.5 text-[11px] dark:bg-white/[0.06]"
x-text="redirectUri || @js($redirectUri)">{{ $redirectUri }}</code>
</li>
<li>
Enable scopes: <code class="rounded bg-neutral-100 px-1.5 py-0.5 text-[11px] dark:bg-white/[0.06]">api</code>,
<code class="rounded bg-neutral-100 px-1.5 py-0.5 text-[11px] dark:bg-white/[0.06]">read_user</code>,
<code class="rounded bg-neutral-100 px-1.5 py-0.5 text-[11px] dark:bg-white/[0.06]">read_repository</code>
</li>
<li>Uncheck <strong class="text-black dark:text-fg">Confidential</strong> if you run into issues</li>
</ul>
</div>
</x-application.settings-section>
<form wire:submit="submit" class="contents">
<x-application.settings-section title="Step 2 · Enter credentials"
description="Paste the Application ID and secret from GitLab, then save.">
<x-slot:actions>
<x-forms.button type="submit">Save</x-forms.button>
</x-slot:actions>
<div class="grid gap-4 lg:grid-cols-2">
<x-forms.input id="name" label="Name" />
<x-forms.input id="clientId" label="Application ID" required
helper="The Application ID from your GitLab OAuth Application." />
<x-forms.input id="clientSecretInput" label="Application secret" type="password"
:required="blank($clientSecretInput) && blank(data_get($gitlab_app, 'client_secret'))"
helper="The Secret from your GitLab OAuth Application. Saved encrypted." />
<x-forms.input id="groupName" label="Group name"
helper="Optional. Comma-separated group names to filter repositories." />
</div>
@endif
<div x-data="{
activeAccordion: '',
setActiveAccordion(id) {
this.activeAccordion = (this.activeAccordion == id) ? '' : id
}
}" class="relative w-full py-2 mx-auto overflow-hidden text-sm font-normal rounded-md">
<div x-data="{ id: $id('accordion') }" class="cursor-pointer">
<button @click="setActiveAccordion(id)"
class="flex items-center justify-between w-full px-1 py-2 text-left select-none dark:hover:text-white hover:bg-white/5"
type="button">
<h4>Advanced / Self-hosted</h4>
<svg class="w-4 h-4 duration-200 ease-out" :class="{ 'rotate-180': activeAccordion == id }"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@if (! isCloud() || isDev())
<div class="mt-4 grid gap-4 lg:grid-cols-2">
<div class="lg:col-span-2 text-[12px] text-neutral-500 dark:text-fg-dim">
GitLab will redirect back to this Coolify URL. It must match the Callback URL on your GitLab OAuth Application exactly.
</div>
<div class="lg:col-span-2 max-w-md">
<x-forms.listbox id="use_custom_webhook_endpoint" label="Webhook endpoint"
:live="true" :options="[
['value' => false, 'label' => 'Use an instance endpoint'],
['value' => true, 'label' => 'Use a custom endpoint'],
]"
x-model="useCustomWebhookEndpoint"
helper="Use a custom public URL when Coolify is behind a tunnel or reverse proxy." />
</div>
<div class="lg:col-span-2" x-show="!useCustomWebhookEndpoint">
<x-forms.select wire:model.live="webhook_endpoint" x-model="webhookEndpoint"
label="Selected endpoint"
helper="GitLab will use this endpoint unless custom mode is enabled.">
@if ($fqdn)
<option value="{{ $fqdn }}">Use {{ $fqdn }}</option>
@endif
@if ($ipv4)
<option value="{{ $ipv4 }}">Use {{ $ipv4 }}</option>
@endif
@if ($ipv6)
<option value="{{ $ipv6 }}">Use {{ $ipv6 }}</option>
@endif
@if (config('app.url'))
<option value="{{ config('app.url') }}">Use {{ config('app.url') }}</option>
@endif
</x-forms.select>
</div>
<div class="lg:col-span-2" x-cloak x-show="useCustomWebhookEndpoint">
<x-forms.input x-model="customWebhookEndpoint" id="custom_webhook_endpoint"
type="url" label="Custom endpoint"
placeholder="https://coolify.example.com"
helper="GitLab will use this custom public URL. Do not include /webhooks." />
</div>
</div>
@endif
<div class="mt-4" x-data="{ open: false }">
<button type="button" @click="open = !open"
class="flex w-full items-center justify-between rounded-lg border border-neutral-200 px-3 py-2.5 text-left text-[12px] font-medium text-neutral-700 transition-colors hover:bg-neutral-50 dark:border-white/[0.08] dark:text-fg-dim dark:hover:bg-white/[0.03]">
Advanced / self-hosted
<svg class="size-3.5 transition-transform" :class="{ 'rotate-180': open }"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div x-show="activeAccordion==id" x-collapse x-cloak class="px-2">
<div class="flex flex-col gap-2 pt-0 opacity-70">
<div class="flex gap-2">
<x-forms.input id="htmlUrl" label="GitLab URL"
helper="Only change this for self-hosted GitLab (e.g. https://gitlab.example.com)." />
<x-forms.input id="apiUrl" label="API URL"
helper="Usually your GitLab URL with /api/v4 appended." />
<div x-cloak x-show="open" x-collapse
class="mt-3 grid gap-4 rounded-lg border border-neutral-200 p-3 lg:grid-cols-2 dark:border-white/[0.08]">
<x-forms.input id="htmlUrl" label="GitLab URL"
helper="Only change this for self-hosted GitLab (e.g. https://gitlab.example.com)." />
<x-forms.input id="apiUrl" label="API URL"
helper="Usually your GitLab URL with /api/v4 appended." />
<x-forms.input id="customUser" label="SSH user" />
<x-forms.input type="number" id="customPort" label="SSH port" />
@if (! isCloud())
<div class="max-w-xs lg:col-span-2">
<x-forms.checkbox label="System wide" id="isSystemWide"
helper="If checked, this GitLab App will be available for everyone in this Coolify instance." />
</div>
<div class="flex gap-2">
<x-forms.input id="customUser" label="SSH User" />
<x-forms.input type="number" id="customPort" label="SSH Port" />
</div>
@if (!isCloud())
<div class="w-48">
<x-forms.checkbox label="System Wide" id="isSystemWide"
helper="If checked, this GitLab App will be available for everyone in this Coolify instance." />
</div>
@endif
</div>
@endif
</div>
</div>
</div>
</x-application.settings-section>
</form>
@if ($clientId)
<h3 class="pt-2">Step 3: Authorize with GitLab</h3>
<div class="text-sm">Click the button below to authorize Coolify with your GitLab instance. The redirect URI must match the Callback URL configured in GitLab.</div>
<a href="{{ $this->getOAuthUrl() }}" class="w-fit" wire:key="oauth-url-{{ md5((string) $redirectUri) }}">
<x-forms.button class="mt-2">
<x-application.settings-section title="Step 3 · Authorize with GitLab"
description="Authorize Coolify with your GitLab instance. The redirect URI must match the Callback URL configured in GitLab.">
<a href="{{ $this->getOAuthUrl() }}" wire:key="oauth-url-{{ md5((string) $redirectUri) }}"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
Connect to GitLab
</x-forms.button>
</a>
<x-external-link />
</a>
</x-application.settings-section>
@endif
</div>
@endif