Merge branch 'next' into shadow/fix-typo-slash-proxy-page

This commit is contained in:
Andras Bacsai
2025-09-22 09:49:59 +02:00
committed by GitHub
30 changed files with 1680 additions and 377 deletions

View File

@@ -6,10 +6,31 @@
@apply hidden!;
}
@utility apexcharts-grid-borders {
@apply dark:hidden!;
}
@utility apexcharts-xaxistooltip {
@apply hidden!;
}
@utility apexcharts-tooltip-custom {
@apply bg-white dark:bg-coolgray-100 border border-neutral-200 dark:border-coolgray-300 rounded-lg shadow-lg p-3 text-sm;
min-width: 160px;
}
@utility apexcharts-tooltip-custom-value {
@apply text-neutral-700 dark:text-neutral-300 mb-1;
}
@utility apexcharts-tooltip-value-bold {
@apply font-bold text-black dark:text-white;
}
@utility apexcharts-tooltip-custom-title {
@apply text-xs text-neutral-500 dark:text-neutral-400 font-medium;
}
@utility input-sticky {
@apply block py-1.5 w-full text-sm text-black rounded-sm border-0 ring-1 ring-inset dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300 focus:ring-2 focus:ring-neutral-400 dark:focus:ring-coolgray-300;
}

View File

@@ -11,6 +11,7 @@
'content' => null,
'checkboxes' => [],
'actions' => [],
'warningMessage' => null,
'confirmWithText' => true,
'confirmationText' => 'Confirm Deletion',
'confirmationLabel' => 'Please confirm the execution of the actions by entering the Name below',
@@ -228,7 +229,7 @@
<div x-show="step === 2">
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error" role="alert">
<p class="font-bold">Warning</p>
<p>This operation is permanent and cannot be undone. Please think again before proceeding!
<p>{!! $warningMessage ?: 'This operation is permanent and cannot be undone. Please think again before proceeding!' !!}
</p>
</div>
<div class="mb-4">The following actions will be performed:</div>

View File

@@ -59,20 +59,20 @@
if (this.zoom === '90') {
const style = document.createElement('style');
style.textContent = `
html {
font-size: 93.75%;
}
:root {
--vh: 1vh;
}
@media (min-width: 1024px) {
html {
font-size: 87.5%;
font-size: 93.75%;
}
}
`;
:root {
--vh: 1vh;
}
@media (min-width: 1024px) {
html {
font-size: 87.5%;
}
}
`;
document.head.appendChild(style);
}
}
@@ -82,6 +82,9 @@
<div class="text-2xl font-bold tracking-wide dark:text-white">Coolify</div>
<x-version />
</div>
<div>
<livewire:global-search />
</div>
<livewire:settings-dropdown />
</div>
<div class="px-2 pt-2 pb-7">

View File

@@ -138,7 +138,8 @@
}
}
let theme = localStorage.theme
let baseColor = '#FCD452'
let cpuColor = '#1e90ff'
let ramColor = '#00ced1'
let textColor = '#ffffff'
let editorBackground = '#181818'
let editorTheme = 'blackboard'
@@ -149,12 +150,14 @@
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
if (theme == 'dark') {
baseColor = '#FCD452'
cpuColor = '#1e90ff'
ramColor = '#00ced1'
textColor = '#ffffff'
editorBackground = '#181818'
editorTheme = 'blackboard'
} else {
baseColor = 'black'
cpuColor = '#1e90ff'
ramColor = '#00ced1'
textColor = '#000000'
editorBackground = '#ffffff'
editorTheme = null

View File

@@ -0,0 +1,236 @@
<div x-data="{
modalOpen: false,
selectedIndex: -1,
openModal() {
this.modalOpen = true;
this.selectedIndex = -1;
@this.openSearchModal();
},
closeModal() {
this.modalOpen = false;
this.selectedIndex = -1;
@this.closeSearchModal();
},
navigateResults(direction) {
const results = document.querySelectorAll('.search-result-item');
if (results.length === 0) return;
if (direction === 'down') {
this.selectedIndex = Math.min(this.selectedIndex + 1, results.length - 1);
} else if (direction === 'up') {
this.selectedIndex = Math.max(this.selectedIndex - 1, -1);
}
if (this.selectedIndex >= 0 && this.selectedIndex < results.length) {
results[this.selectedIndex].focus();
results[this.selectedIndex].scrollIntoView({ block: 'nearest' });
} else if (this.selectedIndex === -1) {
this.$refs.searchInput?.focus();
}
},
init() {
// Listen for / key press globally
document.addEventListener('keydown', (e) => {
if (e.key === '/' && !['INPUT', 'TEXTAREA'].includes(e.target.tagName) && !this.modalOpen) {
e.preventDefault();
this.openModal();
}
});
// Listen for Cmd+K or Ctrl+K globally
document.addEventListener('keydown', (e) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
if (this.modalOpen) {
this.closeModal();
} else {
this.openModal();
}
}
});
// Listen for Escape key to close modal
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.modalOpen) {
this.closeModal();
}
});
// Listen for arrow keys when modal is open
document.addEventListener('keydown', (e) => {
if (!this.modalOpen) return;
if (e.key === 'ArrowDown') {
e.preventDefault();
this.navigateResults('down');
} else if (e.key === 'ArrowUp') {
e.preventDefault();
this.navigateResults('up');
}
});
}
}">
<!-- Search bar in navbar -->
<div class="flex justify-center">
<button @click="openModal()" type="button" title="Search (Press / or ⌘K)"
class="flex items-center gap-1.5 px-2.5 py-1.5 bg-neutral-100 dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-200 rounded-md hover:bg-neutral-200 dark:hover:bg-coolgray-200 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-neutral-500 dark:text-neutral-400" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<kbd
class="px-1 py-0.5 text-xs font-semibold text-neutral-500 dark:text-neutral-400 bg-neutral-200 dark:bg-coolgray-200 rounded">/</kbd>
</button>
</div>
<!-- Modal overlay -->
<template x-teleport="body">
<div x-show="modalOpen" x-cloak
class="fixed top-0 lg:pt-10 left-0 z-99 flex items-start justify-center w-screen h-screen">
<div @click="closeModal()" class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs">
</div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave="ease-in duration-100"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95"
class="relative w-full py-6 border rounded-sm min-w-full lg:min-w-[36rem] max-w-[48rem] bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300"
@click.stop>
<div class="flex justify-between items-center pb-3">
<h3 class="pr-8 text-2xl font-bold">Search</h3>
<button @click="closeModal()"
class="flex absolute top-2 right-2 justify-center items-center w-8 h-8 rounded-full dark:text-white hover:bg-coolgray-300">
<svg class="w-6 h-6" 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="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="relative w-auto">
<input type="text" wire:model.live.debounce.500ms="searchQuery"
placeholder="Type to search for applications, services, databases, and servers..."
x-ref="searchInput" x-init="$watch('modalOpen', value => { if (value) setTimeout(() => $refs.searchInput.focus(), 100) })" class="w-full input mb-4" />
<!-- Search results -->
<div class="relative min-h-[330px] max-h-[400px] overflow-y-auto scrollbar">
<!-- Loading indicator -->
<div wire:loading.flex wire:target="searchQuery"
class="min-h-[330px] items-center justify-center">
<div class="text-center">
<svg class="animate-spin mx-auto h-8 w-8 text-neutral-400"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
<p class="mt-2 text-sm text-neutral-600 dark:text-neutral-400">
Searching...
</p>
</div>
</div>
<!-- Results content - hidden while loading -->
<div wire:loading.remove wire:target="searchQuery">
@if (strlen($searchQuery) >= 2 && count($searchResults) > 0)
<div class="space-y-1 my-4 pb-4">
@foreach ($searchResults as $index => $result)
<a href="{{ $result['link'] ?? '#' }}"
class="search-result-item block p-3 mx-1 hover:bg-neutral-200 dark:hover:bg-coolgray-200 transition-colors focus:outline-none focus:ring-1 focus:ring-coollabs focus:bg-neutral-100 dark:focus:bg-coolgray-200 ">
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="font-medium text-neutral-900 dark:text-white">
{{ $result['name'] }}
</span>
@if ($result['type'] === 'server')
<span
class="px-2 py-0.5 text-xs rounded bg-coolgray-100 text-white">
Server
</span>
@endif
</div>
<div class="flex items-center gap-2">
@if (!empty($result['project']) && !empty($result['environment']))
<span
class="text-xs text-neutral-500 dark:text-neutral-400">
{{ $result['project'] }} / {{ $result['environment'] }}
</span>
@endif
@if ($result['type'] === 'application')
<span
class="px-2 py-0.5 text-xs rounded bg-coolgray-100 text-white">
Application
</span>
@elseif ($result['type'] === 'service')
<span
class="px-2 py-0.5 text-xs rounded bg-coolgray-100 text-white">
Service
</span>
@elseif ($result['type'] === 'database')
<span
class="px-2 py-0.5 text-xs rounded bg-coolgray-100 text-white">
{{ ucfirst($result['subtype'] ?? 'Database') }}
</span>
@endif
</div>
@if (!empty($result['description']))
<div
class="text-sm text-neutral-600 dark:text-neutral-400 mt-0.5">
{{ Str::limit($result['description'], 100) }}
</div>
@endif
</div>
<svg xmlns="http://www.w3.org/2000/svg"
class="shrink-0 ml-2 h-4 w-4 text-neutral-400" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</div>
</a>
@endforeach
</div>
@elseif (strlen($searchQuery) >= 2 && count($searchResults) === 0)
<div class="flex items-center justify-center min-h-[330px]">
<div class="text-center">
<p class="text-sm text-neutral-600 dark:text-neutral-400">
No results found for "<strong>{{ $searchQuery }}</strong>"
</p>
<p class="text-xs text-neutral-500 dark:text-neutral-500 mt-2">
Try different keywords or check the spelling
</p>
</div>
</div>
@elseif (strlen($searchQuery) > 0 && strlen($searchQuery) < 2)
<div class="flex items-center justify-center min-h-[330px]">
<div class="text-center">
<p class="text-sm text-neutral-600 dark:text-neutral-400">
Type at least 2 characters to search
</p>
</div>
</div>
@else
<div class="flex items-center justify-center min-h-[330px]">
<div class="text-center">
<p class="text-sm text-neutral-600 dark:text-neutral-400">
Start typing to search
</p>
<p class="text-xs text-neutral-500 dark:text-neutral-500 mt-2">
Search for applications, services, databases, and servers
</p>
</div>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</div>

View File

@@ -28,19 +28,17 @@
@endcan
</div>
@endif
@if (data_get($resource, 'build_pack') !== 'dockercompose')
<div class="w-64">
@can('manageEnvironment', $resource)
<x-forms.checkbox id="use_build_secrets" label="Use Docker Build Secrets"
helper="Enable Docker BuildKit secrets for enhanced security during builds. Secrets won't be exposed in the final image. Requires Docker 18.09+ with BuildKit support."
instantSave></x-forms.checkbox>
@else
<x-forms.checkbox id="use_build_secrets" label="Use Docker Build Secrets"
helper="Enable Docker BuildKit secrets for enhanced security during builds. Secrets won't be exposed in the final image. Requires Docker 18.09+ with BuildKit support."
disabled></x-forms.checkbox>
@endcan
</div>
@endif
<div class="w-64">
@can('manageEnvironment', $resource)
<x-forms.checkbox id="use_build_secrets" label="Use Docker Build Secrets"
helper="Enable Docker BuildKit secrets for enhanced security during builds. Secrets won't be exposed in the final image. Requires Docker 18.09+ with BuildKit support."
instantSave></x-forms.checkbox>
@else
<x-forms.checkbox id="use_build_secrets" label="Use Docker Build Secrets"
helper="Enable Docker BuildKit secrets for enhanced security during builds. Secrets won't be exposed in the final image. Requires Docker 18.09+ with BuildKit support."
disabled></x-forms.checkbox>
@endcan
</div>
</div>
@endif
@if ($resource->type() === 'service' || $resource?->build_pack === 'dockercompose')

View File

@@ -7,7 +7,7 @@
@if ($isLocked)
<div class="flex flex-1 w-full gap-2">
<x-forms.input disabled id="key" />
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<svg class="icon my-1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M5 13a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6z" />
<path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0-2 0m-3-5V7a4 4 0 1 1 8 0v4" />
@@ -21,6 +21,95 @@
step2ButtonText="Permanently Delete" />
@endcan
</div>
@can('update', $this->env)
<div class="flex flex-col w-full gap-3">
<div class="flex w-full items-center gap-4 overflow-x-auto whitespace-nowrap">
@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" />
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
<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?" />
@else
@if ($is_shared)
<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?" />
@else
@if ($isSharedVariable)
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@else
@if (!$env->is_nixpacks)
<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" />
@endif
<x-forms.checkbox instantSave id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
@if (!$env->is_nixpacks)
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@if ($is_multiline === false)
<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?" />
@endif
@endif
@endif
@endif
@endif
@endif
</div>
</div>
@else
<div class="flex flex-col w-full gap-3">
<div class="flex w-full items-center gap-4 overflow-x-auto whitespace-nowrap">
@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" />
<x-forms.checkbox disabled id="is_multiline" label="Is Multiline?" />
<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?" />
@else
@if ($is_shared)
<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?" />
@else
@if ($isSharedVariable)
<x-forms.checkbox disabled id="is_multiline" label="Is Multiline?" />
@else
<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_multiline" label="Is Multiline?" />
@if ($is_multiline === false)
<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?" />
@endif
@endif
@endif
@endif
@endif
</div>
</div>
@endcan
@else
@can('update', $this->env)
@if ($isDisabled)
@@ -78,22 +167,20 @@
@if ($isSharedVariable)
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@else
@if (!$env->is_coolify)
@if (!$env->is_nixpacks)
<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" />
@endif
<x-forms.checkbox instantSave id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
@if (!$env->is_nixpacks)
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@if ($is_multiline === false)
<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?" />
@endif
@if (!$env->is_nixpacks)
<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" />
@endif
<x-forms.checkbox instantSave id="is_runtime"
helper="Make this variable available in the running container at runtime."
label="Available at Runtime" />
@if (!$env->is_nixpacks)
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@if ($is_multiline === false)
<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?" />
@endif
@endif
@endif
@@ -129,8 +216,8 @@
@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" />
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" />

View File

@@ -1,21 +1,20 @@
<div>
<div class="flex items-center gap-2 ">
<div class="flex items-center gap-2">
<h2>Metrics</h2>
</div>
<div class="pb-4">Basic metrics for your container.</div>
@if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose')
<div class="alert alert-warning">Metrics are not available for Docker Compose applications yet!</div>
@elseif(!$resource->destination->server->isMetricsEnabled())
<div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
<div> Go to <a class="underline dark:text-white"
href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to
enable
it.</div>
@else
@if (!str($resource->status)->contains('running'))
<div class="alert alert-warning">Metrics are only available when this resource is running!</div>
<div class="pb-4">Basic metrics for your application container.</div>
<div>
@if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose')
<div class="alert alert-warning">Metrics are not available for Docker Compose applications yet!</div>
@elseif(!$resource->destination->server->isMetricsEnabled())
<div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
<div>Go to <a class="underline dark:text-white" href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to enable it.</div>
@else
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
@if (!str($resource->status)->contains('running'))
<div class="alert alert-warning">Metrics are only available when the application container is running!</div>
@else
<div>
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
<option value="5">5 minutes (live)</option>
<option value="10">10 minutes (live)</option>
<option value="30">30 minutes</option>
@@ -26,7 +25,7 @@
</x-forms.select>
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()"
class="pt-5">
<h4>CPU (%)</h4>
<h4>CPU Usage</h4>
<div wire:ignore id="{!! $chartId !!}-cpu"></div>
<script>
@@ -34,6 +33,7 @@
const optionsServerCpu = {
stroke: {
curve: 'straight',
width: 2,
},
chart: {
height: '150px',
@@ -52,7 +52,7 @@
},
},
animations: {
enabled: false,
enabled: true,
},
},
fill: {
@@ -68,74 +68,90 @@
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
},
series: [{
name: "CPU %",
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
legend: {
show: false
}
grid: {
show: true,
borderColor: '',
},
colors: [cpuColor],
xaxis: {
type: 'datetime',
},
series: [{
name: "CPU %",
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">CPU: <span class="apexcharts-tooltip-value-bold">' + value + '%</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
}
},
legend: {
show: false
}
}
const serverCpuChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-cpu`), optionsServerCpu);
serverCpuChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => {
checkTheme();
serverCpuChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
noData: {
text: 'Loading...',
style: {
color: textColor,
}
}
});
});
});
const serverCpuChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-cpu`), optionsServerCpu);
serverCpuChart.render();
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => {
checkTheme();
serverCpuChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [cpuColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
show: true,
labels: {
show: true,
style: {
colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' %';
}
}
},
noData: {
text: 'Loading...',
style: {
color: textColor,
}
}
});
});
</script>
<h3>Memory (MB)</h3>
<h4>Memory Usage</h4>
<div wire:ignore id="{!! $chartId !!}-memory"></div>
<script>
@@ -143,6 +159,7 @@
const optionsServerMemory = {
stroke: {
curve: 'straight',
width: 2,
},
chart: {
height: '150px',
@@ -161,7 +178,7 @@
},
},
animations: {
enabled: false,
enabled: true,
},
},
fill: {
@@ -177,81 +194,99 @@
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
series: [{
name: "Memory (MB)",
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
legend: {
show: false
}
grid: {
show: true,
borderColor: '',
},
colors: [ramColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
series: [{
name: "Memory (MB)",
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">Memory: <span class="apexcharts-tooltip-value-bold">' + value + ' MB</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
}
},
legend: {
show: false
}
}
const serverMemoryChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-memory`),
optionsServerMemory);
serverMemoryChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => {
checkTheme();
serverMemoryChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
min: 0,
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
noData: {
text: 'Loading...',
style: {
color: textColor,
}
}
});
});
});
const serverMemoryChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-memory`),
optionsServerMemory);
serverMemoryChart.render();
Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => {
checkTheme();
serverMemoryChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [ramColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
min: 0,
show: true,
labels: {
show: true,
style: {
colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' MB';
}
}
},
noData: {
text: 'Loading...',
style: {
color: textColor,
}
}
});
});
</script>
</div>
</div>
@endif
@endif
</div>
</div>

View File

@@ -7,7 +7,7 @@
<x-server.sidebar :server="$server" activeMenu="metrics" />
<div class="w-full">
<h2>Metrics</h2>
<div class="pb-4">Basic metrics for your container.</div>
<div class="pb-4">Basic metrics for your server.</div>
@if ($server->isMetricsEnabled())
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
@@ -19,7 +19,7 @@
<option value="10080">1 week</option>
<option value="43200">30 days</option>
</x-forms.select>
<h4 class="pt-4">CPU (%)</h4>
<h4 class="pt-4">CPU Usage</h4>
<div wire:ignore id="{!! $chartId !!}-cpu"></div>
<script>
@@ -27,6 +27,7 @@
const optionsServerCpu = {
stroke: {
curve: 'straight',
width: 2,
},
chart: {
height: '150px',
@@ -45,7 +46,7 @@
},
},
animations: {
enabled: false,
enabled: true,
},
},
fill: {
@@ -61,16 +62,16 @@
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
},
series: [{
name: 'CPU %',
grid: {
show: true,
borderColor: '',
},
colors: [cpuColor],
xaxis: {
type: 'datetime',
},
series: [{
name: 'CPU %',
data: []
}],
noData: {
@@ -79,12 +80,27 @@
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">CPU: <span class="apexcharts-tooltip-value-bold">' + value + '%</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
}
},
legend: {
show: false
}
@@ -95,11 +111,11 @@
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => {
checkTheme();
serverCpuChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [baseColor],
serverCpuChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [cpuColor],
xaxis: {
type: 'datetime',
labels: {
@@ -109,15 +125,18 @@
}
}
},
yaxis: {
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
show: true,
labels: {
show: true,
style: {
colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' %';
}
}
},
noData: {
text: 'Loading...',
style: {
@@ -130,7 +149,7 @@
</script>
<div>
<h4>Memory (%)</h4>
<h4>Memory Usage</h4>
<div wire:ignore id="{!! $chartId !!}-memory"></div>
<script>
@@ -138,6 +157,7 @@
const optionsServerMemory = {
stroke: {
curve: 'straight',
width: 2,
},
chart: {
height: '150px',
@@ -156,7 +176,7 @@
},
},
animations: {
enabled: false,
enabled: true,
},
},
fill: {
@@ -172,15 +192,15 @@
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
grid: {
show: true,
borderColor: '',
},
colors: [ramColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
@@ -196,12 +216,27 @@
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
},
custom: function({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
const date = new Date(timestamp);
const timeString = String(date.getUTCHours()).padStart(2, '0') + ':' +
String(date.getUTCMinutes()).padStart(2, '0') + ':' +
String(date.getUTCSeconds()).padStart(2, '0') + ', ' +
date.getUTCFullYear() + '-' +
String(date.getUTCMonth() + 1).padStart(2, '0') + '-' +
String(date.getUTCDate()).padStart(2, '0');
return '<div class="apexcharts-tooltip-custom">' +
'<div class="apexcharts-tooltip-custom-value">Memory: <span class="apexcharts-tooltip-value-bold">' + value + '%</span></div>' +
'<div class="apexcharts-tooltip-custom-title">' + timeString + '</div>' +
'</div>';
}
},
legend: {
show: false
}
@@ -212,11 +247,11 @@
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => {
checkTheme();
serverMemoryChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [baseColor],
serverMemoryChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [ramColor],
xaxis: {
type: 'datetime',
labels: {
@@ -226,16 +261,19 @@
}
}
},
yaxis: {
min: 0,
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
min: 0,
show: true,
labels: {
show: true,
style: {
colors: textColor,
},
formatter: function(value) {
return Math.round(value) + ' %';
}
}
},
noData: {
text: 'Loading...',
style: {

View File

@@ -4,26 +4,26 @@
<div x-init="$wire.loadProxyConfiguration">
@if ($selectedProxy !== 'NONE')
<form wire:submit='submit'>
<div class="flex items-center gap-2">
<h2>Configuration</h2>
@if ($server->proxy->status === 'exited' || $server->proxy->status === 'removing')
<x-forms.button canGate="update" :canResource="$server" wire:click.prevent="changeProxy">Switch
Proxy</x-forms.button>
@else
<x-forms.button canGate="update" :canResource="$server" disabled
wire:click.prevent="changeProxy">Switch Proxy</x-forms.button>
@endif
<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">
<path fill="currentColor"
d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16" />
</svg>Before switching proxies, please read <a class="underline dark:text-white"
href="https://coolify.io/docs/knowledge-base/server/proxies#switch-between-proxies">this</a>.
</div>
<h3>Advanced</h3>
<div class="pb-4 w-96">
<div class="flex items-center gap-2">
<h2>Configuration</h2>
@if ($server->proxy->status === 'exited' || $server->proxy->status === 'removing')
@can('update', $server)
<x-modal-confirmation title="Confirm Proxy Switching?"
buttonTitle="Switch Proxy"
submitAction="changeProxy" :actions="[
'Custom proxy configurations may be reset to their default settings.'
]" warningMessage="This operation may cause issues. Please refer to the guide <a href='https://coolify.io/docs/knowledge-base/server/proxies#switch-between-proxies' target='_blank' class='underline text-white'>switching between proxies</a> before proceeding!" step2ButtonText="Switch Proxy" :confirmWithText="false" :confirmWithPassword="false">
</x-modal-confirmation>
@endcan
@else
<x-forms.button canGate="update" :canResource="$server"
wire:click="$dispatch('error', 'Currently running proxy must be stopped before switching proxy')">Switch Proxy</x-forms.button>
@endif
<x-forms.button canGate="update" :canResource="$server" type="submit">Save</x-forms.button>
</div>
<div class="subtitle">Configure your proxy settings and advanced options.</div>
<h3>Advanced</h3>
<div class="pb-6 w-96">
<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"
@@ -36,11 +36,30 @@
id="redirectUrl" label="Redirect to (optional)" />
@endif
</div>
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value)
<h3>Traefik</h3>
@elseif ($server->proxyType() === 'CADDY')
<h3>Caddy</h3>
@endif
@php
$proxyTitle = $server->proxyType() === ProxyTypes::TRAEFIK->value ? 'Traefik (Coolify Proxy)' : 'Caddy (Coolify Proxy)';
@endphp
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value || $server->proxyType() === 'CADDY')
<div class="flex items-center gap-2">
<h3>{{ $proxyTitle }}</h3>
@if($proxySettings)
@can('update', $server)
<x-modal-confirmation title="Reset Proxy Configuration?"
buttonTitle="Reset Configuration"
submitAction="resetProxyConfiguration" :actions="[
'Reset proxy configuration to default settings',
'All custom configurations will be lost',
'Custom ports and entrypoints will be removed',
]"
confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm by entering the server name below"
shortConfirmationLabel="Server Name" step2ButtonText="Reset Configuration"
:confirmWithPassword="false" :confirmWithText="true">
</x-modal-confirmation>
@endcan
@endif
</div>
@endif
@if (
$server->proxy->last_applied_settings &&
$server->proxy->last_saved_settings !== $server->proxy->last_applied_settings)
@@ -73,6 +92,12 @@
</x-modal-confirmation>
@endcan
</div>
<div class="flex flex-col gap-2 pt-2">
<x-forms.textarea canGate="update" :canResource="$server" useMonacoEditor
monacoEditorLanguage="yaml"
label="Configuration file ({{ $this->configurationFilePath }})" name="proxySettings"
id="proxySettings" rows="30" />
</div>
@endif
</div>
</form>