mirror of
https://github.com/coollabsio/coolify.git
synced 2026-08-24 10:05:47 -05:00
Merge remote-tracking branch 'origin/next' into remove-local-volume-source
This commit is contained in:
@@ -2121,6 +2121,36 @@ input[type="search"]::-webkit-search-results-decoration {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.validation-installation-logs {
|
||||
border: 1px solid var(--coollabs-fill);
|
||||
}
|
||||
|
||||
.checkpoint-scroll-fade::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 2rem;
|
||||
background: linear-gradient(to left, var(--coollabs-base), transparent);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.process-dialog-mobile-fullscreen {
|
||||
height: 100dvh !important;
|
||||
min-height: 100dvh;
|
||||
max-height: 100dvh;
|
||||
border-radius: 0;
|
||||
box-shadow: inset 0 0 0 1px var(--coollabs-hairline) !important;
|
||||
}
|
||||
|
||||
.process-dialog-mobile-fullscreen .process-dialog-body {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Data table (layer-card body, full-bleed) */
|
||||
.data-table-header {
|
||||
display: grid;
|
||||
|
||||
@@ -38,11 +38,11 @@
|
||||
this.visible = true;
|
||||
const rect = target.getBoundingClientRect();
|
||||
this.below = rect.top < 48;
|
||||
this.x = rect.left;
|
||||
this.x = rect.left + rect.width / 2;
|
||||
this.y = this.below ? rect.bottom + 8 : rect.top - 8;
|
||||
this.$nextTick(() => {
|
||||
const width = this.$refs.tooltip?.offsetWidth || 0;
|
||||
this.x = Math.max(8, Math.min(window.innerWidth - width - 8, this.x));
|
||||
this.x = Math.max(8, Math.min(window.innerWidth - width - 8, this.x - width / 2));
|
||||
this.$nextTick(() => this.positioned = true);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@props([
|
||||
'closeWithX' => false,
|
||||
'mobileFullscreen' => false,
|
||||
'open' => false,
|
||||
'size' => 'lg',
|
||||
])
|
||||
@@ -40,7 +41,11 @@
|
||||
|
||||
<div class="fixed inset-0 overflow-y-auto">
|
||||
<div @if (! $closeWithX) @click.self="processDialogOpen = false" @endif
|
||||
class="flex min-h-full items-center justify-center p-4 sm:p-6">
|
||||
@class([
|
||||
'flex min-h-full items-center justify-center',
|
||||
'p-4 sm:p-6' => ! $mobileFullscreen,
|
||||
'p-0 sm:p-6' => $mobileFullscreen,
|
||||
])>
|
||||
<div x-show="processDialogOpen"
|
||||
x-trap.inert.noscroll="processDialogOpen"
|
||||
x-transition:enter="ease-out duration-150"
|
||||
@@ -54,6 +59,7 @@
|
||||
aria-labelledby="process-dialog-title"
|
||||
@class([
|
||||
'application-settings-section application-settings-form process-dialog relative flex flex-col overflow-hidden',
|
||||
'process-dialog-mobile-fullscreen' => $mobileFullscreen,
|
||||
$panelWidth,
|
||||
// Fixed shell size so empty “waiting for process” state does not collapse.
|
||||
'min-h-[min(70dvh,28rem)] h-[min(85dvh,52rem)] max-h-[calc(100dvh-2rem)]',
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
'icon' => 'shield-star',
|
||||
'group' => 'Platform',
|
||||
'visible' => $server->isFunctional() && ! $server->isSwarm() && ! $server->settings->is_build_server && auth()->user()?->can('viewSentinel', $server),
|
||||
'warning' => $server->isSentinelEnabled() && ! $server->isSentinelLive(),
|
||||
'children' => [
|
||||
['label' => 'Configuration', 'route' => 'server.sentinel', 'active' => request()->routeIs('server.sentinel'), 'icon' => 'settings'],
|
||||
['label' => 'Logs', 'route' => 'server.sentinel.logs', 'active' => request()->routeIs('server.sentinel.logs'), 'icon' => 'file-content'],
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
@php
|
||||
$serverReady = $server->isFunctional();
|
||||
$proxyNeedsAttention = $server->proxySet() && ! in_array($proxyStatus, ['running'], true);
|
||||
$proxyUpdateAvailable = $server->proxySet()
|
||||
&& ($server->hasCurrentTraefikOutdatedInfo() || $server->hasPendingProxyConfiguration());
|
||||
$proxyNeedsAttention = $server->proxySet()
|
||||
&& (! in_array($proxyStatus, ['running'], true) || $proxyUpdateAvailable);
|
||||
$sentinelNeedsAttention = $showSentinelStatus && ! $server->isSentinelLive();
|
||||
|
||||
[$summaryLabel, $summaryType] = match (true) {
|
||||
@@ -58,9 +61,9 @@
|
||||
class="listbox-option gap-2.5!" @click="open = false" role="menuitem">
|
||||
<span @class([
|
||||
'size-1.5 shrink-0 rounded-full',
|
||||
'bg-success' => $proxyStatus === 'running',
|
||||
'bg-warning' => in_array($proxyStatus, ['starting', 'restarting', 'stopping'], true),
|
||||
'bg-error' => ! in_array($proxyStatus, ['running', 'starting', 'restarting', 'stopping'], true),
|
||||
'bg-success' => $proxyStatus === 'running' && ! $proxyUpdateAvailable,
|
||||
'bg-warning' => $proxyNeedsAttention && ($proxyUpdateAvailable || in_array($proxyStatus, ['starting', 'restarting', 'stopping'], true)),
|
||||
'bg-error' => $proxyNeedsAttention && ! $proxyUpdateAvailable && ! in_array($proxyStatus, ['starting', 'restarting', 'stopping'], true),
|
||||
])></span>
|
||||
<span class="flex-1">Proxy</span>
|
||||
<span>{{ str($proxyStatus ?: 'unknown')->headline() }}</span>
|
||||
@@ -69,7 +72,11 @@
|
||||
@if ($showSentinelStatus)
|
||||
<a href="{{ route('server.sentinel', ['server_uuid' => $server->uuid]) }}" {{ wireNavigate() }}
|
||||
class="listbox-option gap-2.5!" @click="open = false" role="menuitem">
|
||||
<span class="size-1.5 shrink-0 rounded-full {{ $server->isSentinelLive() ? 'bg-success' : 'bg-error' }}"></span>
|
||||
<span @class([
|
||||
'size-1.5 shrink-0 rounded-full',
|
||||
'bg-success' => ! $sentinelNeedsAttention,
|
||||
'bg-warning' => $sentinelNeedsAttention,
|
||||
])></span>
|
||||
<span class="flex-1">Sentinel</span>
|
||||
<span>{{ $server->isSentinelLive() ? 'In sync' : 'Out of sync' }}</span>
|
||||
</a>
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
themeColor: localStorage.getItem('themeColor') || '#6b16ed',
|
||||
themeColorFrame: null,
|
||||
avatarUrl: @js($user?->avatar_path ? route('profile.avatar', ['v' => $user->updated_at->timestamp]) : null),
|
||||
openPanel() {
|
||||
this.appearanceOpen = false;
|
||||
this.open = true;
|
||||
},
|
||||
closePanel() {
|
||||
this.open = false;
|
||||
},
|
||||
setTheme(type, closeMenu = true) {
|
||||
this.theme = type;
|
||||
localStorage.setItem('theme', type);
|
||||
|
||||
if (closeMenu) {
|
||||
this.appearanceOpen = false;
|
||||
this.open = false;
|
||||
this.closePanel();
|
||||
}
|
||||
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
@@ -62,9 +68,9 @@
|
||||
localStorage.setItem('themeColor', color);
|
||||
localStorage.setItem('theme', 'custom');
|
||||
},
|
||||
}" @avatar-updated.window="avatarUrl = $event.detail.url" @keydown.escape.window="open = false; appearanceOpen = false"
|
||||
@click.outside="open = false; appearanceOpen = false">
|
||||
<button type="button" @click="open = !open"
|
||||
}" @avatar-updated.window="avatarUrl = $event.detail.url" @keydown.escape.window="closePanel()"
|
||||
@click.outside="closePanel()">
|
||||
<button type="button" @click="open ? closePanel() : openPanel()"
|
||||
title="{{ $userName }}" aria-label="Account menu for {{ $userName }}"
|
||||
@if ($sidebar) :class="collapsed && 'w-8 justify-center px-0'" @endif
|
||||
@class([
|
||||
@@ -188,7 +194,7 @@
|
||||
</a>
|
||||
<x-modal-input title="How can we help?">
|
||||
<x-slot:content>
|
||||
<div class="listbox-option cursor-pointer" @click="open = false">
|
||||
<div class="listbox-option cursor-pointer" @click="closePanel()">
|
||||
<span class="flex items-center gap-2">
|
||||
<x-reicon name="feedback" class="size-4 opacity-80" />
|
||||
Feedback
|
||||
|
||||
@@ -124,6 +124,9 @@
|
||||
<div id="server-timing-hud-slot-mobile" data-server-timing-hud-slot
|
||||
class="hidden shrink-0 items-center"></div>
|
||||
<div id="configuration-warning-hud-slot-mobile" class="relative shrink-0"></div>
|
||||
@if (isInstanceAdmin() && !isCloud())
|
||||
<livewire:upgrade key="mobile-upgrade" />
|
||||
@endif
|
||||
<x-top-user-menu />
|
||||
<button type="button" class="-m-1 p-2 text-neutral-500 dark:text-fg-dim" x-on:click="open = !open">
|
||||
<span class="sr-only">Open sidebar</span>
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
<div class="grid min-w-0 grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
||||
@foreach ($dashboardServers as $server)
|
||||
@php
|
||||
$proxyNeedsAttention = $server->proxySet() && $server->proxy->status !== 'running';
|
||||
$proxyNeedsAttention = $server->proxySet() && ($server->proxy->status !== 'running' || $server->hasCurrentTraefikOutdatedInfo());
|
||||
$sentinelNeedsAttention = $server->isSentinelEnabled() && ! $server->isSentinelLive();
|
||||
|
||||
[$serverStatus, $serverStatusType] = match (true) {
|
||||
|
||||
@@ -113,6 +113,10 @@
|
||||
@endcan
|
||||
<x-forms.input canGate="update" :canResource="$settings" id="smtpTimeout" type="number"
|
||||
helper="Timeout value for sending emails." label="Timeout" />
|
||||
<x-forms.input canGate="update" :canResource="$settings" id="smtpEhloDomain"
|
||||
placeholder="coolify.example.com"
|
||||
helper="Fully qualified domain sent in the SMTP EHLO command. Uses the system default when empty."
|
||||
label="EHLO domain" />
|
||||
</div>
|
||||
</x-application.settings-section>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
&& $server->settings->is_usable
|
||||
&& ! $server->settings->force_disabled
|
||||
&& ! $isTransferredAway;
|
||||
$proxyNeedsAttention = $isReady && $server->proxySet() && $server->proxy->status !== 'running';
|
||||
$proxyNeedsAttention = $isReady && $server->proxySet()
|
||||
&& ($server->proxy->status !== 'running' || $server->hasCurrentTraefikOutdatedInfo());
|
||||
$sentinelNeedsAttention = $isReady && $server->isSentinelEnabled() && ! $server->isSentinelLive();
|
||||
|
||||
$status = match (true) {
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<x-process-dialog closeWithX size="xl" :open="$isValidating">
|
||||
<x-process-dialog closeWithX mobileFullscreen size="xl" :open="$isValidating">
|
||||
<x-slot:title>Validate and configure</x-slot:title>
|
||||
<x-slot:content>
|
||||
<livewire:server.validate-and-install :server="$server"
|
||||
|
||||
@@ -84,15 +84,26 @@
|
||||
</x-forms.button>
|
||||
@else
|
||||
<div data-validation-checkpoints
|
||||
class="overflow-hidden rounded-[10px] border border-neutral-200 dark:border-white/[0.08]">
|
||||
class="shrink-0 overflow-hidden rounded-[10px] border border-neutral-200 dark:border-white/[0.08]">
|
||||
<div class="border-b border-neutral-200 px-4 py-2.5 dark:border-white/[0.08]">
|
||||
<h3 class="text-[13px] font-medium text-neutral-600 dark:text-fg-dim">Validation checkpoints</h3>
|
||||
</div>
|
||||
<div class="divide-y divide-neutral-200 dark:divide-white/[0.07]">
|
||||
@foreach ($checkpoints as $checkpoint)
|
||||
<x-checkpoint-item :title="$checkpoint['title']" :description="$checkpoint['description']"
|
||||
:status="$checkpoint['status']" />
|
||||
@endforeach
|
||||
<div class="checkpoint-scroll-fade relative min-w-0" x-data="{
|
||||
observer: null,
|
||||
scrollToRunning() {
|
||||
this.$refs.track.querySelector('[data-checkpoint-status=running]')?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
|
||||
}
|
||||
}"
|
||||
x-init="$nextTick(() => scrollToRunning()); observer = new MutationObserver(() => $nextTick(() => scrollToRunning())); observer.observe($refs.track, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-checkpoint-status'] })"
|
||||
x-destroy="observer?.disconnect()">
|
||||
<div class="flex min-w-0 snap-x snap-mandatory overflow-x-auto overscroll-x-contain scroll-smooth scrollbar divide-x divide-neutral-200 dark:divide-white/[0.07]"
|
||||
x-ref="track">
|
||||
@foreach ($checkpoints as $checkpoint)
|
||||
<x-checkpoint-item :title="$checkpoint['title']" :description="$checkpoint['description']"
|
||||
:status="$checkpoint['status']" data-checkpoint-status="{{ $checkpoint['status'] }}"
|
||||
class="basis-[88%] shrink-0 snap-start sm:basis-72 lg:basis-80" />
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -107,7 +118,7 @@
|
||||
</x-forms.button>
|
||||
</div>
|
||||
@elseif ($isInstalling)
|
||||
<section class="application-settings-section">
|
||||
<section class="application-settings-section validation-installation-logs">
|
||||
<div class="application-settings-section-body">
|
||||
<livewire:activity-monitor :header="$installationStep.' installation logs'" :showWaiting="false" />
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{{-- One bar for the whole page. Three stacked bars made Save run
|
||||
submitResend(), which required an API key even when Resend was off. --}}
|
||||
<x-unsaved-bar action="submit"
|
||||
targets="smtpFromName,smtpFromAddress,smtpHost,smtpPort,smtpEncryption,smtpUsername,smtpPassword,smtpTimeout,resendApiKey" />
|
||||
targets="smtpFromName,smtpFromAddress,smtpHost,smtpPort,smtpEncryption,smtpUsername,smtpPassword,smtpTimeout,smtpEhloDomain,resendApiKey" />
|
||||
|
||||
<form wire:submit="submit">
|
||||
<x-application.settings-section title="Sender">
|
||||
@@ -48,6 +48,9 @@
|
||||
autocomplete="new-password" />
|
||||
<x-forms.input id="smtpTimeout" type="number"
|
||||
helper="Maximum delivery time in seconds." label="Timeout" />
|
||||
<x-forms.input id="smtpEhloDomain" placeholder="coolify.example.com"
|
||||
helper="Fully qualified domain sent in the SMTP EHLO command. Uses the system default when empty."
|
||||
label="EHLO domain" />
|
||||
</div>
|
||||
</x-application.settings-section>
|
||||
</form>
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
{{-- Exclude is_auto_update_enabled (instantSave) so the bar does not flash. --}}
|
||||
<x-unsaved-bar action="submit" targets="update_check_frequency,auto_update_frequency" />
|
||||
|
||||
<x-application.settings-section title="Update Coolify"
|
||||
helper="Install the latest Coolify version manually when an update is available.">
|
||||
<livewire:upgrade :full-button="true" key="settings-upgrade" />
|
||||
</x-application.settings-section>
|
||||
|
||||
<x-application.settings-section title="Update checks">
|
||||
<x-slot:actions>
|
||||
<x-forms.button type="button" wire:click="checkManually">
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
Teams
|
||||
</div>
|
||||
@foreach (auth()->user()->teams as $team)
|
||||
<button type="button" wire:click="switch_to({{ $team->id }})" @click="open = false"
|
||||
<button type="button" wire:click="switch_to({{ $team->id }}, window.location.href)" @click="open = false"
|
||||
class="listbox-option {{ $team->id === $currentTeam->id ? 'bg-neutral-100 font-medium dark:bg-white/[0.06]' : '' }}">
|
||||
<span class="min-w-0 flex-1 truncate">{{ $team->name }}</span>
|
||||
</button>
|
||||
@@ -66,7 +66,7 @@
|
||||
Teams
|
||||
</div>
|
||||
@foreach (auth()->user()->teams as $team)
|
||||
<button type="button" wire:click="switch_to({{ $team->id }})" @click="teamOpen = false"
|
||||
<button type="button" wire:click="switch_to({{ $team->id }}, window.location.href)" @click="teamOpen = false"
|
||||
class="listbox-option {{ $team->id === $currentTeam->id ? 'bg-neutral-100 font-medium dark:bg-white/[0.06]' : '' }}">
|
||||
<span class="min-w-0 flex-1 truncate">{{ $team->name }}</span>
|
||||
</button>
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
})">
|
||||
@if ($isUpgradeAvailable)
|
||||
<div :class="{ 'z-40': modalOpen }" class="relative w-auto h-auto">
|
||||
@if ($fullButton)
|
||||
<x-forms.button type="button" @click="modalOpen=true" x-show="!showProgress" x-cloak isHighlighted>
|
||||
Upgrade now
|
||||
</x-forms.button>
|
||||
<x-forms.button type="button" @click="modalOpen=true" x-show="showProgress" x-cloak isHighlighted>
|
||||
Updating…
|
||||
</x-forms.button>
|
||||
@else
|
||||
<button type="button" title="Upgrade in progress" aria-label="Upgrade in progress"
|
||||
@click="modalOpen=true" x-show="showProgress" x-cloak
|
||||
class="inline-flex h-[18px] cursor-pointer items-center rounded-full bg-coollabs/10 px-1.5 text-[9.5px] font-semibold leading-none text-coollabs ring-1 ring-inset ring-coollabs/25 transition-colors hover:bg-coollabs/15 dark:bg-warning/15 dark:text-warning dark:ring-warning/25 dark:hover:bg-warning/20">
|
||||
@@ -16,6 +24,7 @@
|
||||
class="inline-flex h-[18px] cursor-pointer items-center rounded-full bg-coollabs/10 px-1.5 text-[9.5px] font-semibold leading-none text-coollabs ring-1 ring-inset ring-coollabs/25 transition-colors hover:bg-coollabs/15 dark:bg-warning/15 dark:text-warning dark:ring-warning/25 dark:hover:bg-warning/20">
|
||||
Update available
|
||||
</button>
|
||||
@endif
|
||||
<template x-teleport="body">
|
||||
<div x-show="modalOpen"
|
||||
class="fixed inset-0 z-99 flex min-h-full items-center justify-center overflow-y-auto p-4"
|
||||
@@ -162,6 +171,8 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@elseif ($fullButton)
|
||||
<p class="text-sm text-neutral-600 dark:text-fg-dim">Coolify is up to date.</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user