Merge remote-tracking branch 'origin/next' into audit-policies

# Conflicts:
#	app/Http/Controllers/Api/SecurityController.php
#	app/Http/Controllers/Api/ServersController.php
#	app/Livewire/Admin/Index.php
#	app/Livewire/Destination/Show.php
#	app/Livewire/NavbarDeleteTeam.php
#	app/Livewire/Project/Application/Previews.php
#	app/Livewire/Project/DeleteProject.php
#	app/Livewire/Project/Shared/ResourceOperations.php
#	app/Livewire/Server/Resources.php
#	app/Livewire/Server/ValidateAndInstall.php
#	app/Livewire/Storage/Show.php
#	resources/views/livewire/dashboard.blade.php
#	resources/views/livewire/project/application/heading.blade.php
#	resources/views/livewire/project/database/heading.blade.php
#	resources/views/livewire/project/service/heading.blade.php
#	resources/views/livewire/project/shared/environment-variable/show.blade.php
#	resources/views/livewire/project/shared/scheduled-task/show.blade.php
#	tests/Feature/Security/TrustHostsMiddlewareTest.php
This commit is contained in:
Andras Bacsai committed 2026-04-19 15:19:37 +02:00
commit 43f33d94ad
685 files changed
+41904 -5674

No files matched your search

@@ -1,16 +1,23 @@
<form class="flex flex-col w-full gap-2 rounded-sm" wire:submit='submit'>
<form class="flex flex-col w-full gap-2 rounded-sm" wire:submit='submit'
x-data="{ isMultiline: $wire.entangle('is_multiline') }">
<x-forms.input placeholder="NODE_ENV" id="key" label="Name" required />
@if ($is_multiline)
<x-forms.textarea id="value" label="Value" required />
@else
<x-forms.env-var-input placeholder="production" id="value" label="Value" required
:availableVars="$shared ? [] : $this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')" />
@endif
<template x-if="isMultiline">
<div wire:key="env-value-textarea">
<x-forms.textarea id="value" label="Value" required class="font-sans" spellcheck />
</div>
</template>
<template x-if="!isMultiline">
<div wire:key="env-value-input">
<x-forms.env-var-input placeholder="production" id="value" label="Value" required
:availableVars="$shared ? [] : $this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')"
:serverUuid="data_get($parameters, 'server_uuid')" />
</div>
</template>
@if (!$shared && !$is_multiline)
<div class="text-xs text-neutral-500 dark:text-neutral-400 -mt-1">
@if (!$shared)
<div x-cloak x-show="!isMultiline" wire:key="env-value-tip" class="text-xs text-neutral-500 dark:text-neutral-400 -mt-1">
Tip: Type <span class="font-mono dark:text-warning text-coollabs">{{</span> to reference a shared environment
variable
</div>
@@ -33,8 +40,8 @@
label="Is Literal?" />
@endif
<x-forms.checkbox id="is_multiline" label="Is Multiline?" />
<x-forms.checkbox id="is_multiline" x-model="isMultiline" label="Is Multiline?" />
<x-forms.button type="submit" @click="slideOverOpen=false">
Save
</x-forms.button>
</form>
</form>
@@ -84,24 +84,24 @@
Inline comments with space before # (e.g., <code class="font-mono">KEY=value #comment</code>) are stripped.
</x-callout>
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables"
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans" id="variables" wire:model="variables"
label="Production Environment Variables"></x-forms.textarea>
@if ($showPreview)
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables"
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans" label="Preview Deployments Environment Variables"
id="variablesPreview" wire:model="variablesPreview"></x-forms.textarea>
@endif
<x-forms.button type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
@else
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables"
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans" id="variables" wire:model="variables"
label="Production Environment Variables" disabled></x-forms.textarea>
@if ($showPreview)
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables"
<x-forms.textarea rows="10" class="whitespace-pre-wrap font-sans" label="Preview Deployments Environment Variables"
id="variablesPreview" wire:model="variablesPreview" disabled></x-forms.textarea>
@endif
@endcan
</form>
@endif
</div>
</div>
@@ -26,6 +26,7 @@
<div class="flex flex-col w-full gap-2 lg:flex-row lg:items-end">
<div class="flex-1">
<x-forms.input id="comment" label="Comment"
placeholder="{{ $isMagicVariable ? 'This env cannot be edited manually, it is handled by Coolify.' : '' }}"
helper="Add a note to document what this environment variable is used for." maxlength="256" />
</div>
<x-forms.button type="submit">Update</x-forms.button>
@@ -34,12 +35,6 @@
<div class="flex flex-wrap w-full items-center gap-4">
@if (!$is_redis_credential)
@if ($type === 'service')
<x-forms.checkbox instantSave id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox instantSave id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
@if (!$isMagicVariable)
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
<x-forms.checkbox instantSave id="is_literal"
@@ -48,6 +43,12 @@
@endif
@else
@if ($is_shared)
<x-forms.checkbox instantSave id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox instantSave id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
<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?" />
@@ -86,12 +87,6 @@
<div class="flex flex-wrap w-full items-center gap-4">
@if (!$is_redis_credential)
@if ($type === 'service')
<x-forms.checkbox disabled id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox disabled id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
@if (!$isMagicVariable)
<x-forms.checkbox disabled id="is_multiline" label="Is Multiline?" />
<x-forms.checkbox disabled id="is_literal"
@@ -100,6 +95,12 @@
@endif
@else
@if ($is_shared)
<x-forms.checkbox disabled id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox disabled id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
<x-forms.checkbox disabled 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?" />
@@ -140,41 +141,51 @@
<div class="flex flex-col w-full gap-2">
<div class="flex flex-col w-full gap-2 lg:flex-row">
<x-forms.input disabled id="key" />
<x-forms.input disabled type="password" id="value" />
<x-forms.env-var-input
disabled
type="password"
id="value"
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')"
:serverUuid="data_get($parameters, 'server_uuid')" />
@if ($is_shared)
<x-forms.input disabled type="password" id="real_value" />
@endif
</div>
@if (!$isMagicVariable)
<x-forms.input disabled id="comment" label="Comment"
helper="Add a note to document what this environment variable is used for." maxlength="256" />
@endif
<x-forms.input instantSave id="comment" label="Comment"
placeholder="{{ $isMagicVariable ? 'This env cannot be edited manually, it is handled by Coolify.' : '' }}"
helper="Add a note to document what this environment variable is used for." maxlength="256" />
</div>
@else
<div class="flex flex-col w-full gap-2">
<div class="flex flex-col w-full gap-2 lg:flex-row">
@if ($is_multiline)
<x-forms.input :required="$is_redis_credential" isMultiline="{{ $is_multiline }}" id="key" />
<x-forms.textarea :required="$is_redis_credential" type="password" id="value" />
<div class="flex-1" wire:key="env-show-value-textarea-{{ $env->id }}">
<x-forms.textarea :required="$is_redis_credential" type="password" id="value" />
</div>
@else
<x-forms.input :disabled="$is_redis_credential" :required="$is_redis_credential" id="key" />
<x-forms.env-var-input
:required="$is_redis_credential"
type="password"
id="value"
:availableVars="$this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')" />
<div class="w-full" wire:key="env-show-value-input-{{ $env->id }}">
<x-forms.env-var-input
:required="$is_redis_credential"
type="password"
id="value"
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')"
:serverUuid="data_get($parameters, 'server_uuid')" />
</div>
@endif
@if ($is_shared)
<x-forms.input :disabled="$is_redis_credential" :required="$is_redis_credential" disabled
type="password" id="real_value" />
@endif
</div>
@if (!$isMagicVariable)
<x-forms.input instantSave id="comment" label="Comment"
helper="Add a note to document what this environment variable is used for." maxlength="256" />
@endif
<x-forms.input instantSave id="comment" label="Comment"
placeholder="{{ $isMagicVariable ? 'This env cannot be edited manually, it is handled by Coolify.' : '' }}"
helper="Add a note to document what this environment variable is used for." maxlength="256" />
</div>
@endif
@else
@@ -191,18 +202,18 @@
disabled
type="password"
id="value"
:availableVars="$this->availableSharedVariables"
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
:projectUuid="data_get($parameters, 'project_uuid')"
:environmentUuid="data_get($parameters, 'environment_uuid')" />
:environmentUuid="data_get($parameters, 'environment_uuid')"
:serverUuid="data_get($parameters, 'server_uuid')" />
@if ($is_shared)
<x-forms.input disabled type="password" id="real_value" />
@endif
@endif
</div>
@if (!$isMagicVariable)
<x-forms.input disabled id="comment" label="Comment"
helper="Add a note to document what this environment variable is used for." maxlength="256" />
@endif
<x-forms.input disabled id="comment" label="Comment"
placeholder="{{ $isMagicVariable ? 'This env cannot be edited manually, it is handled by Coolify.' : '' }}"
helper="Add a note to document what this environment variable is used for." maxlength="256" />
</div>
@endcan
@can('update', $this->env)
@@ -210,12 +221,6 @@
<div class="flex flex-wrap w-full items-center gap-4">
@if (!$is_redis_credential)
@if ($type === 'service')
<x-forms.checkbox instantSave id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox instantSave id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
@if (!$isMagicVariable)
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
<x-forms.checkbox instantSave id="is_literal"
@@ -224,6 +229,12 @@
@endif
@else
@if ($is_shared)
<x-forms.checkbox instantSave id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox instantSave id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
<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?" />
@@ -279,6 +290,10 @@
step2ButtonText="Permanently Delete" />
@endif
</div>
@elseif ($type === 'service')
<div class="flex w-full justify-end gap-2">
<x-forms.button wire:click='lock'>Lock</x-forms.button>
</div>
@endif
</div>
@else
@@ -286,12 +301,6 @@
<div class="flex flex-wrap w-full items-center gap-4">
@if (!$is_redis_credential)
@if ($type === 'service')
<x-forms.checkbox disabled id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox disabled id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
@if (!$isMagicVariable)
<x-forms.checkbox disabled id="is_multiline" label="Is Multiline?" />
<x-forms.checkbox disabled id="is_literal"
@@ -300,6 +309,12 @@
@endif
@else
@if ($is_shared)
<x-forms.checkbox disabled id="is_buildtime"
helper="Make this variable available during Docker build process. Useful for build secrets and dependencies."
label="Available at Buildtime" />
<x-forms.checkbox disabled id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
<x-forms.checkbox disabled 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?" />
@@ -333,4 +348,4 @@
@endif
</form>
</div>
</div>
@@ -21,7 +21,8 @@
<div>No containers are running or terminal access is disabled on this server.</div>
@else
<form class="w-96 min-w-fit flex gap-2 items-end" wire:submit="$dispatchSelf('connectToContainer')"
x-data="{ autoConnected: false }" x-init="if ({{ count($containers) }} === 1 && !autoConnected) {
x-data="{ autoConnected: false }"
x-on:terminal-websocket-ready.window="if ({{ count($containers) }} === 1 && !autoConnected) {
autoConnected = true;
$nextTick(() => $wire.dispatchSelf('connectToContainer'));
}">
@@ -8,6 +8,7 @@
rafId: null,
scrollDebounce: null,
colorLogs: localStorage.getItem('coolify-color-logs') === 'true',
logFilters: JSON.parse(localStorage.getItem('coolify-log-filters')) || {error: true, warning: true, debug: true, info: true},
searchQuery: '',
matchCount: 0,
containerName: '{{ $container ?? "logs" }}',
@@ -70,6 +71,17 @@
}
}, 150);
},
getLogLevel(content) {
if (/\b(error|err|failed|failure|exception|fatal|panic|critical)\b/.test(content)) return 'error';
if (/\b(warn|warning|wrn|caution)\b/.test(content)) return 'warning';
if (/\b(debug|dbg|trace|verbose)\b/.test(content)) return 'debug';
return 'info';
},
toggleLogFilter(level) {
this.logFilters[level] = !this.logFilters[level];
localStorage.setItem('coolify-log-filters', JSON.stringify(this.logFilters));
this.applySearch();
},
toggleColorLogs() {
this.colorLogs = !this.colorLogs;
localStorage.setItem('coolify-color-logs', this.colorLogs);
@@ -81,17 +93,11 @@
const lines = logs.querySelectorAll('[data-log-line]');
lines.forEach(line => {
const content = (line.dataset.logContent || '').toLowerCase();
const level = this.getLogLevel(content);
line.dataset.logLevel = level;
line.classList.remove('log-error', 'log-warning', 'log-debug', 'log-info');
if (!this.colorLogs) return;
if (/\b(error|err|failed|failure|exception|fatal|panic|critical)\b/.test(content)) {
line.classList.add('log-error');
} else if (/\b(warn|warning|wrn|caution)\b/.test(content)) {
line.classList.add('log-warning');
} else if (/\b(debug|dbg|trace|verbose)\b/.test(content)) {
line.classList.add('log-debug');
} else if (/\b(info|inf|notice)\b/.test(content)) {
line.classList.add('log-info');
}
line.classList.add('log-' + level);
});
},
hasActiveLogSelection() {
@@ -118,7 +124,10 @@
lines.forEach(line => {
const content = (line.dataset.logContent || '').toLowerCase();
const textSpan = line.querySelector('[data-line-text]');
const matches = !query || content.includes(query);
const level = line.dataset.logLevel || this.getLogLevel(content);
const passesFilter = this.logFilters[level] !== false;
const matchesSearch = !query || content.includes(query);
const matches = passesFilter && matchesSearch;
line.classList.toggle('hidden', !matches);
if (matches && query) count++;
@@ -389,6 +398,52 @@
d="M9.53 16.122a3 3 0 0 0-5.78 1.128 2.25 2.25 0 0 1-2.4 2.245 4.5 4.5 0 0 0 8.4-2.245c0-.399-.078-.78-.22-1.128Zm0 0a15.998 15.998 0 0 0 3.388-1.62m-5.043-.025a15.994 15.994 0 0 1 1.622-3.395m3.42 3.42a15.995 15.995 0 0 0 4.764-4.648l3.876-5.814a1.151 1.151 0 0 0-1.597-1.597L14.146 6.32a15.996 15.996 0 0 0-4.649 4.763m3.42 3.42a6.776 6.776 0 0 0-3.42-3.42" />
</svg>
</button>
<div x-data="{ filterOpen: false }" class="relative">
<button x-on:click="filterOpen = !filterOpen" title="Filter Log Levels"
:class="Object.values(logFilters).some(v => !v) ? '!text-warning' : ''"
class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
<svg class="w-4 h-4" 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="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3Z" />
</svg>
</button>
<div x-show="filterOpen" x-on:click.away="filterOpen = false"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
class="absolute right-0 z-50 mt-2 w-max origin-top-right rounded-md bg-white dark:bg-coolgray-200 shadow-lg ring-1 ring-neutral-200 dark:ring-coolgray-300 focus:outline-none">
<div class="py-1">
<label class="flex items-center gap-2 px-4 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-coolgray-300 cursor-pointer select-none">
<input type="checkbox" :checked="logFilters.error" x-on:change="toggleLogFilter('error')"
class="rounded border-gray-300 dark:border-gray-600 text-warning focus:ring-warning dark:bg-coolgray-300" />
<span class="w-2.5 h-2.5 rounded-full bg-red-500"></span>
Error
</label>
<label class="flex items-center gap-2 px-4 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-coolgray-300 cursor-pointer select-none">
<input type="checkbox" :checked="logFilters.warning" x-on:change="toggleLogFilter('warning')"
class="rounded border-gray-300 dark:border-gray-600 text-warning focus:ring-warning dark:bg-coolgray-300" />
<span class="w-2.5 h-2.5 rounded-full bg-yellow-500"></span>
Warning
</label>
<label class="flex items-center gap-2 px-4 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-coolgray-300 cursor-pointer select-none">
<input type="checkbox" :checked="logFilters.debug" x-on:change="toggleLogFilter('debug')"
class="rounded border-gray-300 dark:border-gray-600 text-warning focus:ring-warning dark:bg-coolgray-300" />
<span class="w-2.5 h-2.5 rounded-full bg-purple-500"></span>
Debug
</label>
<label class="flex items-center gap-2 px-4 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-coolgray-300 cursor-pointer select-none">
<input type="checkbox" :checked="logFilters.info" x-on:change="toggleLogFilter('info')"
class="rounded border-gray-300 dark:border-gray-600 text-warning focus:ring-warning dark:bg-coolgray-300" />
<span class="w-2.5 h-2.5 rounded-full bg-blue-500"></span>
Info
</label>
</div>
</div>
</div>
<button title="Follow Logs" :class="alwaysScroll ? '!text-warning' : ''"
x-on:click="toggleScroll"
class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
@@ -425,7 +480,7 @@
@php
$displayLines = collect(explode("\n", $outputs))->filter(fn($line) => trim($line) !== '');
@endphp
<div id="logs" class="font-mono max-w-full cursor-default">
<div id="logs" class="font-logs max-w-full cursor-default">
<div x-show="searchQuery.trim() && matchCount === 0"
class="text-gray-500 dark:text-gray-400 py-2">
No matches found.
@@ -463,7 +518,7 @@
</div>
@else
<pre id="logs"
class="font-mono whitespace-pre-wrap break-all max-w-full text-neutral-400">No logs yet.</pre>
class="font-logs whitespace-pre-wrap break-all max-w-full text-neutral-400">No logs yet.</pre>
@endif
</div>
</div>
@@ -8,39 +8,35 @@
<livewire:project.application.heading :application="$resource" />
<div>
<h2>Logs</h2>
@if (str($status)->contains('exited'))
<div class="pt-4">The resource is not running.</div>
@else
<div class="pt-2" wire:loading wire:target="loadAllContainers">
Loading containers...
</div>
<div x-init="$wire.loadAllContainers()" wire:loading.remove wire:target="loadAllContainers">
@forelse ($servers as $server)
<div class="py-2">
<h4>Server: {{ $server->name }}</h4>
@if ($server->isFunctional())
@if (isset($serverContainers[$server->id]) && count($serverContainers[$server->id]) > 0)
@php
$totalContainers = collect($serverContainers)->flatten(1)->count();
@endphp
@foreach ($serverContainers[$server->id] as $container)
<livewire:project.shared.get-logs
wire:key="{{ data_get($container, 'ID', uniqid()) }}" :server="$server"
:resource="$resource" :container="data_get($container, 'Names')"
:expandByDefault="$totalContainers === 1" />
@endforeach
@else
<div class="pt-2">No containers are running on server: {{ $server->name }}</div>
@endif
<div class="pt-2" wire:loading wire:target="loadAllContainers">
Loading containers...
</div>
<div x-init="$wire.loadAllContainers()" wire:loading.remove wire:target="loadAllContainers">
@forelse ($servers as $server)
<div class="py-2">
<h4>Server: {{ $server->name }}</h4>
@if ($server->isFunctional())
@if (isset($serverContainers[$server->id]) && count($serverContainers[$server->id]) > 0)
@php
$totalContainers = collect($serverContainers)->flatten(1)->count();
@endphp
@foreach ($serverContainers[$server->id] as $container)
<livewire:project.shared.get-logs
wire:key="{{ data_get($container, 'ID', uniqid()) }}" :server="$server"
:resource="$resource" :container="data_get($container, 'Names')"
:expandByDefault="$totalContainers === 1" />
@endforeach
@else
<div class="pt-2">Server {{ $server->name }} is not functional.</div>
<div class="pt-2">No containers are running on server: {{ $server->name }}</div>
@endif
</div>
@empty
<div>No functional server found for the application.</div>
@endforelse
</div>
@endif
@else
<div class="pt-2">Server {{ $server->name }} is not functional.</div>
@endif
</div>
@empty
<div>No functional server found for the application.</div>
@endforelse
</div>
</div>
@elseif ($type === 'database')
<h1>Logs</h1>
@@ -18,24 +18,20 @@
label="CPU Weight" id="limitsCpuShares" />
</div>
<h3 class="pt-4">Limit Memory</h3>
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<x-forms.input canGate="update" :canResource="$resource"
helper="Examples: 69b (byte) or 420k (kilobyte) or 1337m (megabyte) or 1g (gigabyte).<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_reservation'>here</a>."
label="Soft Memory Limit" id="limitsMemoryReservation" />
<x-forms.input canGate="update" :canResource="$resource"
helper="0-100.<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_swappiness'>here</a>."
type="number" min="0" max="100" label="Swappiness"
id="limitsMemorySwappiness" />
</div>
<div class="flex gap-2">
<x-forms.input canGate="update" :canResource="$resource"
helper="Examples: 69b (byte) or 420k (kilobyte) or 1337m (megabyte) or 1g (gigabyte).<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_limit'>here</a>."
label="Maximum Memory Limit" id="limitsMemory" />
<x-forms.input canGate="update" :canResource="$resource"
helper="Examples:69b (byte) or 420k (kilobyte) or 1337m (megabyte) or 1g (gigabyte).<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#memswap_limit'>here</a>."
label="Maximum Swap Limit" id="limitsMemorySwap" />
</div>
<div class="flex gap-2">
<x-forms.input canGate="update" :canResource="$resource"
helper="<span class='text-helper'>Examples</span><br>• 69b (byte)<br>• 420k (kilobyte)<br>• 1337m (megabyte)<br>• 1g (gigabyte)<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_reservation'>here</a>."
label="Soft Memory Limit" id="limitsMemoryReservation" />
<x-forms.input canGate="update" :canResource="$resource"
helper="Value between 0-100.<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_swappiness'>here</a>."
type="number" min="0" max="100" label="Swappiness"
id="limitsMemorySwappiness" />
<x-forms.input canGate="update" :canResource="$resource"
helper="<span class='text-helper'>Examples</span><br>• 69b (byte)<br>• 420k (kilobyte)<br>• 1337m (megabyte)<br>• 1g (gigabyte)<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_limit'>here</a>."
label="Maximum Memory Limit" id="limitsMemory" />
<x-forms.input canGate="update" :canResource="$resource"
helper="<span class='text-helper'>Examples</span><br>• 69b (byte)<br>• 420k (kilobyte)<br>• 1337m (megabyte)<br>• 1g (gigabyte)<br><br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#memswap_limit'>here</a>."
label="Maximum Swap Limit" id="limitsMemorySwap" />
</div>
</form>
</div>
@@ -63,6 +63,9 @@
}">
<h3 class="pt-4">Clone Resource</h3>
<div class="pb-2">Duplicate this resource to another server or network destination.</div>
<x-callout type="info" title="Important" class="mb-4">
Cloning only duplicates resource configuration (such as environment variables, build settings etc..). It does not include any resource data, such as databases or stored files.
</x-callout>
@can('update', $resource)
<div class="space-y-4 pb-8">
@@ -1,18 +1,8 @@
<div>
<x-slot:title>
{{ data_get_str($resource, 'name')->limit(10) }} > Scheduled Tasks | Coolify
</x-slot>
@if ($type === 'application')
<h1>Scheduled Task</h1>
<livewire:project.application.heading :application="$resource" />
@elseif ($type === 'service')
<livewire:project.service.heading :service="$resource" :parameters="$parameters" />
@endif
<form wire:submit="submit" class="w-full">
<div class="flex flex-col gap-2 pb-2">
<div class="flex gap-2 items-end">
<h2>Scheduled Task</h2>
<h2>Task {{ $task->name }}</h2>
<x-forms.button canGate="update" :canResource="$resource" type="submit">
Save
</x-forms.button>
@@ -24,6 +14,11 @@
@endcan
@endif
@can('update', $resource)
@if (!$isEnabled)
<x-forms.button wire:click="toggleEnabled" isHighlighted>Enable Task</x-forms.button>
@else
<x-forms.button wire:click="toggleEnabled">Disable Task</x-forms.button>
@endif
<x-modal-confirmation title="Confirm Scheduled Task Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $task->id }})" :actions="['The selected scheduled task will be permanently deleted.']" confirmationText="{{ $task->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
@@ -31,17 +26,11 @@
step2ButtonText="Permanently Delete" />
@endcan
</div>
<div class="w-48">
@can('update', $resource)
<x-forms.checkbox instantSave id="isEnabled" label="Enabled" />
@else
<x-forms.checkbox disabled id="isEnabled" label="Enabled" />
@endcan
</div>
<h3 class="pt-4">Configuration</h3>
<div class="flex gap-2 w-full">
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="Name" id="name" label="Name" required />
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="php artisan schedule:run" id="command" label="Command" required />
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="0 0 * * * or daily" id="frequency" label="Frequency" required />
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="0 0 * * * or daily" id="frequency" label="Frequency"
helper="You can use every_minute, hourly, daily, weekly, monthly, yearly or a cron expression." required />
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" type="number" placeholder="300" id="timeout"
helper="Maximum execution time in seconds (60-36000)." label="Timeout (seconds)" required />
@if ($type === 'application')
@@ -54,6 +43,7 @@
id="container" label="Service name" />
@endif
</div>
<x-forms.input :disabled="!auth()->user()->can('update', $resource)" placeholder="php artisan schedule:run" id="command" label="Command" required />
</form>
<div class="pt-4">
@@ -38,6 +38,15 @@
<x-forms.input id="mountPath" required readonly />
</div>
@endif
@if (!$isService)
@can('update', $resource)
<div class="w-full sm:w-96">
<x-forms.checkbox instantSave canGate="update" :canResource="$resource" label="Add suffix for PR deployments"
id="isPreviewSuffixEnabled"
helper="When enabled, a -pr-N suffix is added to this volume's name for preview deployments (e.g. myvolume becomes myvolume-pr-1). Disable this for volumes that should be shared between the main and preview deployments."></x-forms.checkbox>
</div>
@endcan
@endif
@else
@can('update', $resource)
@if ($isFirst)
@@ -54,6 +63,13 @@
<x-forms.input id="mountPath" required />
</div>
@endif
@if (!$isService)
<div class="w-full sm:w-96">
<x-forms.checkbox instantSave canGate="update" :canResource="$resource" label="Add suffix for PR deployments"
id="isPreviewSuffixEnabled"
helper="When enabled, a -pr-N suffix is added to this volume's name for preview deployments (e.g. myvolume becomes myvolume-pr-1). Disable this for volumes that should be shared between the main and preview deployments."></x-forms.checkbox>
</div>
@endif
<div class="flex gap-2">
<x-forms.button type="submit">
Update