From 33618f98eadef391ad7ee39cff26b62e5667fb52 Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Wed, 17 Dec 2025 22:51:53 +1100 Subject: [PATCH 1/8] fix(template): superset version and postgres volume mount --- templates/compose/superset-with-postgresql.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/compose/superset-with-postgresql.yaml b/templates/compose/superset-with-postgresql.yaml index d314a587e..cd16b6ae0 100644 --- a/templates/compose/superset-with-postgresql.yaml +++ b/templates/compose/superset-with-postgresql.yaml @@ -7,7 +7,7 @@ services: superset: - image: amancevice/superset:latest + image: amancevice/superset:5.0.0 environment: - SERVICE_URL_SUPERSET_8088 - SECRET_KEY=${SERVICE_BASE64_64_SUPERSETSECRETKEY} @@ -63,13 +63,13 @@ services: retries: 10 postgres: - image: postgres:17-alpine + image: postgres:18 environment: - POSTGRES_USER=${SERVICE_USER_POSTGRES} - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES} - POSTGRES_DB=${POSTGRES_DB:-superset-db} volumes: - - superset_postgres_data:/var/lib/postgresql/data + - superset_postgres_data:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s @@ -77,7 +77,7 @@ services: retries: 10 redis: - image: redis:8-alpine + image: redis:8 volumes: - superset_redis_data:/data command: redis-server --requirepass ${SERVICE_PASSWORD_REDIS} From c640c98cfb6efda2469d772256b7df37c862a9a0 Mon Sep 17 00:00:00 2001 From: nikube Date: Thu, 18 Dec 2025 10:27:24 +0100 Subject: [PATCH 2/8] fix: add persistent storage for Dolibarr documents and custom modules --- templates/compose/dolibarr.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/compose/dolibarr.yaml b/templates/compose/dolibarr.yaml index a5e349052..0ec07ca32 100644 --- a/templates/compose/dolibarr.yaml +++ b/templates/compose/dolibarr.yaml @@ -22,6 +22,9 @@ services: - DOLI_CRON=${DOLI_CRON:-0} - DOLI_INIT_DEMO=${DOLI_INIT_DEMO:-0} - DOLI_COMPANY_NAME=${DOLI_COMPANY_NAME:-MyBigCompany} + volumes: + - dolibarr_docs:/var/www/documents + - dolibarr_custom:/var/www/html/custom healthcheck: test: ["CMD", "curl", "-f", "http://127.0.0.1:80"] interval: 2s From c6c9d5a591866b26b1e96839fd14e0611b42b96b Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 18 Dec 2025 19:36:54 +0100 Subject: [PATCH 3/8] fix(deployment): Skip docker rm -f for builder containers with --rm flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builder containers are started with the --rm flag, which automatically removes them when stopped. The explicit docker rm -f is redundant and adds unnecessary steps to deployment logs. This change adds a skipRemove parameter to graceful_shutdown_container() and sets it to true for builder container shutdowns (uuid-based) while keeping the default behavior for application containers. Fixes #7566 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- app/Jobs/ApplicationDeploymentJob.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index cc1a44f9a..56a29276b 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -371,7 +371,7 @@ public function handle(): void try { $this->application_deployment_queue->addLogEntry("Gracefully shutting down build container: {$this->deployment_uuid}"); - $this->graceful_shutdown_container($this->deployment_uuid); + $this->graceful_shutdown_container($this->deployment_uuid, skipRemove: true); } catch (Exception $e) { // Log but don't fail - container cleanup errors are expected when container is already gone \Log::warning('Failed to shutdown container '.$this->deployment_uuid.': '.$e->getMessage()); @@ -1968,7 +1968,7 @@ private function prepare_builder_image(bool $firstTry = true) $this->application_deployment_queue->addLogEntry('Preparing container with helper image with updated envs.'); } - $this->graceful_shutdown_container($this->deployment_uuid); + $this->graceful_shutdown_container($this->deployment_uuid, skipRemove: true); $this->execute_remote_command( [ $runCommand, @@ -1983,8 +1983,8 @@ private function prepare_builder_image(bool $firstTry = true) private function restart_builder_container_with_actual_commit() { - // Stop and remove the current helper container - $this->graceful_shutdown_container($this->deployment_uuid); + // Stop the current helper container (no need for rm -f as it was started with --rm) + $this->graceful_shutdown_container($this->deployment_uuid, skipRemove: true); // Clear cached env_args to force regeneration with actual SOURCE_COMMIT value $this->env_args = null; @@ -3171,14 +3171,20 @@ private function build_image() $this->application_deployment_queue->addLogEntry('Building docker image completed.'); } - private function graceful_shutdown_container(string $containerName) + private function graceful_shutdown_container(string $containerName, bool $skipRemove = false) { try { $timeout = isDev() ? 1 : 30; - $this->execute_remote_command( - ["docker stop -t $timeout $containerName", 'hidden' => true, 'ignore_errors' => true], - ["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true] - ); + if ($skipRemove) { + $this->execute_remote_command( + ["docker stop -t $timeout $containerName", 'hidden' => true, 'ignore_errors' => true] + ); + } else { + $this->execute_remote_command( + ["docker stop -t $timeout $containerName", 'hidden' => true, 'ignore_errors' => true], + ["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true] + ); + } } catch (Exception $error) { $this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr'); } From e6ed3130b586b8a6b8b07fa136ae69a23999c3eb Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:34:39 +0100 Subject: [PATCH 4/8] feat(stripe): Add manual subscription sync command with dry-run support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add cloud:sync-stripe-subscriptions command to manually check all subscriptions against Stripe. By default it only reports discrepancies without making changes. Use --fix flag to actually apply corrections. This addresses race conditions where subscriptions can be cancelled in Stripe but remain marked as active in Coolify's database. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- .../Cloud/SyncStripeSubscriptions.php | 81 ++++++++++++++++ app/Jobs/SyncStripeSubscriptionsJob.php | 92 +++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 app/Console/Commands/Cloud/SyncStripeSubscriptions.php create mode 100644 app/Jobs/SyncStripeSubscriptionsJob.php diff --git a/app/Console/Commands/Cloud/SyncStripeSubscriptions.php b/app/Console/Commands/Cloud/SyncStripeSubscriptions.php new file mode 100644 index 000000000..e64f86926 --- /dev/null +++ b/app/Console/Commands/Cloud/SyncStripeSubscriptions.php @@ -0,0 +1,81 @@ +error('This command can only be run on Coolify Cloud.'); + + return 1; + } + + if (! isStripe()) { + $this->error('Stripe is not configured.'); + + return 1; + } + + $fix = $this->option('fix'); + + if ($fix) { + $this->warn('Running with --fix: discrepancies will be corrected.'); + } else { + $this->info('Running in check mode (no changes will be made). Use --fix to apply corrections.'); + } + + $this->newLine(); + + $job = new SyncStripeSubscriptionsJob($fix); + $result = $job->handle(); + + if (isset($result['error'])) { + $this->error($result['error']); + + return 1; + } + + $this->info("Total subscriptions checked: {$result['total_checked']}"); + $this->newLine(); + + if (count($result['discrepancies']) > 0) { + $this->warn('Discrepancies found: '.count($result['discrepancies'])); + $this->newLine(); + + foreach ($result['discrepancies'] as $discrepancy) { + $this->line(" - Subscription ID: {$discrepancy['subscription_id']}"); + $this->line(" Team ID: {$discrepancy['team_id']}"); + $this->line(" Stripe ID: {$discrepancy['stripe_subscription_id']}"); + $this->line(" Stripe Status: {$discrepancy['stripe_status']}"); + $this->newLine(); + } + + if ($fix) { + $this->info('All discrepancies have been fixed.'); + } else { + $this->comment('Run with --fix to correct these discrepancies.'); + } + } else { + $this->info('No discrepancies found. All subscriptions are in sync.'); + } + + if (count($result['errors']) > 0) { + $this->newLine(); + $this->error('Errors encountered: '.count($result['errors'])); + foreach ($result['errors'] as $error) { + $this->line(" - Subscription {$error['subscription_id']}: {$error['error']}"); + } + } + + return 0; + } +} diff --git a/app/Jobs/SyncStripeSubscriptionsJob.php b/app/Jobs/SyncStripeSubscriptionsJob.php new file mode 100644 index 000000000..9eb946e4d --- /dev/null +++ b/app/Jobs/SyncStripeSubscriptionsJob.php @@ -0,0 +1,92 @@ +onQueue('high'); + } + + public function handle(): array + { + if (! isCloud() || ! isStripe()) { + return ['error' => 'Not running on Cloud or Stripe not configured']; + } + + $subscriptions = Subscription::whereNotNull('stripe_subscription_id') + ->where('stripe_invoice_paid', true) + ->get(); + + $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key')); + $discrepancies = []; + $errors = []; + + foreach ($subscriptions as $subscription) { + try { + $stripeSubscription = $stripe->subscriptions->retrieve( + $subscription->stripe_subscription_id + ); + + // Check if Stripe says cancelled but we think it's active + if (in_array($stripeSubscription->status, ['canceled', 'incomplete_expired', 'unpaid'])) { + $discrepancies[] = [ + 'subscription_id' => $subscription->id, + 'team_id' => $subscription->team_id, + 'stripe_subscription_id' => $subscription->stripe_subscription_id, + 'stripe_status' => $stripeSubscription->status, + ]; + + // Only fix if --fix flag is passed + if ($this->fix) { + $subscription->update([ + 'stripe_invoice_paid' => false, + 'stripe_past_due' => false, + ]); + + if ($stripeSubscription->status === 'canceled') { + $subscription->team?->subscriptionEnded(); + } + } + } + + // Small delay to avoid Stripe rate limits + usleep(100000); // 100ms + } catch (\Exception $e) { + $errors[] = [ + 'subscription_id' => $subscription->id, + 'error' => $e->getMessage(), + ]; + } + } + + // Only notify if discrepancies found and fixed + if ($this->fix && count($discrepancies) > 0) { + send_internal_notification( + 'SyncStripeSubscriptionsJob: Fixed '.count($discrepancies)." discrepancies:\n". + json_encode($discrepancies, JSON_PRETTY_PRINT) + ); + } + + return [ + 'total_checked' => $subscriptions->count(), + 'discrepancies' => $discrepancies, + 'errors' => $errors, + 'fixed' => $this->fix, + ]; + } +} From eb7a37e93389f45cadf8a2d11802eae9ba1f8c0c Mon Sep 17 00:00:00 2001 From: 360Creators Date: Fri, 19 Dec 2025 16:51:39 +0100 Subject: [PATCH 5/8] Fix: CVE-2025-55182 React2shell infected supabase/studio:2025.06.02-sha-8f2993d --- templates/compose/supabase.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/compose/supabase.yaml b/templates/compose/supabase.yaml index b0720afe4..f6cb7897a 100644 --- a/templates/compose/supabase.yaml +++ b/templates/compose/supabase.yaml @@ -280,7 +280,7 @@ services: config: hide_credentials: true supabase-studio: - image: supabase/studio:2025.06.02-sha-8f2993d + image: supabase/studio:2025.12.17-sha-43f4f7f healthcheck: test: [ From ddcb22e468f05d9f1c6901efea779385710809a4 Mon Sep 17 00:00:00 2001 From: Tam Nguyen Date: Sun, 21 Dec 2025 12:02:19 +1100 Subject: [PATCH 6/8] deps: bump superset to 6.0.0 --- templates/compose/superset-with-postgresql.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/compose/superset-with-postgresql.yaml b/templates/compose/superset-with-postgresql.yaml index cd16b6ae0..4558222a2 100644 --- a/templates/compose/superset-with-postgresql.yaml +++ b/templates/compose/superset-with-postgresql.yaml @@ -7,7 +7,7 @@ services: superset: - image: amancevice/superset:5.0.0 + image: amancevice/superset:6.0.0 environment: - SERVICE_URL_SUPERSET_8088 - SECRET_KEY=${SERVICE_BASE64_64_SUPERSETSECRETKEY} From c98f947a0c478bdee5237f9194bdf6ca3fdb6c21 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 23 Dec 2025 15:22:39 +0100 Subject: [PATCH 7/8] fix: Update version numbers to 4.0.0-beta.459 and 4.0.0-beta.460 --- config/constants.php | 2 +- other/nightly/versions.json | 4 ++-- versions.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/constants.php b/config/constants.php index e6e3ad29a..07141bd17 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.458', + 'version' => '4.0.0-beta.459', 'helper_version' => '1.0.12', 'realtime_version' => '1.0.10', 'self_hosted' => env('SELF_HOSTED', true), diff --git a/other/nightly/versions.json b/other/nightly/versions.json index 366684ff5..8a7441054 100644 --- a/other/nightly/versions.json +++ b/other/nightly/versions.json @@ -1,10 +1,10 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.458" + "version": "4.0.0-beta.459" }, "nightly": { - "version": "4.0.0-beta.459" + "version": "4.0.0-beta.460" }, "helper": { "version": "1.0.12" diff --git a/versions.json b/versions.json index 366684ff5..8a7441054 100644 --- a/versions.json +++ b/versions.json @@ -1,10 +1,10 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.458" + "version": "4.0.0-beta.459" }, "nightly": { - "version": "4.0.0-beta.459" + "version": "4.0.0-beta.460" }, "helper": { "version": "1.0.12" From f995426fb32d810577dad5d46f275cc4a6e5c38d Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 23 Dec 2025 15:34:09 +0100 Subject: [PATCH 8/8] fix(sentinel): Add missing instantSave method and prevent duplicate notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add public instantSave() method to handle instant saves from checkbox clicks - Remove redundant updatedIsMetricsEnabled() and updatedIsSentinelDebugEnabled() hooks - These hooks were causing duplicate notifications when checkboxes were toggled The instantSave attribute on checkboxes triggers wire:click='instantSave', which was failing because the method didn't exist. Now it saves settings and restarts Sentinel in one action, preventing the duplicate updates from both wire:click and wire:model events. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- app/Livewire/Server/Sentinel.php | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/app/Livewire/Server/Sentinel.php b/app/Livewire/Server/Sentinel.php index 20135f3c9..cdcdc71fc 100644 --- a/app/Livewire/Server/Sentinel.php +++ b/app/Livewire/Server/Sentinel.php @@ -110,26 +110,6 @@ public function restartSentinel() } } - public function updatedIsSentinelDebugEnabled($value) - { - try { - $this->submit(); - $this->restartSentinel(); - } catch (\Throwable $e) { - return handleError($e, $this); - } - } - - public function updatedIsMetricsEnabled($value) - { - try { - $this->submit(); - $this->restartSentinel(); - } catch (\Throwable $e) { - return handleError($e, $this); - } - } - public function updatedIsSentinelEnabled($value) { try { @@ -175,6 +155,16 @@ public function submit() } } + public function instantSave() + { + try { + $this->syncData(true); + $this->restartSentinel(); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + public function render() { return view('livewire.server.sentinel');