Merge branch 'next' into feat-db-ssl

This commit is contained in:
Andras Bacsai
2025-03-17 15:15:24 +01:00
committed by GitHub
117 changed files with 4584 additions and 1786 deletions

View File

@@ -79,6 +79,16 @@
</x-slot:button-text>
</x-popup>
</span>
@if (currentTeam()->subscriptionPastOverDue())
<x-banner :closable=false>
<div><span class="font-bold text-red-500">WARNING:</span> Your subscription is in over-due. If your latest
payment is not paid within a week, all automations <span class="font-bold text-red-500">will
be deactivated</span>. Visit <a href="{{ route('subscription.show') }}"
class="underline dark:text-white">/subscription</a> to check your subscription status or pay your
invoice (or check your email for the invoice).
</div>
</x-banner>
@endif
@if (currentTeam()->serverOverflow())
<x-banner :closable=false>
<div><span class="font-bold text-red-500">WARNING:</span> The number of active servers exceeds the limit

View File

@@ -58,12 +58,9 @@
<x-forms.checkbox instantSave id="isConnectToDockerNetworkEnabled" label="Connect To Predefined Network"
helper="By default, you do not reach the Coolify defined networks.<br>Starting a docker compose based resource will have an internal network. <br>If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.<br><br>For more information, check <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/knowledge-base/docker/compose#connect-to-predefined-networks'>this</a>." />
@endif
@if ($isLogDrainEnabled === false)
<h3 class="pt-4">Logs</h3>
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave id="isLogDrainEnabled" label="Drain Logs" />
@endif
<h3 class="pt-4">Logs</h3>
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave id="isLogDrainEnabled" label="Drain Logs" />
@if ($application->git_based())
<h3>Git</h3>
<x-forms.checkbox instantSave id="isGitSubmodulesEnabled" label="Submodules"

View File

@@ -15,8 +15,7 @@
href="{{ route('project.application.advanced', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Advanced</a>
@if ($application->destination->server->isSwarm())
<a class="menu-item"
wire:current.exact="menu-item-active"
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.swarm', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Swarm Configuration</a>
@endif
@@ -60,21 +59,22 @@
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.preview-deployments', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Preview Deployments</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.healthcheck', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Healthcheck</a>
@if ($application->build_pack !== 'dockercompose')
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.healthcheck', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Healthcheck</a>
@endif
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.rollback', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Rollback</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.resource-limits', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Resource Limits</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Resource Operations</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}" >Metrics</a>
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Metrics</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Tags</a>
@@ -103,7 +103,7 @@
<livewire:project.shared.webhooks :resource="$application" />
@elseif ($currentRoute === 'project.application.preview-deployments')
<livewire:project.application.previews :application="$application" />
@elseif ($currentRoute === 'project.application.healthcheck')
@elseif ($currentRoute === 'project.application.healthcheck' && $application->build_pack !== 'dockercompose')
<livewire:project.shared.health-checks :resource="$application" />
@elseif ($currentRoute === 'project.application.rollback')
<livewire:project.application.rollback :application="$application" />

View File

@@ -185,7 +185,7 @@
</div>
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
automatically.
<a class="underline" href="https://coolify.io/docs/applications">Framework
<a class="underline" href="https://coolify.io/docs/applications/">Framework
Specific Docs</a>
</div>
@endif

View File

@@ -54,6 +54,49 @@
<div class="text-gray-600 dark:text-gray-400 text-sm">
Location: {{ data_get($execution, 'filename', 'N/A') }}
</div>
<div class="flex items-center gap-3 mt-2">
<div class="text-gray-600 dark:text-gray-400 text-sm">
Backup Availability:
</div>
<span @class([
'px-2 py-1 rounded text-xs font-medium',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get($execution, 'local_storage_deleted', false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 'local_storage_deleted', false),
])>
<span class="flex items-center gap-1">
@if(!data_get($execution, 'local_storage_deleted', false))
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
@else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
</svg>
@endif
Local Storage
</span>
</span>
@if($backup->save_s3)
<span @class([
'px-2 py-1 rounded text-xs font-medium',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get($execution, 's3_storage_deleted', false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 's3_storage_deleted', false),
])>
<span class="flex items-center gap-1">
@if(!data_get($execution, 's3_storage_deleted', false))
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
@else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
</svg>
@endif
S3 Storage
</span>
</span>
@endif
</div>
@if (data_get($execution, 'message'))
<div class="mt-2 p-2 bg-gray-100 dark:bg-coolgray-200 rounded">
<pre class="whitespace-pre-wrap text-sm">{{ data_get($execution, 'message') }}</pre>

View File

@@ -15,9 +15,9 @@
</div>
<div class="pb-4">Deploy any public or private Git repositories through a GitHub App.</div>
@if ($github_apps->count() !== 0)
<h2 class="pt-4 pb-4">Select a Github App</h2>
<div class="flex flex-col gap-2">
@if ($current_step === 'github_apps')
<h2 class="pt-4 pb-4">Select a Github App</h2>
<div class="flex flex-col justify-center gap-2 text-left">
@foreach ($github_apps as $ghapp)
<div class="flex">
@@ -43,25 +43,28 @@
@endif
@if ($current_step === 'repository')
@if ($repositories->count() > 0)
<div class="flex items-end gap-2">
<x-forms.select class="w-full" label="Repository" wire:model="selected_repository_id">
@foreach ($repositories as $repo)
@if ($loop->first)
<option selected value="{{ data_get($repo, 'id') }}">
{{ data_get($repo, 'name') }}
</option>
@else
<option value="{{ data_get($repo, 'id') }}">{{ data_get($repo, 'name') }}
</option>
@endif
@endforeach
</x-forms.select>
<div class="flex flex-col gap-2 pb-6">
<div class="flex gap-2">
<x-forms.select class="w-full" label="Repository" wire:model="selected_repository_id">
@foreach ($repositories as $repo)
@if ($loop->first)
<option selected value="{{ data_get($repo, 'id') }}">
{{ data_get($repo, 'name') }}
</option>
@else
<option value="{{ data_get($repo, 'id') }}">{{ data_get($repo, 'name') }}
</option>
@endif
@endforeach
</x-forms.select>
</div>
<x-forms.button wire:click.prevent="loadBranches"> Load Repository </x-forms.button>
</div>
@else
<div>No repositories found. Check your GitHub App configuration.</div>
@endif
@if ($branches->count() > 0)
<h2 class="text-lg font-bold">Configuration</h2>
<div class="flex flex-col gap-2 pb-6">
<form class="flex flex-col" wire:submit='submit'>
<div class="flex flex-col gap-2 pb-6">

View File

@@ -80,7 +80,7 @@
respective
companies, and use of them does not imply any affiliation or endorsement.<br>Find more services
<a class="dark:text-white underline" target="_blank"
href="https://coolify.io/docs/services">here</a>.
href="https://coolify.io/docs/services/overview">here</a>.
</div>
<div class="grid justify-start grid-cols-1 gap-4 text-left xl:grid-cols-2">

View File

@@ -176,7 +176,9 @@
</div>
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
your compose file (Service Stack tab).</div>
your compose file (<a class="underline"
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
wire:navigate>General tab</a>).</div>
@foreach ($applications as $application)
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
:resource="$application" />

View File

@@ -7,6 +7,10 @@
<h2>{{ Str::headline($database->name) }}</h2>
@endif
<x-forms.button type="submit">Save</x-forms.button>
<x-modal-confirmation title="Confirm Service Database Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete" :actions="['The selected service database container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($database->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
shortConfirmationLabel="Service Database Name" step3ButtonText="Permanently Delete" />
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">

View File

@@ -1,35 +1,48 @@
<div x-data="{ raw: true }">
<div x-data="{ raw: true, showNormalTextarea: false }">
<div class="pb-4">Volume names are updated upon save. The service UUID will be added as a prefix to all volumes, to
prevent
name collision. <br>To see the actual volume names, check the Deployable Compose file, or go to Storage
menu.</div>
<div x-cloak x-show="raw" class="font-mono">
<x-forms.textarea allowTab useMonacoEditor monacoEditorLanguage="yaml" rows="20"
id="service.docker_compose_raw">
</x-forms.textarea>
<div x-cloak x-show="showNormalTextarea">
<x-forms.textarea rows="20" id="service.docker_compose_raw">
</x-forms.textarea>
</div>
<div x-cloak x-show="!showNormalTextarea">
<x-forms.textarea allowTab useMonacoEditor monacoEditorLanguage="yaml" rows="20"
id="service.docker_compose_raw">
</x-forms.textarea>
</div>
</div>
<div x-cloak x-show="raw === false" class="font-mono">
<x-forms.textarea rows="20" readonly id="service.docker_compose">
</x-forms.textarea>
</div>
<div class="pt-2 w-96">
<x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
<div class="pt-2 flex gap-2">
<div class="flex flex-col gap-2">
<x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
<x-forms.checkbox label="Show Normal Textarea" x-model="showNormalTextarea"></x-forms.checkbox>
</div>
</div>
<div class="flex justify-end w-full gap-2 pt-4">
<div class="flex items-end gap-2">
<div x-cloak x-show="raw">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
</div>
<div x-cloak x-show="raw === false">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
Compose</x-forms.button>
</div>
<div class="flex w-full gap-2 pt-4">
<div x-cloak x-show="raw">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
</div>
<div x-cloak x-show="raw === false">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
Compose</x-forms.button>
</div>
<div class="flex-1"></div>
<x-forms.button class="w-64" wire:click.prevent='saveEditedCompose'>
@if (blank($service->service_type))
<x-forms.button class="w-28" wire:click.prevent='validateCompose'>
Validate
</x-forms.button>
@endif
<x-forms.button class="w-28" wire:click.prevent='saveEditedCompose'>
Save
</x-forms.button>
</div>

View File

@@ -21,15 +21,6 @@
]" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to file" />
@else
<x-modal-confirmation title="Confirm File Conversion to Directory?" buttonTitle="Convert to directory"
submitAction="convertToDirectory" :actions="[
'The selected file will be permanently deleted and an empty directory will be created in its place.',
]" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to directory" />
@endif
@if ($fileStorage->is_directory)
<x-modal-confirmation title="Confirm Directory Deletion?" buttonTitle="Delete Directory" isErrorButton
submitAction="delete" :checkboxes="$directoryDeletionCheckboxes" :actions="[
'The selected directory and all its contents will be permanently deleted from the container.',
@@ -37,13 +28,21 @@
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
@else
@if (!$fileStorage->is_binary)
<x-modal-confirmation title="Confirm File Conversion to Directory?"
buttonTitle="Convert to directory" submitAction="convertToDirectory" :actions="[
'The selected file will be permanently deleted and an empty directory will be created in its place.',
]"
confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to directory" />
@endif
<x-modal-confirmation title="Confirm File Deletion?" buttonTitle="Delete File" isErrorButton
submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
@endif
@if (!$fileStorage->is_based_on_git)
{{-- @if (!$fileStorage->is_based_on_git)
<x-modal-confirmation isErrorButton buttonTitle="Delete">
<div class="px-2">This storage will be deleted. It is not reversible. <strong
class="text-error">Please
@@ -58,7 +57,7 @@
label="Permanently delete file from the server?"></x-forms.checkbox>
@endif
</x-modal-confirmation>
@endif
@endif --}}
</div>
@if (!$fileStorage->is_directory)
@if (data_get($resource, 'settings.is_preserve_repository_enabled'))
@@ -70,11 +69,10 @@
<x-forms.textarea
label="{{ $fileStorage->is_based_on_git ? 'Content (refreshed after a successful deployment)' : 'Content' }}"
rows="20" id="fileStorage.content"
readonly="{{ $fileStorage->is_based_on_git }}"></x-forms.textarea>
@if (!$fileStorage->is_based_on_git)
readonly="{{ $fileStorage->is_based_on_git || $fileStorage->is_binary }}"></x-forms.textarea>
@if (!$fileStorage->is_based_on_git && !$fileStorage->is_binary)
<x-forms.button class="w-full" type="submit">Save</x-forms.button>
@endif
@endif
</form>
</div>

View File

@@ -3,8 +3,8 @@
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="menu-item"
class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-active' : '' }}"
wire:navigate href="{{ route('project.service.configuration', [...$parameters, 'stack_service_uuid' => null]) }}">
class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-active' : '' }}" wire:navigate
href="{{ route('project.service.configuration', [...$parameters, 'stack_service_uuid' => null]) }}">
<button><- Back</button>
</a>
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
@@ -12,8 +12,8 @@
wire:navigate href="#">General</a>
@if ($serviceDatabase?->isBackupSolutionAvailable())
<a :class="activeTab === 'backups' && 'menu-item-active'" class="menu-item"
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'"
wire:navigate href="#backups">Backups</a>
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" wire:navigate
href="#backups">Backups</a>
@endif
</div>
<div class="w-full">

View File

@@ -47,6 +47,10 @@
<x-forms.checkbox instantSave id="is_build_time"
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
label="Build Variable?" />
<x-forms.checkbox instantSave id="is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
label="Is Literal?" />
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@else
@if ($is_shared)
<x-forms.checkbox instantSave id="is_build_time"

View File

@@ -3,9 +3,9 @@
<h2>Scheduled Tasks</h2>
<x-modal-input buttonTitle="+ Add" title="New Scheduled Task" :closeOutside="false">
@if ($resource->type() == 'application')
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames" />
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :id="$resource->id" :containerNames="$containerNames" />
@elseif ($resource->type() == 'service')
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames" />
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :id="$resource->id" :containerNames="$containerNames" />
@endif
</x-modal-input>
</div>

View File

@@ -32,6 +32,11 @@
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.tenant"
label="Tenant" />
@endif
@if ($oauth_setting->provider == 'google')
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.tenant"
helper="Optional parameter that supplies a hosted domain (HD) to Google, which<br>triggers a login hint to be displayed on the OAuth screen with this domain.<br><br><a class='underline dark:text-warning text-coollabs' href='https://developers.google.com/identity/openid-connect/openid-connect#hd-param' target='_blank'>Google Documentation</a>"
label="Tenant" />
@endif
@if ($oauth_setting->provider == 'authentik')
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.base_url"
label="Base URL" />

View File

@@ -132,12 +132,12 @@
<h4 class="py-4">Confirmation Settings</h4>
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4">
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
<x-forms.checkbox instantSave id="disable_two_step_confirmation" label="Disable Two Step Confirmation"
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
</div>
@else
<div class="md:w-96 pb-4">
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-disabled">
<x-modal-confirmation title="Disable Two Step Confirmation?"
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
:actions="[

View File

@@ -27,6 +27,7 @@
confirmationText="{{ data_get($github_app, 'name') }}" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@endif
</div>
</div>
<div class="subtitle">Your Private GitHub App for private repositories.</div>
@@ -46,7 +47,7 @@
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<div class="flex items-end gap-2 w-full">
<x-forms.input id="github_app.name" label="App Name" disabled />
<x-forms.input id="github_app.name" label="App Name" />
<x-forms.button wire:click.prevent="updateGithubAppName" class="bg-coollabs">
Sync Name
</x-forms.button>
@@ -57,7 +58,7 @@
</x-forms.button>
</a>
</div>
<x-forms.input id="github_app.organization" label="Organization" disabled
<x-forms.input id="github_app.organization" label="Organization"
placeholder="If empty, personal user will be used" />
</div>
@if (!isCloud())
@@ -68,27 +69,32 @@
</div>
@endif
<div class="flex gap-2">
<x-forms.input id="github_app.html_url" label="HTML Url" disabled />
<x-forms.input id="github_app.api_url" label="API Url" disabled />
<x-forms.input id="github_app.html_url" label="HTML Url" />
<x-forms.input id="github_app.api_url" label="API Url" />
</div>
<div class="flex gap-2">
@if ($github_app->html_url === 'https://github.com')
<x-forms.input id="github_app.custom_user" label="User" disabled />
<x-forms.input type="number" id="github_app.custom_port" label="Port" disabled />
@else
<x-forms.input id="github_app.custom_user" label="User" required />
<x-forms.input type="number" id="github_app.custom_port" label="Port" required />
@endif
<x-forms.input id="github_app.custom_user" label="User" required />
<x-forms.input type="number" id="github_app.custom_port" label="Port" required />
</div>
<div class="flex gap-2">
<x-forms.input type="number" id="github_app.app_id" label="App Id" disabled />
<x-forms.input type="number" id="github_app.app_id" label="App Id" required />
<x-forms.input type="number" id="github_app.installation_id" label="Installation Id"
disabled />
required />
</div>
<div class="flex gap-2">
<x-forms.input id="github_app.client_id" label="Client Id" type="password" disabled />
<x-forms.input id="github_app.client_secret" label="Client Secret" type="password" />
<x-forms.input id="github_app.webhook_secret" label="Webhook Secret" type="password" />
<x-forms.input id="github_app.client_id" label="Client Id" type="password" required />
<x-forms.input id="github_app.client_secret" label="Client Secret" type="password" required />
<x-forms.input id="github_app.webhook_secret" label="Webhook Secret" type="password" required />
</div>
<div class="flex gap-2">
<x-forms.select id="github_app.private_key_id" label="Private Key" required>
@if (blank($github_app->private_key_id))
<option value="0" selected>Select a private key</option>
@endif
@foreach ($privateKeys as $privateKey)
<option value="{{ $privateKey->id }}">{{ $privateKey->name }}</option>
@endforeach
</x-forms.select>
</div>
<div class="flex items-end gap-2 ">
<h2 class="pt-4">Permissions</h2>
@@ -182,120 +188,129 @@
shortConfirmationLabel="GitHub App Name" :confirmWithPassword="false" step2ButtonText="Permanently Delete" />
</div>
</div>
<div class=" pb-5 rounded 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">
<div class="pb-10">
@if (!isCloud() || isDev())
<div class="flex items-end gap-2">
<x-forms.select wire:model.live='webhook_endpoint' label="Webhook Endpoint"
helper="All Git webhooks will be sent to this endpoint. <br><br>If you would like to use domain instead of IP address, set your Coolify instance's FQDN in the Settings menu.">
@if ($ipv4)
<option value="{{ $ipv4 }}">Use {{ $ipv4 }}</option>
@endif
@if ($ipv6)
<option value="{{ $ipv6 }}">Use {{ $ipv6 }}</option>
@endif
@if ($fqdn)
<option value="{{ $fqdn }}">Use {{ $fqdn }}</option>
@endif
@if (config('app.url'))
<option value="{{ config('app.url') }}">Use {{ config('app.url') }}</option>
@endif
</x-forms.select>
<x-forms.button isHighlighted
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}',{{ $administration }})">
Register Now
</x-forms.button>
</div>
@else
<div class="flex gap-2">
<h2>Register a GitHub App</h2>
<x-forms.button isHighlighted
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}',{{ $administration }})">
Register Now
</x-forms.button>
</div>
<div>You need to register a GitHub App before using this source.</div>
@endif
<div class="flex flex-col gap-2">
<h3>Manual Installation</h3>
<div class="flex gap-2 items-center">
If you want to fill the form manually, you can continue below. Only for advanced users.
<x-forms.button wire:click.prevent="createGithubAppManually">
Continue
</x-forms.button>
</div>
<h3>Automated Installation</h3>
<div class=" pb-5 rounded 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">
<div class="pb-10">
@if (!isCloud() || isDev())
<div class="flex items-end gap-2">
<x-forms.select wire:model.live='webhook_endpoint' label="Webhook Endpoint"
helper="All Git webhooks will be sent to this endpoint. <br><br>If you would like to use domain instead of IP address, set your Coolify instance's FQDN in the Settings menu.">
@if ($ipv4)
<option value="{{ $ipv4 }}">Use {{ $ipv4 }}</option>
@endif
@if ($ipv6)
<option value="{{ $ipv6 }}">Use {{ $ipv6 }}</option>
@endif
@if ($fqdn)
<option value="{{ $fqdn }}">Use {{ $fqdn }}</option>
@endif
@if (config('app.url'))
<option value="{{ config('app.url') }}">Use {{ config('app.url') }}</option>
@endif
</x-forms.select>
<x-forms.button isHighlighted
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}',{{ $administration }})">
Register Now
</x-forms.button>
</div>
@else
<div class="flex gap-2">
<h2>Register a GitHub App</h2>
<x-forms.button isHighlighted
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}',{{ $administration }})">
Register Now
</x-forms.button>
</div>
<div>You need to register a GitHub App before using this source.</div>
@endif
<div class="flex flex-col gap-2 pt-4 w-96">
<x-forms.checkbox disabled instantSave id="default_permissions" label="Mandatory"
helper="Contents: read<br>Metadata: read<br>Email: read" />
<x-forms.checkbox instantSave id="preview_deployment_permissions" label="Preview Deployments "
helper="Necessary for updating pull requests with useful comments (deployment status, links, etc.)<br><br>Pull Request: read & write" />
{{-- <x-forms.checkbox instantSave id="administration" label="Administration (for Github Runners)"
<div class="flex flex-col gap-2 pt-4 w-96">
<x-forms.checkbox disabled instantSave id="default_permissions" label="Mandatory"
helper="Contents: read<br>Metadata: read<br>Email: read" />
<x-forms.checkbox instantSave id="preview_deployment_permissions" label="Preview Deployments "
helper="Necessary for updating pull requests with useful comments (deployment status, links, etc.)<br><br>Pull Request: read & write" />
{{-- <x-forms.checkbox instantSave id="administration" label="Administration (for Github Runners)"
helper="Necessary for adding Github Runners to repositories.<br><br>Administration: read & write" /> --}}
</div>
</div>
</div>
</div>
<script>
function createGithubApp(webhook_endpoint, preview_deployment_permissions, administration) {
const {
organization,
uuid,
html_url
} = @json($github_app);
if (!webhook_endpoint) {
alert('Please select a webhook endpoint.');
return;
<script>
function createGithubApp(webhook_endpoint, preview_deployment_permissions, administration) {
const {
organization,
uuid,
html_url
} = @json($github_app);
if (!webhook_endpoint) {
alert('Please select a webhook endpoint.');
return;
}
let baseUrl = webhook_endpoint;
const name = @js($name);
const isDev = @js(config('app.env')) ===
'local';
const devWebhook = @js(config('constants.webhooks.dev_webhook'));
if (isDev && devWebhook) {
baseUrl = devWebhook;
}
const webhookBaseUrl = `${baseUrl}/webhooks`;
const path = organization ? `organizations/${organization}/settings/apps/new` : 'settings/apps/new';
const default_permissions = {
contents: 'read',
metadata: 'read',
emails: 'read',
administration: 'read'
};
if (preview_deployment_permissions) {
default_permissions.pull_requests = 'write';
}
if (administration) {
default_permissions.administration = 'write';
}
const data = {
name,
url: baseUrl,
hook_attributes: {
url: `${webhookBaseUrl}/source/github/events`,
active: true,
},
redirect_url: `${webhookBaseUrl}/source/github/redirect`,
callback_urls: [`${baseUrl}/login/github/app`],
public: false,
request_oauth_on_install: false,
setup_url: `${webhookBaseUrl}/source/github/install?source=${uuid}`,
setup_on_update: true,
default_permissions,
default_events: ['pull_request', 'push']
};
const form = document.createElement('form');
form.setAttribute('method', 'post');
form.setAttribute('action', `${html_url}/${path}?state=${uuid}`);
const input = document.createElement('input');
input.setAttribute('id', 'manifest');
input.setAttribute('name', 'manifest');
input.setAttribute('type', 'hidden');
input.setAttribute('value', JSON.stringify(data));
form.appendChild(input);
document.getElementsByTagName('body')[0].appendChild(form);
form.submit();
}
let baseUrl = webhook_endpoint;
const name = @js($name);
const isDev = @js(config('app.env')) ===
'local';
const devWebhook = @js(config('constants.webhooks.dev_webhook'));
if (isDev && devWebhook) {
baseUrl = devWebhook;
}
const webhookBaseUrl = `${baseUrl}/webhooks`;
const path = organization ? `organizations/${organization}/settings/apps/new` : 'settings/apps/new';
const default_permissions = {
contents: 'read',
metadata: 'read',
emails: 'read',
administration: 'read'
};
if (preview_deployment_permissions) {
default_permissions.pull_requests = 'write';
}
if (administration) {
default_permissions.administration = 'write';
}
const data = {
name,
url: baseUrl,
hook_attributes: {
url: `${webhookBaseUrl}/source/github/events`,
active: true,
},
redirect_url: `${webhookBaseUrl}/source/github/redirect`,
callback_urls: [`${baseUrl}/login/github/app`],
public: false,
request_oauth_on_install: false,
setup_url: `${webhookBaseUrl}/source/github/install?source=${uuid}`,
setup_on_update: true,
default_permissions,
default_events: ['pull_request', 'push']
};
const form = document.createElement('form');
form.setAttribute('method', 'post');
form.setAttribute('action', `${html_url}/${path}?state=${uuid}`);
const input = document.createElement('input');
input.setAttribute('id', 'manifest');
input.setAttribute('name', 'manifest');
input.setAttribute('type', 'hidden');
input.setAttribute('value', JSON.stringify(data));
form.appendChild(input);
document.getElementsByTagName('body')[0].appendChild(form);
form.submit();
}
</script>
</script>
@endif
</div>