feat(acl): Change views/backend code to able to use proper ACL's later on. Currently it is not enabled.

This commit is contained in:
Andras Bacsai
2025-08-26 10:27:31 +02:00
parent 5a88377a67
commit 63fcc0ebc3
159 changed files with 3610 additions and 1922 deletions

View File

@@ -9,7 +9,7 @@
<div>
<div class="flex items-center gap-2">
<h2>Advanced</h2>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button canGate="update" :canResource="$server" type="submit">Save</x-forms.button>
</div>
<div class="mb-4">Advanced configuration for your server.</div>
</div>
@@ -59,10 +59,10 @@
<div class="flex flex-col gap-6">
<div class="flex flex-col">
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
<x-forms.input placeholder="0 23 * * *" id="serverDiskUsageCheckFrequency"
label="Disk usage check frequency" required
<x-forms.input canGate="update" :canResource="$server" placeholder="0 23 * * *"
id="serverDiskUsageCheckFrequency" label="Disk usage check frequency" required
helper="Cron expression for disk usage check frequency.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at 11:00 PM." />
<x-forms.input id="serverDiskUsageNotificationThreshold"
<x-forms.input canGate="update" :canResource="$server" id="serverDiskUsageNotificationThreshold"
label="Server disk usage notification threshold (%)" required
helper="If the server disk usage exceeds this threshold, Coolify will send a notification to the team members." />
</div>
@@ -71,9 +71,11 @@
<div class="flex flex-col">
<h3>Builds</h3>
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
<x-forms.input id="concurrentBuilds" label="Number of concurrent builds" required
<x-forms.input canGate="update" :canResource="$server" id="concurrentBuilds"
label="Number of concurrent builds" required
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
<x-forms.input id="dynamicTimeout" label="Deployment timeout (seconds)" required
<x-forms.input canGate="update" :canResource="$server" id="dynamicTimeout"
label="Deployment timeout (seconds)" required
helper="You can define the maximum duration for a deployment to run before timing it out." />
</div>
</div>

View File

@@ -8,28 +8,30 @@
<div class="flex flex-col gap-4">
<div class="flex items-center gap-2">
<h2>CA Certificate</h2>
<div class="flex gap-2">
<x-modal-confirmation title="Confirm changing of CA Certificate?" buttonTitle="Save"
submitAction="saveCaCertificate" :actions="[
'This will overwrite the existing CA certificate at /data/coolify/ssl/coolify-ca.crt with your custom CA certificate.',
'This will regenerate all SSL certificates for databases on this server and it will sign them with your custom CA.',
'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with your new CA certificate.',
'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
]"
confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
step3ButtonText="Save Certificate">
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Regenerate Certificate?" buttonTitle="Regenerate "
submitAction="regenerateCaCertificate" :actions="[
'This will generate a new CA certificate at /data/coolify/ssl/coolify-ca.crt and replace the existing one.',
'This will regenerate all SSL certificates for databases on this server and it will sign them with the new CA certificate.',
'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with the new CA certificate.',
'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
]"
confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
step3ButtonText="Regenerate Certificate">
</x-modal-confirmation>
</div>
@can('update', $server)
<div class="flex gap-2">
<x-modal-confirmation title="Confirm changing of CA Certificate?" buttonTitle="Save"
submitAction="saveCaCertificate" :actions="[
'This will overwrite the existing CA certificate at /data/coolify/ssl/coolify-ca.crt with your custom CA certificate.',
'This will regenerate all SSL certificates for databases on this server and it will sign them with your custom CA.',
'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with your new CA certificate.',
'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
]"
confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
step3ButtonText="Save Certificate">
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Regenerate Certificate?" buttonTitle="Regenerate "
submitAction="regenerateCaCertificate" :actions="[
'This will generate a new CA certificate at /data/coolify/ssl/coolify-ca.crt and replace the existing one.',
'This will regenerate all SSL certificates for databases on this server and it will sign them with the new CA certificate.',
'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with the new CA certificate.',
'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
]"
confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
step3ButtonText="Regenerate Certificate">
</x-modal-confirmation>
</div>
@endcan
</div>
<div class="space-y-4">
<div class="text-sm">
@@ -63,9 +65,11 @@
</span>
@endif
</div>
<x-forms.button wire:click="toggleCertificate" type="button" class="py-1! px-2! text-sm">
{{ $showCertificate ? 'Hide' : 'Show' }}
</x-forms.button>
@can('view', $server)
<x-forms.button wire:click="toggleCertificate" type="button" class="py-1! px-2! text-sm">
{{ $showCertificate ? 'Hide' : 'Show' }}
</x-forms.button>
@endcan
</div>
@if ($showCertificate)
<textarea class="w-full h-[370px] input" wire:model="certificateContent"

View File

@@ -88,13 +88,20 @@
<livewire:activity-monitor header="Logs" fullHeight />
</x-slot:content>
</x-slide-over>
<form @submit.prevent="$wire.dispatch('automatedCloudflareConfig')"
class="flex flex-col gap-2 w-full">
<x-forms.input id="cloudflare_token" required label="Cloudflare Token" type="password" />
<x-forms.input id="ssh_domain" label="Configured SSH Domain" required
helper="The SSH domain you configured in Cloudflare. Make sure there is no protocol like http(s):// so you provide a FQDN not a URL. <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels/server-ssh' target='_blank'>Documentation</a>" />
<x-forms.button type="submit" isHighlighted>Continue</x-forms.button>
</form>
@can('update', $server)
<form @submit.prevent="$wire.dispatch('automatedCloudflareConfig')"
class="flex flex-col gap-2 w-full">
<x-forms.input id="cloudflare_token" required label="Cloudflare Token" type="password" />
<x-forms.input id="ssh_domain" label="Configured SSH Domain" required
helper="The SSH domain you configured in Cloudflare. Make sure there is no protocol like http(s):// so you provide a FQDN not a URL. <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels/server-ssh' target='_blank'>Documentation</a>" />
<x-forms.button type="submit" isHighlighted>Continue</x-forms.button>
</form>
@else
<div
class="p-4 mb-4 text-sm text-yellow-800 bg-yellow-100 rounded-sm dark:bg-yellow-900 dark:text-yellow-300">
You don't have permission to configure Cloudflare Tunnel for this server.
</div>
@endcan
</div>
@script
<script>
@@ -111,14 +118,21 @@
</div>
<h3 class="pt-6 pb-2">Manual</h3>
<div class="pl-2">
<x-modal-confirmation buttonFullWidth title="I manually configured Cloudflare Tunnel?"
buttonTitle="I manually configured Cloudflare Tunnel" submitAction="manualCloudflareConfig"
:actions="[
'You set everything up manually, including in Cloudflare and on the server (cloudflared is running).',
'If you missed something, the connection will not work.',
]" confirmationText="I manually configured Cloudflare Tunnel"
confirmationLabel="Please type the confirmation text to confirm that you manually configured Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text" />
@can('update', $server)
<x-modal-confirmation buttonFullWidth title="I manually configured Cloudflare Tunnel?"
buttonTitle="I manually configured Cloudflare Tunnel" submitAction="manualCloudflareConfig"
:actions="[
'You set everything up manually, including in Cloudflare and on the server (cloudflared is running).',
'If you missed something, the connection will not work.',
]" confirmationText="I manually configured Cloudflare Tunnel"
confirmationLabel="Please type the confirmation text to confirm that you manually configured Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text" />
@else
<div
class="p-4 mb-4 text-sm text-yellow-800 bg-yellow-100 rounded-sm dark:bg-yellow-900 dark:text-yellow-300">
You don't have permission to configure Cloudflare Tunnel for this server.
</div>
@endcan
</div>
@endif
</div>

View File

@@ -9,10 +9,12 @@
@if ($server->isFunctional())
<div class="flex items-end gap-2">
<h2>Destinations</h2>
<x-modal-input buttonTitle="+ Add" title="New Destination">
<livewire:destination.new.docker :server_id="$server->id" />
</x-modal-input>
<x-forms.button isHighlighted wire:click='scan'>Scan for Destinations</x-forms.button>
@can('update', $server)
<x-modal-input buttonTitle="+ Add" title="New Destination">
<livewire:destination.new.docker :server_id="$server->id" />
</x-modal-input>
@endcan
<x-forms.button canGate="update" :canResource="$server" isHighlighted wire:click='scan'>Scan for Destinations</x-forms.button>
</div>
<div>Destinations are used to segregate resources by network.</div>
<h4 class="pt-4 pb-2">Available Destinations</h4>
@@ -34,7 +36,7 @@
<div class="flex flex-wrap gap-2 ">
@foreach ($networks as $network)
<div class="min-w-fit">
<x-forms.button wire:click="add('{{ data_get($network, 'Name') }}')">Add
<x-forms.button canGate="update" :canResource="$server" wire:click="add('{{ data_get($network, 'Name') }}')">Add
{{ data_get($network, 'Name') }}</x-forms.button>
</div>
@endforeach

View File

@@ -10,7 +10,7 @@
<div>
<div class="flex items-center gap-2">
<h2>Docker Cleanup</h2>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button type="submit" canGate="update" :canResource="$server">Save</x-forms.button>
</div>
<div class="mt-3 mb-4">Configure Docker cleanup settings for your server.</div>
</div>
@@ -18,27 +18,30 @@
<div class="flex flex-col gap-2">
<div class="flex gap-4">
<h3>Docker Cleanup</h3>
<x-modal-confirmation title="Confirm Docker Cleanup?" buttonTitle="Trigger Manual Cleanup"
isHighlightedButton submitAction="manualCleanup" :actions="[
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
'Permanently deletes all unused images',
'Clears build cache',
'Removes old versions of the Coolify helper image',
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
'Optionally permanently deletes all unused networks (if enabled in advanced options).',
]" :confirmWithText="false"
:confirmWithPassword="false" step2ButtonText="Trigger Docker Cleanup" />
@can('update', $server)
<x-modal-confirmation title="Confirm Docker Cleanup?" buttonTitle="Trigger Manual Cleanup"
isHighlightedButton submitAction="manualCleanup" :actions="[
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
'Permanently deletes all unused images',
'Clears build cache',
'Removes old versions of the Coolify helper image',
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
'Optionally permanently deletes all unused networks (if enabled in advanced options).',
]" :confirmWithText="false"
:confirmWithPassword="false" step2ButtonText="Trigger Docker Cleanup" />
@endcan
</div>
<div class="flex flex-wrap items-center gap-4">
<x-forms.input placeholder="*/10 * * * *" id="dockerCleanupFrequency"
label="Docker cleanup frequency" required
<x-forms.input canGate="update" :canResource="$server" placeholder="*/10 * * * *"
id="dockerCleanupFrequency" label="Docker cleanup frequency" required
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at midnight." />
@if (!$forceDockerCleanup)
<x-forms.input id="dockerCleanupThreshold" label="Docker cleanup threshold (%)" required
<x-forms.input canGate="update" :canResource="$server" id="dockerCleanupThreshold"
label="Docker cleanup threshold (%)" required
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
@endif
<div class="w-96">
<x-forms.checkbox
<x-forms.checkbox canGate="update" :canResource="$server"
helper="Enabling Force Docker Cleanup or manually triggering a cleanup will perform the following actions:
<ul class='list-disc pl-4 mt-2'>
<li>Removes stopped containers managed by Coolify (as containers are none persistent, no data will be lost).</li>
@@ -58,14 +61,16 @@
functional issues.
</p>
<div class="w-96">
<x-forms.checkbox instantSave id="deleteUnusedVolumes" label="Delete Unused Volumes"
<x-forms.checkbox canGate="update" :canResource="$server" instantSave id="deleteUnusedVolumes"
label="Delete Unused Volumes"
helper="This option will remove all unused Docker volumes during cleanup.<br><br><strong>Warning: Data form stopped containers will be lost!</strong><br><br>Consequences include:<br>
<ul class='list-disc pl-4 mt-2'>
<li>Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).</li>
<li>Data from stopped containers volumes will be permanently lost.</li>
<li>No way to recover deleted volume data.</li>
</ul>" />
<x-forms.checkbox instantSave id="deleteUnusedNetworks" label="Delete Unused Networks"
<x-forms.checkbox canGate="update" :canResource="$server" instantSave id="deleteUnusedNetworks"
label="Delete Unused Networks"
helper="This option will remove all unused Docker networks during cleanup.<br><br><strong>Warning: Functionality may be lost and containers may not be able to communicate with each other!</strong><br><br>Consequences include:<br>
<ul class='list-disc pl-4 mt-2'>
<li>Networks not attached to running containers will be permanently deleted (stopped containers are affected).</li>

View File

@@ -4,9 +4,11 @@
</x-slot>
<div class="flex items-center gap-2">
<h1>Servers</h1>
<x-modal-input buttonTitle="+ Add" title="New Server" :closeOutside="false">
<livewire:server.create />
</x-modal-input>
@can('createAnyResource')
<x-modal-input buttonTitle="+ Add" title="New Server" :closeOutside="false">
<livewire:server.create />
</x-modal-input>
@endcan
</div>
<div class="subtitle">All your servers are here.</div>
<div class="grid gap-4 lg:grid-cols-2">

View File

@@ -20,7 +20,8 @@
@if ($isLogDrainAxiomEnabled || $isLogDrainCustomEnabled)
<x-forms.checkbox disabled id="isLogDrainNewRelicEnabled" label="Enabled" />
@else
<x-forms.checkbox instantSave id="isLogDrainNewRelicEnabled" label="Enabled" />
<x-forms.checkbox instantSave canGate="update" :canResource="$server"
id="isLogDrainNewRelicEnabled" label="Enabled" />
@endif
</div>
<div class="flex flex-col gap-4">
@@ -33,9 +34,10 @@
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
@else
<x-forms.input type="password" required id="logDrainNewRelicLicenseKey"
label="License Key" />
<x-forms.input required id="logDrainNewRelicBaseUri"
<x-forms.input canGate="update" :canResource="$server" type="password" required
id="logDrainNewRelicLicenseKey" label="License Key" />
<x-forms.input canGate="update" :canResource="$server" required
id="logDrainNewRelicBaseUri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
@@ -43,7 +45,7 @@
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
<x-forms.button canGate="update" :canResource="$server" type="submit">
Save
</x-forms.button>
</div>
@@ -54,7 +56,8 @@
@if ($isLogDrainNewRelicEnabled || $isLogDrainCustomEnabled)
<x-forms.checkbox disabled id="isLogDrainAxiomEnabled" label="Enabled" />
@else
<x-forms.checkbox instantSave id="isLogDrainAxiomEnabled" label="Enabled" />
<x-forms.checkbox instantSave canGate="update" :canResource="$server"
id="isLogDrainAxiomEnabled" label="Enabled" />
@endif
</div>
<form wire:submit='submit("axiom")' class="flex flex-col">
@@ -66,14 +69,15 @@
<x-forms.input disabled required id="logDrainAxiomDatasetName"
label="Dataset Name" />
@else
<x-forms.input type="password" required id="logDrainAxiomApiKey"
label="API Key" />
<x-forms.input required id="logDrainAxiomDatasetName" label="Dataset Name" />
<x-forms.input canGate="update" :canResource="$server" type="password" required
id="logDrainAxiomApiKey" label="API Key" />
<x-forms.input canGate="update" :canResource="$server" required
id="logDrainAxiomDatasetName" label="Dataset Name" />
@endif
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
<x-forms.button canGate="update" :canResource="$server" type="submit">
Save
</x-forms.button>
</div>
@@ -83,7 +87,8 @@
@if ($isLogDrainNewRelicEnabled || $isLogDrainAxiomEnabled)
<x-forms.checkbox disabled id="isLogDrainCustomEnabled" label="Enabled" />
@else
<x-forms.checkbox instantSave id="isLogDrainCustomEnabled" label="Enabled" />
<x-forms.checkbox instantSave canGate="update" :canResource="$server"
id="isLogDrainCustomEnabled" label="Enabled" />
@endif
</div>
<form wire:submit='submit("custom")' class="flex flex-col">
@@ -94,15 +99,15 @@
<x-forms.textarea disabled id="logDrainCustomConfigParser"
label="Custom Parser Configuration" />
@else
<x-forms.textarea rows="6" required id="logDrainCustomConfig"
label="Custom FluentBit Configuration" />
<x-forms.textarea id="logDrainCustomConfigParser"
label="Custom Parser Configuration" />
<x-forms.textarea canGate="update" :canResource="$server" rows="6" required
id="logDrainCustomConfig" label="Custom FluentBit Configuration" />
<x-forms.textarea canGate="update" :canResource="$server"
id="logDrainCustomConfigParser" label="Custom Parser Configuration" />
@endif
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
<x-forms.button canGate="update" :canResource="$server" type="submit">
Save
</x-forms.button>
</div>

View File

@@ -77,18 +77,22 @@
]) }}">
Resources
</a>
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
href="{{ route('server.command', [
'server_uuid' => data_get($server, 'uuid'),
]) }}">
Terminal
</a>
<a class="{{ request()->routeIs('server.security.patches') ? 'dark:text-white' : '' }}"
href="{{ route('server.security.patches', [
'server_uuid' => data_get($server, 'uuid'),
]) }}">
Security
</a>
@can('canAccessTerminal')
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
href="{{ route('server.command', [
'server_uuid' => data_get($server, 'uuid'),
]) }}">
Terminal
</a>
@endcan
@can('update', $server)
<a class="{{ request()->routeIs('server.security.patches') ? 'dark:text-white' : '' }}"
href="{{ route('server.security.patches', [
'server_uuid' => data_get($server, 'uuid'),
]) }}">
Security
</a>
@endcan
</nav>
<div class="order-first sm:order-last">
<div>

View File

@@ -8,10 +8,12 @@
<div class="w-full">
<div class="flex items-end gap-2">
<h2>Private Key</h2>
<x-modal-input buttonTitle="+ Add" title="New Private Key">
<livewire:security.private-key.create />
</x-modal-input>
<x-forms.button isHighlighted wire:click.prevent='checkConnection'>
@can('createAnyResource')
<x-modal-input buttonTitle="+ Add" title="New Private Key">
<livewire:security.private-key.create />
</x-modal-input>
@endcan
<x-forms.button canGate="update" :canResource="$server" isHighlighted wire:click.prevent='checkConnection'>
Check connection
</x-forms.button>
</div>
@@ -25,7 +27,7 @@
<div class="box-description">{{ $private_key->description }}</div>
</div>
@if (data_get($server, 'privateKey.uuid') !== $private_key->uuid)
<x-forms.button class="w-full" wire:click='setPrivateKey({{ $private_key->id }})'>
<x-forms.button canGate="update" :canResource="$server" class="w-full" wire:click='setPrivateKey({{ $private_key->id }})'>
Use this key
</x-forms.button>
@else

View File

@@ -7,12 +7,11 @@
<div class="flex items-center gap-2">
<h2>Configuration</h2>
@if ($server->proxy->status === 'exited' || $server->proxy->status === 'removing')
<x-forms.button wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
<x-forms.button canGate="update" :canResource="$server" wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
@else
<x-forms.button disabled wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
<x-forms.button canGate="update" :canResource="$server" disabled wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
@endif
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button canGate="update" :canResource="$server" type="submit">Save</x-forms.button>
</div>
<div class="pb-4 "> <svg class="inline-flex w-6 h-6 mr-2 dark:text-warning" viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg">
@@ -23,16 +22,16 @@
</div>
<h3>Advanced</h3>
<div class="pb-4 w-96">
<x-forms.checkbox
<x-forms.checkbox canGate="update" :canResource="$server"
helper="If set, all resources will only have docker container labels for {{ str($server->proxyType())->title() }}.<br>For applications, labels needs to be regenerated manually. <br>Resources needs to be restarted."
id="server.settings.generate_exact_labels"
label="Generate labels only for {{ str($server->proxyType())->title() }}" instantSave />
<x-forms.checkbox instantSave="instantSaveRedirect" id="redirect_enabled"
label="Override default request handler"
<x-forms.checkbox canGate="update" :canResource="$server" instantSave="instantSaveRedirect"
id="redirect_enabled" label="Override default request handler"
helper="Requests to unknown hosts or stopped services will receive a 503 response or be redirected to the URL you set below (need to enable this first)." />
@if ($redirect_enabled)
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url"
label="Redirect to (optional)" />
<x-forms.input canGate="update" :canResource="$server" placeholder="https://app.coolify.io"
id="redirect_url" label="Redirect to (optional)" />
@endif
</div>
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value)
@@ -53,9 +52,11 @@
<div wire:loading.remove wire:target="loadProxyConfiguration">
@if ($proxy_settings)
<div class="flex flex-col gap-2 pt-4">
<x-forms.textarea useMonacoEditor monacoEditorLanguage="yaml" label="Configuration file"
name="proxy_settings" id="proxy_settings" rows="30" />
<x-forms.button wire:click.prevent="reset_proxy_configuration">
<x-forms.textarea canGate="update" :canResource="$server" useMonacoEditor
monacoEditorLanguage="yaml" label="Configuration file" name="proxy_settings"
id="proxy_settings" rows="30" />
<x-forms.button canGate="update" :canResource="$server"
wire:click.prevent="reset_proxy_configuration">
Reset configuration to default
</x-forms.button>
</div>
@@ -65,33 +66,44 @@
@elseif($selectedProxy === 'NONE')
<div class="flex items-center gap-2">
<h2>Configuration</h2>
<x-forms.button wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
@can('update', $server)
<x-forms.button wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
@endcan
</div>
<div class="pt-2 pb-4">Custom (None) Proxy Selected</div>
@else
<div class="flex items-center gap-2">
<h2>Configuration</h2>
<x-forms.button wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
@can('update', $server)
<x-forms.button wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
@endcan
</div>
@endif
@else
<div>
<h2>Configuration</h2>
<div class="subtitle">Select a proxy you would like to use on this server.</div>
<div class="grid gap-4">
<x-forms.button class="box" wire:click="selectProxy('NONE')">
Custom (None)
</x-forms.button>
<x-forms.button class="box" wire:click="selectProxy('TRAEFIK')">
Traefik
</x-forms.button>
<x-forms.button class="box" wire:click="selectProxy('CADDY')">
Caddy
</x-forms.button>
{{-- <x-forms.button disabled class="box">
Nginx
</x-forms.button> --}}
</div>
@can('update', $server)
<div class="grid gap-4">
<x-forms.button class="box" wire:click="selectProxy('NONE')">
Custom (None)
</x-forms.button>
<x-forms.button class="box" wire:click="selectProxy('TRAEFIK')">
Traefik
</x-forms.button>
<x-forms.button class="box" wire:click="selectProxy('CADDY')">
Caddy
</x-forms.button>
{{-- <x-forms.button disabled class="box">
Nginx
</x-forms.button> --}}
</div>
@else
<div
class="p-4 mb-4 text-sm text-yellow-800 bg-yellow-100 rounded-sm dark:bg-yellow-900 dark:text-yellow-300">
You don't have permission to configure proxy settings for this server.
</div>
@endcan
</div>
@endif
</div>

View File

@@ -1,10 +1,12 @@
<div class="flex gap-2">
<h3 class="dark:text-white">File: {{ str_replace('|', '.', $fileName) }}</h3>
<div class="flex gap-2">
<x-modal-input buttonTitle="Edit" title="Edit Configuration">
<livewire:server.proxy.new-dynamic-configuration :server_id="$server_id" :fileName="$fileName" :value="$value"
:newFile="$newFile" wire:key="{{ $fileName }}" />
</x-modal-input>
</div>
<x-forms.button isError wire:click="delete('{{ $fileName }}')">Delete</x-forms.button>
@can('update', $server)
<div class="flex gap-2">
<x-modal-input buttonTitle="Edit" title="Edit Configuration">
<livewire:server.proxy.new-dynamic-configuration :server_id="$server_id" :fileName="$fileName" :value="$value"
:newFile="$newFile" wire:key="{{ $fileName }}" />
</x-modal-input>
</div>
<x-forms.button isError wire:click="delete('{{ $fileName }}')">Delete</x-forms.button>
@endcan
</div>

View File

@@ -12,9 +12,11 @@
<div class="flex gap-2">
<h2>Dynamic Configurations</h2>
<x-forms.button wire:click="loadDynamicConfigurations">Reload</x-forms.button>
<x-modal-input buttonTitle="+ Add" title="New Dynamic Configuration">
<livewire:server.proxy.new-dynamic-configuration :server_id="$server->id" />
</x-modal-input>
@can('update', $server)
<x-modal-input buttonTitle="+ Add" title="New Dynamic Configuration">
<livewire:server.proxy.new-dynamic-configuration :server_id="$server->id" />
</x-modal-input>
@endcan
</div>
<div class='pb-4'>You can add dynamic proxy configurations here.</div>
</div>
@@ -38,7 +40,7 @@
wire:model="contents.{{ $fileName }}" rows="5" />
@else
<livewire:server.proxy.dynamic-configuration-navbar :server_id="$server->id"
:fileName="$fileName" :value="$value ?? ''" :newFile="false"
:server="$server" :fileName="$fileName" :value="$value ?? ''" :newFile="false"
wire:key="{{ $fileName }}-{{ $loop->index }}" />
<x-forms.textarea disabled wire:model="contents.{{ $fileName }}"
rows="10" />

View File

@@ -1,5 +1,6 @@
<form wire:submit.prevent="addDynamicConfiguration" class="flex flex-col w-full gap-4">
<x-forms.input id="fileName" label="Filename" required />
<x-forms.textarea allowTab useMonacoEditor id="value" label="Configuration" required rows="20" />
<x-forms.button type="submit" @click="slideOverOpen=false">Save</x-forms.button>
<x-forms.input canGate="update" :canResource="$server" id="fileName" label="Filename" required />
<x-forms.textarea canGate="update" :canResource="$server" allowTab useMonacoEditor id="value" label="Configuration"
required rows="20" />
<x-forms.button canGate="update" :canResource="$server" type="submit" @click="slideOverOpen=false">Save</x-forms.button>
</form>

View File

@@ -14,9 +14,9 @@
submitAction="submit" :actions="[
'If you misconfigure the server, you could lose a lot of functionalities of Coolify.',
]" :confirmWithText="false" :confirmWithPassword="false"
step2ButtonText="Save" />
step2ButtonText="Save" canGate="update" :canResource="$server" />
@else
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button type="submit" canGate="update" :canResource="$server">Save</x-forms.button>
@if ($server->isFunctional())
<x-slide-over closeWithX fullScreen>
<x-slot:title>Validate & configure</x-slot:title>
@@ -24,7 +24,7 @@
<livewire:server.validate-and-install :server="$server" ask />
</x-slot:content>
<x-forms.button @click="slideOverOpen=true" wire:click.prevent='validateServer'
isHighlighted>
isHighlighted canGate="update" :canResource="$server">
Revalidate server
</x-forms.button>
</x-slide-over>
@@ -68,67 +68,86 @@
@endif
<div class="flex flex-col gap-2 pt-4">
<div class="flex flex-col gap-2 w-full lg:flex-row">
<x-forms.input id="name" label="Name" required />
<x-forms.input id="description" label="Description" />
<x-forms.input canGate="update" :canResource="$server" id="name" label="Name" required />
<x-forms.input canGate="update" :canResource="$server" id="description" label="Description" />
@if (!$isSwarmWorker && !$isBuildServer)
<x-forms.input placeholder="https://example.com" id="wildcardDomain" label="Wildcard Domain"
<x-forms.input canGate="update" :canResource="$server" placeholder="https://example.com"
id="wildcardDomain" label="Wildcard Domain"
helper='A wildcard domain allows you to receive a randomly generated domain for your new applications. <br><br>For instance, if you set "https://example.com" as your wildcard domain, your applications will receive domains like "https://randomId.example.com".' />
@endif
</div>
<div class="flex flex-col gap-2 w-full lg:flex-row">
<x-forms.input type="password" id="ip" label="IP Address/Domain"
<x-forms.input canGate="update" :canResource="$server" type="password" id="ip"
label="IP Address/Domain"
helper="An IP Address (127.0.0.1) or domain (example.com). Make sure there is no protocol like http(s):// so you provide a FQDN not a URL."
required />
<div class="flex gap-2">
<x-forms.input id="user" label="User" required />
<x-forms.input type="number" id="port" label="Port" required />
<x-forms.input canGate="update" :canResource="$server" id="user" label="User" required />
<x-forms.input canGate="update" :canResource="$server" type="number" id="port"
label="Port" required />
</div>
</div>
<div class="w-full" x-data="{
open: false,
search: '{{ $serverTimezone ?: '' }}',
timezones: @js($this->timezones),
placeholder: '{{ $serverTimezone ? 'Search timezone...' : 'Select Server Timezone' }}',
init() {
this.$watch('search', value => {
if (value === '') {
this.open = true;
}
})
}
}">
<div class="w-full">
<div class="flex items-center mb-1">
<label for="serverTimezone">Server
Timezone</label>
<label for="serverTimezone">Server Timezone</label>
<x-helper class="ml-2"
helper="Server's timezone. This is used for backups, cron jobs, etc." />
</div>
<div class="relative">
<div class="inline-flex relative items-center w-64">
<input autocomplete="off"
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input" :placeholder="placeholder" wire:model="serverTimezone">
<svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
</svg>
@can('update', $server)
<div x-data="{
open: false,
search: '{{ $serverTimezone ?: '' }}',
timezones: @js($this->timezones),
placeholder: '{{ $serverTimezone ? 'Search timezone...' : 'Select Server Timezone' }}',
init() {
this.$watch('search', value => {
if (value === '') {
this.open = true;
}
})
}
}">
<div class="relative">
<div class="inline-flex relative items-center w-64">
<input autocomplete="off"
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input" :placeholder="placeholder" wire:model="serverTimezone">
<svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
</svg>
</div>
<div x-show="open"
class="overflow-auto overflow-x-hidden absolute z-50 mt-1 w-64 max-h-60 bg-white rounded-md border shadow-lg dark:bg-coolgray-100 dark:border-coolgray-200 scrollbar">
<template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone">
<div @click="search = timezone; open = false; $wire.set('serverTimezone', timezone); $wire.submit()"
class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
x-text="timezone"></div>
</template>
</div>
</div>
</div>
<div x-show="open"
class="overflow-auto overflow-x-hidden absolute z-50 mt-1 w-64 max-h-60 bg-white rounded-md border shadow-lg dark:bg-coolgray-100 dark:border-coolgray-200 scrollbar">
<template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone">
<div @click="search = timezone; open = false; $wire.set('serverTimezone', timezone); $wire.submit()"
class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
x-text="timezone"></div>
</template>
@else
<div class="relative">
<div class="inline-flex relative items-center w-64">
<input readonly disabled autocomplete="off"
class="w-full input opacity-50 cursor-not-allowed"
value="{{ $serverTimezone ?: 'No timezone set' }}" placeholder="Server Timezone">
<svg class="absolute right-0 mr-2 w-4 h-4 opacity-50" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
</svg>
</div>
</div>
</div>
@endcan
</div>
<div class="w-full">
@@ -139,8 +158,8 @@
helper="You can't use this server as a build server because it has defined resources."
label="Use it as a build server?" />
@else
<x-forms.checkbox instantSave id="isBuildServer"
label="Use it as a build server?" />
<x-forms.checkbox canGate="update" :canResource="$server" instantSave
id="isBuildServer" label="Use it as a build server?" />
@endif
</div>
@@ -157,7 +176,8 @@
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="isSwarmManager"
<x-forms.checkbox canGate="update" :canResource="$server" instantSave
type="checkbox" id="isSwarmManager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
@@ -167,7 +187,8 @@
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="isSwarmWorker"
<x-forms.checkbox canGate="update" :canResource="$server" instantSave
type="checkbox" id="isSwarmWorker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@endif
@@ -180,20 +201,23 @@
@if ($server->isFunctional() && !$server->isSwarm() && !$server->isBuildServer())
<form wire:submit.prevent='submit'>
<div class="flex gap-2 items-center pt-4 pb-2">
<h3>Sentinel
</h3>
<h3>Sentinel</h3>
<x-helper helper="Sentinel reports your server's & container's health and collects metrics." />
@if ($server->isSentinelEnabled())
<div class="flex gap-2 items-center">
@if ($server->isSentinelLive())
<x-status.running status="In sync" noLoading title="{{ $sentinelUpdatedAt }}" />
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel'>Restart</x-forms.button>
<x-forms.button type="submit" canGate="update"
:canResource="$server">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel' canGate="update"
:canResource="$server">Restart</x-forms.button>
@else
<x-status.stopped status="Out of sync" noLoading
title="{{ $sentinelUpdatedAt }}" />
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel'>Sync</x-forms.button>
<x-forms.button type="submit" canGate="update"
:canResource="$server">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel' canGate="update"
:canResource="$server">Sync</x-forms.button>
@endif
</div>
@endif
@@ -201,13 +225,15 @@
<div class="flex flex-col gap-2">
<div class="w-96">
<x-forms.checkbox wire:model.live="isSentinelEnabled" label="Enable Sentinel" />
<x-forms.checkbox canGate="update" :canResource="$server" wire:model.live="isSentinelEnabled"
label="Enable Sentinel" />
@if ($server->isSentinelEnabled())
@if (isDev())
<x-forms.checkbox id="isSentinelDebugEnabled" label="Enable Sentinel (with debug)"
instantSave />
<x-forms.checkbox canGate="update" :canResource="$server" id="isSentinelDebugEnabled"
label="Enable Sentinel (with debug)" instantSave />
@endif
<x-forms.checkbox instantSave id="isMetricsEnabled" label="Enable Metrics" />
<x-forms.checkbox canGate="update" :canResource="$server" instantSave
id="isMetricsEnabled" label="Enable Metrics" />
@else
@if (isDev())
<x-forms.checkbox id="isSentinelDebugEnabled" label="Enable Sentinel (with debug)"
@@ -219,23 +245,27 @@
</div>
@if ($server->isSentinelEnabled())
<div class="flex flex-wrap gap-2 sm:flex-nowrap items-end">
<x-forms.input type="password" id="sentinelToken" label="Sentinel token" required
helper="Token for Sentinel." />
<x-forms.button wire:click="regenerateSentinelToken">Regenerate</x-forms.button>
<x-forms.input canGate="update" :canResource="$server" type="password" id="sentinelToken"
label="Sentinel token" required helper="Token for Sentinel." />
<x-forms.button canGate="update" :canResource="$server"
wire:click="regenerateSentinelToken">Regenerate</x-forms.button>
</div>
<x-forms.input id="sentinelCustomUrl" required label="Coolify URL"
<x-forms.input canGate="update" :canResource="$server" id="sentinelCustomUrl" required
label="Coolify URL"
helper="URL to your Coolify instance. If it is empty that means you do not have a FQDN set for your Coolify instance." />
<div class="flex flex-col gap-2">
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input id="sentinelMetricsRefreshRateSeconds"
label="Metrics rate (seconds)" required
<x-forms.input canGate="update" :canResource="$server"
id="sentinelMetricsRefreshRateSeconds" label="Metrics rate (seconds)" required
helper="Interval used for gathering metrics. Lower values result in more disk space usage." />
<x-forms.input id="sentinelMetricsHistoryDays" label="Metrics history (days)"
required helper="Number of days to retain metrics data for." />
<x-forms.input id="sentinelPushIntervalSeconds" label="Push interval (seconds)"
required helper="Interval at which metrics data is sent to the collector." />
<x-forms.input canGate="update" :canResource="$server" id="sentinelMetricsHistoryDays"
label="Metrics history (days)" required
helper="Number of days to retain metrics data for." />
<x-forms.input canGate="update" :canResource="$server"
id="sentinelPushIntervalSeconds" label="Push interval (seconds)" required
helper="Interval at which metrics data is sent to the collector." />
</div>
</div>
@endif