From efd3a360d0f8947fdf7b29bc12738fd8bd8d6db6 Mon Sep 17 00:00:00 2001 From: aaryan359 Date: Tue, 12 Aug 2025 22:05:26 +0530 Subject: [PATCH 01/67] fix the ui for breadcrumbing --- .../resources/breadcrumbs.blade.php | 264 ++++++++++++++++-- 1 file changed, 235 insertions(+), 29 deletions(-) diff --git a/resources/views/components/resources/breadcrumbs.blade.php b/resources/views/components/resources/breadcrumbs.blade.php index 5f7029fd0..78f3f0ba1 100644 --- a/resources/views/components/resources/breadcrumbs.blade.php +++ b/resources/views/components/resources/breadcrumbs.blade.php @@ -5,45 +5,242 @@ ]) + + From 7af5d7683ab12f5aebbc7bb4a6c833362f1cb0fc Mon Sep 17 00:00:00 2001 From: aaryan359 Date: Sat, 23 Aug 2025 17:07:29 +0530 Subject: [PATCH 02/67] fix hover area and app level margin --- .../views/components/resources/breadcrumbs.blade.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/views/components/resources/breadcrumbs.blade.php b/resources/views/components/resources/breadcrumbs.blade.php index 78f3f0ba1..4d047c477 100644 --- a/resources/views/components/resources/breadcrumbs.blade.php +++ b/resources/views/components/resources/breadcrumbs.blade.php @@ -13,7 +13,7 @@ {{ data_get($resource, 'environment.project.name', 'Undefined Name') }} -
-

Are you sure you would like to upgrade your instance to {{ $latestVersion }}?

-
- -

Any deployments running during the update process will - fail. Please ensure no deployments are in progress on any server before continuing. -

-
-
-

You can review the changelogs here.

-
-

If something goes wrong and you cannot upgrade your instance, You can check the following - guide on what to do. -

-
-

Progress

-
-
+ {{-- Content --}} +
+ {{-- Progress View --}} + + + {{-- Confirmation View --}} +
+ + {{-- Footer Actions --}}
Cancel
- Continue + + Upgrade Now
@@ -89,23 +185,57 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel From 0aa7e376b29b7b912dc771a2c237a8cd8c65d7eb Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:41:14 +0100 Subject: [PATCH 44/67] Simplify upgrade modal and improve help text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove changelog preview section to streamline the UI - Simplify warning message - Add reference to upgrade logs location on server - Minor formatting improvements ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- resources/views/livewire/upgrade.blade.php | 87 ++++++++-------------- 1 file changed, 29 insertions(+), 58 deletions(-) diff --git a/resources/views/livewire/upgrade.blade.php b/resources/views/livewire/upgrade.blade.php index 101c4cf94..eedadbb56 100644 --- a/resources/views/livewire/upgrade.blade.php +++ b/resources/views/livewire/upgrade.blade.php @@ -8,17 +8,15 @@ @@ -81,14 +80,21 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
@@ -99,7 +105,8 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 @@ -168,6 +188,7 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel elapsedTime: 0, currentStep: 0, upgradeComplete: false, + upgradeError: false, successCountdown: 3, currentVersion: config.currentVersion || '', latestVersion: config.latestVersion || '', @@ -178,12 +199,16 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel this.currentStep = 1; this.currentStatus = 'Starting upgrade...'; this.startTimer(); + // Trigger server-side upgrade script via Livewire this.$wire.$call('upgrade'); + // Start client-side status polling this.upgrade(); - window.addEventListener('beforeunload', (event) => { + // Prevent accidental navigation during upgrade + this.beforeUnloadHandler = (event) => { event.preventDefault(); event.returnValue = ''; - }); + }; + window.addEventListener('beforeunload', this.beforeUnloadHandler); }, startTimer() { @@ -259,6 +284,11 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel clearInterval(this.elapsedInterval); this.elapsedInterval = null; } + // Remove beforeunload handler now that upgrade is complete + if (this.beforeUnloadHandler) { + window.removeEventListener('beforeunload', this.beforeUnloadHandler); + this.beforeUnloadHandler = null; + } this.upgradeComplete = true; this.currentStep = 5; @@ -278,6 +308,38 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel window.location.reload(); }, + showError(message) { + // Stop all intervals + if (this.checkHealthInterval) { + clearInterval(this.checkHealthInterval); + this.checkHealthInterval = null; + } + if (this.checkUpgradeStatusInterval) { + clearInterval(this.checkUpgradeStatusInterval); + this.checkUpgradeStatusInterval = null; + } + if (this.elapsedInterval) { + clearInterval(this.elapsedInterval); + this.elapsedInterval = null; + } + // Remove beforeunload handler so user can close modal + if (this.beforeUnloadHandler) { + window.removeEventListener('beforeunload', this.beforeUnloadHandler); + this.beforeUnloadHandler = null; + } + + this.upgradeError = true; + this.currentStatus = `Error: ${message}`; + }, + + closeErrorModal() { + this.modalOpen = false; + this.showProgress = false; + this.upgradeError = false; + this.currentStatus = ''; + this.currentStep = 0; + }, + upgrade() { if (this.checkUpgradeStatusInterval) return true; this.currentStep = 1; @@ -294,7 +356,7 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel } else if (data.status === 'complete') { this.showSuccess(); } else if (data.status === 'error') { - this.currentStatus = `Error: ${data.message}`; + this.showError(data.message); } } catch (error) { // Service is down - switch to health check mode diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 8ade89669..ad3f32009 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -244,7 +244,7 @@ nohup bash -c " else log 'Using standard docker-compose configuration' log 'Running docker compose up...' - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 fi log 'Docker compose up completed' From c6945c86eadefd95e676ca22ccb1953180882695 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:12:33 +0100 Subject: [PATCH 55/67] Parse Docker images dynamically from docker-compose files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded image pulls (postgres:15-alpine, redis:7-alpine, coolify-realtime:1.0.10) with dynamic extraction using `docker compose config --images`. This ensures the upgrade script automatically handles new services and version changes without manual updates. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- scripts/upgrade.sh | 99 +++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index ad3f32009..4259aded2 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -59,6 +59,30 @@ curl -fsSL -L $CDN/.env.production -o /data/coolify/source/.env.production log "Configuration files downloaded successfully" echo " Done." +# Extract all images from docker-compose configuration +log "Extracting all images from docker-compose configuration..." +COMPOSE_FILES="-f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml" + +# Check if custom compose file exists +if [ -f /data/coolify/source/docker-compose.custom.yml ]; then + COMPOSE_FILES="$COMPOSE_FILES -f /data/coolify/source/docker-compose.custom.yml" + log "Including custom docker-compose.yml in image extraction" +fi + +# Get all unique images from docker compose config +# LATEST_IMAGE env var is needed for image substitution in compose files +IMAGES=$(LATEST_IMAGE=${LATEST_IMAGE} docker compose --env-file "$ENV_FILE" $COMPOSE_FILES config --images 2>/dev/null | sort -u) + +if [ -z "$IMAGES" ]; then + log "ERROR: Failed to extract images from docker-compose files" + write_status "error" "Failed to parse docker-compose configuration" + echo " ERROR: Failed to parse docker-compose configuration. Aborting upgrade." + exit 1 +fi + +log "Images to pull:" +echo "$IMAGES" | while read img; do log " - $img"; done + # Backup existing .env file before making any changes if [ "$SKIP_BACKUP" != "true" ]; then if [ -f "$ENV_FILE" ]; then @@ -130,60 +154,35 @@ echo "" echo "3/6 Pulling Docker images..." echo " This may take a few minutes depending on your connection." -echo " - Pulling Coolify image..." -log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" -if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1; then - log "Successfully pulled Coolify image" +# Also pull the helper image (not in compose files but needed for upgrade) +HELPER_IMAGE="${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" +echo " - Pulling $HELPER_IMAGE..." +log "Pulling image: $HELPER_IMAGE" +if docker pull "$HELPER_IMAGE" >>"$LOGFILE" 2>&1; then + log "Successfully pulled $HELPER_IMAGE" else - log "ERROR: Failed to pull Coolify image" - write_status "error" "Failed to pull Coolify image" - echo " ERROR: Failed to pull Coolify image. Aborting upgrade." + log "ERROR: Failed to pull $HELPER_IMAGE" + write_status "error" "Failed to pull $HELPER_IMAGE" + echo " ERROR: Failed to pull $HELPER_IMAGE. Aborting upgrade." exit 1 fi -echo " - Pulling Coolify helper image..." -log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" -if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1; then - log "Successfully pulled Coolify helper image" -else - log "ERROR: Failed to pull Coolify helper image" - write_status "error" "Failed to pull Coolify helper image" - echo " ERROR: Failed to pull helper image. Aborting upgrade." - exit 1 -fi - -echo " - Pulling PostgreSQL image..." -log "Pulling image: postgres:15-alpine" -if docker pull postgres:15-alpine >>"$LOGFILE" 2>&1; then - log "Successfully pulled PostgreSQL image" -else - log "ERROR: Failed to pull PostgreSQL image" - write_status "error" "Failed to pull PostgreSQL image" - echo " ERROR: Failed to pull PostgreSQL image. Aborting upgrade." - exit 1 -fi - -echo " - Pulling Redis image..." -log "Pulling image: redis:7-alpine" -if docker pull redis:7-alpine >>"$LOGFILE" 2>&1; then - log "Successfully pulled Redis image" -else - log "ERROR: Failed to pull Redis image" - write_status "error" "Failed to pull Redis image" - echo " ERROR: Failed to pull Redis image. Aborting upgrade." - exit 1 -fi - -echo " - Pulling Coolify realtime image..." -log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" -if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1; then - log "Successfully pulled Coolify realtime image" -else - log "ERROR: Failed to pull Coolify realtime image" - write_status "error" "Failed to pull Coolify realtime image" - echo " ERROR: Failed to pull realtime image. Aborting upgrade." - exit 1 -fi +# Pull all images from compose config +# Using a for loop to avoid subshell issues with exit +for IMAGE in $IMAGES; do + if [ -n "$IMAGE" ]; then + echo " - Pulling $IMAGE..." + log "Pulling image: $IMAGE" + if docker pull "$IMAGE" >>"$LOGFILE" 2>&1; then + log "Successfully pulled $IMAGE" + else + log "ERROR: Failed to pull $IMAGE" + write_status "error" "Failed to pull $IMAGE" + echo " ERROR: Failed to pull $IMAGE. Aborting upgrade." + exit 1 + fi + fi +done log "All images pulled successfully" echo " All images pulled successfully." From 918959b7f5a3de05732fa55a455697ce171845f2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:26:31 +0100 Subject: [PATCH 56/67] Remove unused ChangelogService import from Upgrade component --- app/Livewire/Upgrade.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php index 37cc8ec28..36bee2a23 100644 --- a/app/Livewire/Upgrade.php +++ b/app/Livewire/Upgrade.php @@ -5,7 +5,6 @@ use App\Actions\Server\UpdateCoolify; use App\Models\InstanceSettings; use App\Models\Server; -use App\Services\ChangelogService; use Livewire\Component; class Upgrade extends Component From 6fe4ebeb7e2d0887e6a0c9997fa25c810d2fa8bc Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sun, 14 Dec 2025 10:22:11 +0100 Subject: [PATCH 57/67] Refactor docker run commands in upgrade script to remove project name specification --- scripts/upgrade.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 4259aded2..648849d5c 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -239,11 +239,11 @@ nohup bash -c " if [ -f /data/coolify/source/docker-compose.custom.yml ]; then log 'Using custom docker-compose.yml' log 'Running docker compose up with custom configuration...' - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 else log 'Using standard docker-compose configuration' log 'Running docker compose up...' - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 fi log 'Docker compose up completed' From 8d20d509374564d7436635ba555d55c8b41f74bc Mon Sep 17 00:00:00 2001 From: Kyle Essenmacher Date: Sun, 14 Dec 2025 14:07:29 -0500 Subject: [PATCH 58/67] feat: add Escape key support to exit fullscreen logs view --- .../project/shared/get-logs.blade.php | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 8504a160f..ae0896d7a 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -1,22 +1,28 @@
-
isScrolling: false, toggleScroll() { this.alwaysScroll = !this.alwaysScroll; @@ -375,4 +381,4 @@ class="font-mono whitespace-pre-wrap break-all max-w-full text-neutral-400">No l
-
\ No newline at end of file + From f0dc00f2074e7e3ebbf370430a58e46a2a6f3ad2 Mon Sep 17 00:00:00 2001 From: Lukas <20421914+lumoe@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:24:43 +0100 Subject: [PATCH 59/67] Add STORE_MODEL_IN_DB to env variables --- templates/compose/litellm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/compose/litellm.yaml b/templates/compose/litellm.yaml index 388541519..9fd0a234b 100644 --- a/templates/compose/litellm.yaml +++ b/templates/compose/litellm.yaml @@ -32,6 +32,7 @@ services: - ANTHROPIC_API_BASE=${ANTHROPIC_API_BASE} - VOYAGE_API_KEY=${VOYAGE_API_KEY} - VOYAGE_API_BASE=${VOYAGE_API_BASE} + - STORE_MODEL_IN_DB=${STORE_MODEL_IN_DB} volumes: - type: bind source: ./litellm-config.yaml From 36d7844989a0bbd97b38e8d4777dc75ed61ed189 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:47:03 +0100 Subject: [PATCH 60/67] Fix deployment log view UX issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prevent text selection from being cleared when logs are re-rendered during polling - Preserve fullscreen state when toggling debug logs or other Livewire updates - Fix log filtering to properly apply when debug mode is toggled ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- .../Project/Application/Deployment/Show.php | 2 + .../application/deployment/show.blade.php | 57 ++++++++++++------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/app/Livewire/Project/Application/Deployment/Show.php b/app/Livewire/Project/Application/Deployment/Show.php index 44ab419c2..8c0ee1a3f 100644 --- a/app/Livewire/Project/Application/Deployment/Show.php +++ b/app/Livewire/Project/Application/Deployment/Show.php @@ -20,6 +20,8 @@ class Show extends Component public bool $is_debug_enabled = false; + public bool $fullscreen = false; + private bool $deploymentFinishedDispatched = false; public function getListeners() diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index f2cde05cf..f125cde91 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -6,7 +6,7 @@
- @if (data_get($application_deployment_queue, 'status') === 'in_progress') -
Deployment is -
- {{ Str::headline(data_get($this->application_deployment_queue, 'status')) }}. -
- -
- {{--
Logs will be updated automatically.
--}} - @else -
Deployment is {{ Str::headline(data_get($application_deployment_queue, 'status')) }}. -
- @endif -
+
+ :class="fullscreen ? 'h-full' : 'border border-dotted rounded-sm'">
- - +
+ @if (data_get($application_deployment_queue, 'status') === 'in_progress') +
+ Deployment is + In Progress + +
+ @else +
+ Deployment is + {{ Str::headline(data_get($application_deployment_queue, 'status')) }} +
+ @endif + +
$searchableContent = $line['timestamp'] . ' ' . $lineContent; @endphp
isset($line['command']) && $line['command'], 'flex gap-2', ])> From d40c2caca20113023576e6d711087750532e7a7c Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:49:26 +0100 Subject: [PATCH 61/67] Fix text disappearing during selection in deployment logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure initial render happens even when selection is active by checking if element already has content before skipping re-render. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../livewire/project/application/deployment/show.blade.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index f125cde91..365db724d 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -63,7 +63,8 @@ }, renderHighlightedLog(el, text) { // Skip re-render if user has text selected in logs (preserves copy ability) - if (this.hasActiveLogSelection()) { + // But always render if the element is empty (initial render) + if (el.textContent && this.hasActiveLogSelection()) { return; } From 5cc822c9963312dd9f7bd3f5ba8b7a45d5e771e2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:52:00 +0100 Subject: [PATCH 62/67] Fix text selection issue in runtime logs view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the same selection preservation fix to the runtime logs component (get-logs.blade.php) that was applied to deployment logs: - Add hasActiveLogSelection() helper to detect active text selection - Skip re-render when user has text selected (preserves copy ability) - Add renderTrigger mechanism to ensure filtering works after refresh - Use x-effect for hidden state to properly react to Livewire updates ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../project/shared/get-logs.blade.php | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 8504a160f..692138c5b 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -9,6 +9,7 @@ scrollDebounce: null, colorLogs: localStorage.getItem('coolify-color-logs') === 'true', searchQuery: '', + renderTrigger: 0, containerName: '{{ $container ?? "logs" }}', makeFullscreen() { this.fullscreen = !this.fullscreen; @@ -80,6 +81,18 @@ if (!this.searchQuery.trim()) return true; return line.toLowerCase().includes(this.searchQuery.toLowerCase()); }, + hasActiveLogSelection() { + const selection = window.getSelection(); + if (!selection || selection.isCollapsed || !selection.toString().trim()) { + return false; + } + const logsContainer = document.getElementById('logs'); + if (!logsContainer) return false; + + // Check if selection is within the logs container + const range = selection.getRangeAt(0); + return logsContainer.contains(range.commonAncestorContainer); + }, decodeHtml(text) { // Decode HTML entities, handling double-encoding with max iteration limit to prevent DoS let decoded = text; @@ -96,6 +109,12 @@ return decoded; }, renderHighlightedLog(el, text) { + // Skip re-render if user has text selected in logs (preserves copy ability) + // But always render if the element is empty (initial render) + if (el.textContent && this.hasActiveLogSelection()) { + return; + } + const decoded = this.decodeHtml(text); el.textContent = ''; @@ -167,6 +186,12 @@ this.$wire.getLogs(true); this.logsLoaded = true; } + // Re-render logs after Livewire updates + Livewire.hook('commit', ({ succeed }) => { + succeed(() => { + this.$nextTick(() => { this.renderTrigger++; }); + }); + }); } }"> @if ($collapsible) @@ -350,8 +375,8 @@ class="text-gray-500 dark:text-gray-400 py-2"> @endphp
{{ $timestamp }} @endif
@endforeach From 6b9c633fe744ab2aa47a3a6341bae775628c8268 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:53:02 +0100 Subject: [PATCH 63/67] Prevent Livewire from morphing logs when text is selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use Livewire's morph.updating hook to skip DOM morphing of the logs container when user has text selected. This prevents the selection from being lost when polling or manual refresh occurs. The previous fix only prevented the JavaScript-based re-render, but Livewire's morphing was still replacing the DOM elements entirely. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../livewire/project/application/deployment/show.blade.php | 6 ++++++ resources/views/livewire/project/shared/get-logs.blade.php | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index 365db724d..5f37786f5 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -147,6 +147,12 @@ } }, init() { + // Prevent Livewire from morphing logs container when text is selected + Livewire.hook('morph.updating', ({ el, component, toEl, skip }) => { + if (el.id === 'logs' && this.hasActiveLogSelection()) { + skip(); + } + }); // Re-render logs after Livewire updates document.addEventListener('livewire:navigated', () => { this.$nextTick(() => { this.renderTrigger++; }); diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 692138c5b..91f615227 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -186,6 +186,12 @@ this.$wire.getLogs(true); this.logsLoaded = true; } + // Prevent Livewire from morphing logs container when text is selected + Livewire.hook('morph.updating', ({ el, component, toEl, skip }) => { + if (el.id === 'logs' && this.hasActiveLogSelection()) { + skip(); + } + }); // Re-render logs after Livewire updates Livewire.hook('commit', ({ succeed }) => { succeed(() => { From 987252a179f9ae1575977cb9a86374a63443c501 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:57:07 +0100 Subject: [PATCH 64/67] Move polling button next to refresh button in runtime logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder toolbar buttons so that Refresh and Stream Logs (polling) are adjacent, making the related actions easier to find. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../project/shared/get-logs.blade.php | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 91f615227..c4b610873 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -266,6 +266,23 @@ class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text- d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" /> + - + -
- - Overview - - - Settings - - - All Resources - +
+ @foreach ($projects as $project) + + {{ $project->name }} + + @endforeach
-
  • -
    +
  • +
    {{ data_get($resource, 'environment.name') }} - + - -
    - - All Resources - - - Environment Settings - - - Clone Environment - + +
    + +
    + @foreach ($environments as $environment) + @php + $envResources = collect() + ->merge($environment->applications->map(fn($app) => ['type' => 'application', 'resource' => $app])) + ->merge($environment->databases()->map(fn($db) => ['type' => 'database', 'resource' => $db])) + ->merge($environment->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc])); + @endphp + + @endforeach +
    + + + @foreach ($environments as $environment) + @php + $envResources = collect() + ->merge($environment->applications->map(fn($app) => ['type' => 'application', 'resource' => $app])) + ->merge($environment->databases()->map(fn($db) => ['type' => 'database', 'resource' => $db])) + ->merge($environment->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc])); + @endphp + @if ($envResources->count() > 0) +
    +
    + @foreach ($envResources as $envResource) + @php + $resType = $envResource['type']; + $res = $envResource['resource']; + $resRoute = match($resType) { + 'application' => route('project.application.configuration', [ + 'project_uuid' => $currentProjectUuid, + 'environment_uuid' => $environment->uuid, + 'application_uuid' => $res->uuid, + ]), + 'service' => route('project.service.configuration', [ + 'project_uuid' => $currentProjectUuid, + 'environment_uuid' => $environment->uuid, + 'service_uuid' => $res->uuid, + ]), + 'database' => route('project.database.configuration', [ + 'project_uuid' => $currentProjectUuid, + 'environment_uuid' => $environment->uuid, + 'database_uuid' => $res->uuid, + ]), + }; + $isCurrentResource = $res->uuid === $currentResourceUuid; + @endphp + + @endforeach +
    + + + @foreach ($envResources as $envResource) + @php + $resType = $envResource['type']; + $res = $envResource['resource']; + $resParams = [ + 'project_uuid' => $currentProjectUuid, + 'environment_uuid' => $environment->uuid, + ]; + if ($resType === 'application') { + $resParams['application_uuid'] = $res->uuid; + } elseif ($resType === 'service') { + $resParams['service_uuid'] = $res->uuid; + } else { + $resParams['database_uuid'] = $res->uuid; + } + $resKey = $environment->uuid . '-' . $res->uuid; + @endphp +
    + +
    + @if ($resType === 'application') + + Deployments + Logs + @can('canAccessTerminal') + Terminal + @endcan + @elseif ($resType === 'service') + + Logs + @can('canAccessTerminal') + Terminal + @endcan + @else + + Logs + @can('canAccessTerminal') + Terminal + @endcan + @if ( + $res->getMorphClass() === 'App\Models\StandalonePostgresql' || + $res->getMorphClass() === 'App\Models\StandaloneMongodb' || + $res->getMorphClass() === 'App\Models\StandaloneMysql' || + $res->getMorphClass() === 'App\Models\StandaloneMariadb') + Backups + @endif + @endif +
    + + + +
    + @endforeach +
    + @endif + @endforeach
  • -
  • -
    + @php + $resourceUuid = data_get($resource, 'uuid'); + $resourceType = $resource->getMorphClass(); + $isApplication = $resourceType === 'App\Models\Application'; + $isService = $resourceType === 'App\Models\Service'; + $isDatabase = str_contains($resourceType, 'Database') || str_contains($resourceType, 'Standalone'); + $routeParams = [ + 'project_uuid' => $currentProjectUuid, + 'environment_uuid' => $currentEnvironmentUuid, + ]; + if ($isApplication) { + $routeParams['application_uuid'] = $resourceUuid; + } elseif ($isService) { + $routeParams['service_uuid'] = $resourceUuid; + } else { + $routeParams['database_uuid'] = $resourceUuid; + } + @endphp +
  • +
    + href="{{ $isApplication + ? route('project.application.configuration', $routeParams) + : ($isService + ? route('project.service.configuration', $routeParams) + : route('project.database.configuration', $routeParams)) }}"> {{ data_get($resource, 'name') }} - + - -
    - @if($resource->getMorphClass() === 'App\Models\Application') - - - Configuration - - - Deployments - - - Logs - - - Terminal - - @elseif(str_contains($resource->getMorphClass(), 'Database')) - - - Configuration - - - Backups - - - Logs - - - Terminal - - @elseif($resource->getMorphClass() === 'App\Models\Service') - - - Configuration - - - Logs - - - Terminal - - @endif + +
    + +
    + @if ($isApplication) + + + + Deployments + + + Logs + + @can('canAccessTerminal') + + Terminal + + @endcan + @elseif ($isService) + + + + Logs + + @can('canAccessTerminal') + + Terminal + + @endcan + @else + + + + Logs + + @can('canAccessTerminal') + + Terminal + + @endcan + @if ( + $resourceType === 'App\Models\StandalonePostgresql' || + $resourceType === 'App\Models\StandaloneMongodb' || + $resourceType === 'App\Models\StandaloneMysql' || + $resourceType === 'App\Models\StandaloneMariadb') + + Backups + + @endif + @endif +
    + + +
  • @@ -251,8 +464,9 @@ class="block px-4 py-2 text-sm hover:bg-coolgray-200 dark:hover:bg-coolgray-200"