From 1ab4b9aa31c6ed85815527d06f100afd2c87cb9b Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Sun, 26 Oct 2025 23:39:40 +0100
Subject: [PATCH 1/6] refactor: simplify project data retrieval and enhance
OAuth settings handling
---
app/Livewire/Project/Index.php | 22 +----
app/Livewire/Project/Shared/GetLogs.php | 2 +
app/Livewire/Server/New/ByIp.php | 32 -------
app/Livewire/SettingsOauth.php | 49 ++++++++++-
resources/views/components/navbar.blade.php | 2 +-
resources/views/layouts/app.blade.php | 2 +-
.../views/livewire/project/edit.blade.php | 30 +++----
.../views/livewire/project/index.blade.php | 41 +++++----
.../project/shared/get-logs.blade.php | 4 +-
.../views/livewire/server/new/by-ip.blade.php | 38 +--------
.../livewire/server/proxy/logs.blade.php | 2 +-
.../server/security/patches.blade.php | 85 ++++++++++---------
.../views/livewire/server/show.blade.php | 4 +-
.../views/livewire/settings-oauth.blade.php | 32 +++----
14 files changed, 158 insertions(+), 187 deletions(-)
diff --git a/app/Livewire/Project/Index.php b/app/Livewire/Project/Index.php
index a27a3652f..0e4f15a5c 100644
--- a/app/Livewire/Project/Index.php
+++ b/app/Livewire/Project/Index.php
@@ -18,20 +18,7 @@ class Index extends Component
public function mount()
{
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
- $this->projects = Project::ownedByCurrentTeam()->get()->map(function ($project) {
- $project->settingsRoute = route('project.edit', ['project_uuid' => $project->uuid]);
- $project->canUpdate = auth()->user()->can('update', $project);
- $project->canCreateResource = auth()->user()->can('createAnyResource');
- $firstEnvironment = $project->environments->first();
- $project->addResourceRoute = $firstEnvironment
- ? route('project.resource.create', [
- 'project_uuid' => $project->uuid,
- 'environment_uuid' => $firstEnvironment->uuid,
- ])
- : null;
-
- return $project;
- });
+ $this->projects = Project::ownedByCurrentTeam()->get();
$this->servers = Server::ownedByCurrentTeam()->count();
}
@@ -39,11 +26,4 @@ public function render()
{
return view('livewire.project.index');
}
-
- public function navigateToProject($projectUuid)
- {
- $project = collect($this->projects)->firstWhere('uuid', $projectUuid);
-
- return $this->redirect($project->navigateTo(), navigate: false);
- }
}
diff --git a/app/Livewire/Project/Shared/GetLogs.php b/app/Livewire/Project/Shared/GetLogs.php
index 43fd97c34..304f7b411 100644
--- a/app/Livewire/Project/Shared/GetLogs.php
+++ b/app/Livewire/Project/Shared/GetLogs.php
@@ -33,6 +33,8 @@ class GetLogs extends Component
public ?string $container = null;
+ public ?string $displayName = null;
+
public ?string $pull_request = null;
public ?bool $streamLogs = false;
diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php
index 116775a6f..35526d59e 100644
--- a/app/Livewire/Server/New/ByIp.php
+++ b/app/Livewire/Server/New/ByIp.php
@@ -7,7 +7,6 @@
use App\Models\Team;
use App\Support\ValidationPatterns;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
-use Illuminate\Support\Collection;
use Livewire\Attributes\Locked;
use Livewire\Component;
@@ -39,25 +38,12 @@ class ByIp extends Component
public int $port = 22;
- public bool $is_swarm_manager = false;
-
- public bool $is_swarm_worker = false;
-
- public $selected_swarm_cluster = null;
-
public bool $is_build_server = false;
- #[Locked]
- public Collection $swarm_managers;
-
public function mount()
{
$this->name = generate_random_name();
$this->private_key_id = $this->private_keys->first()?->id;
- $this->swarm_managers = Server::isUsable()->get()->where('settings.is_swarm_manager', true);
- if ($this->swarm_managers->count() > 0) {
- $this->selected_swarm_cluster = $this->swarm_managers->first()->id;
- }
}
protected function rules(): array
@@ -72,9 +58,6 @@ protected function rules(): array
'ip' => 'required|string',
'user' => 'required|string',
'port' => 'required|integer|between:1,65535',
- 'is_swarm_manager' => 'required|boolean',
- 'is_swarm_worker' => 'required|boolean',
- 'selected_swarm_cluster' => 'nullable|integer',
'is_build_server' => 'required|boolean',
];
}
@@ -94,11 +77,6 @@ protected function messages(): array
'port.required' => 'The Port field is required.',
'port.integer' => 'The Port field must be an integer.',
'port.between' => 'The Port field must be between 1 and 65535.',
- 'is_swarm_manager.required' => 'The Swarm Manager field is required.',
- 'is_swarm_manager.boolean' => 'The Swarm Manager field must be true or false.',
- 'is_swarm_worker.required' => 'The Swarm Worker field is required.',
- 'is_swarm_worker.boolean' => 'The Swarm Worker field must be true or false.',
- 'selected_swarm_cluster.integer' => 'The Swarm Cluster field must be an integer.',
'is_build_server.required' => 'The Build Server field is required.',
'is_build_server.boolean' => 'The Build Server field must be true or false.',
]);
@@ -140,9 +118,6 @@ public function submit()
'team_id' => currentTeam()->id,
'private_key_id' => $this->private_key_id,
];
- if ($this->is_swarm_worker) {
- $payload['swarm_cluster'] = $this->selected_swarm_cluster;
- }
if ($this->is_build_server) {
data_forget($payload, 'proxy');
}
@@ -150,13 +125,6 @@ public function submit()
$server->proxy->set('status', 'exited');
$server->proxy->set('type', ProxyTypes::TRAEFIK->value);
$server->save();
- if ($this->is_build_server) {
- $this->is_swarm_manager = false;
- $this->is_swarm_worker = false;
- } else {
- $server->settings->is_swarm_manager = $this->is_swarm_manager;
- $server->settings->is_swarm_worker = $this->is_swarm_worker;
- }
$server->settings->is_build_server = $this->is_build_server;
$server->settings->save();
diff --git a/app/Livewire/SettingsOauth.php b/app/Livewire/SettingsOauth.php
index e23f94a73..f17730a10 100644
--- a/app/Livewire/SettingsOauth.php
+++ b/app/Livewire/SettingsOauth.php
@@ -29,7 +29,16 @@ public function mount()
return redirect()->route('home');
}
$this->oauth_settings_map = OauthSetting::all()->sortBy('provider')->reduce(function ($carry, $setting) {
- $carry[$setting->provider] = $setting;
+ $carry[$setting->provider] = [
+ 'id' => $setting->id,
+ 'provider' => $setting->provider,
+ 'enabled' => $setting->enabled,
+ 'client_id' => $setting->client_id,
+ 'client_secret' => $setting->client_secret,
+ 'redirect_uri' => $setting->redirect_uri,
+ 'tenant' => $setting->tenant,
+ 'base_url' => $setting->base_url,
+ ];
return $carry;
}, []);
@@ -38,16 +47,48 @@ public function mount()
private function updateOauthSettings(?string $provider = null)
{
if ($provider) {
- $oauth = $this->oauth_settings_map[$provider];
+ $oauthData = $this->oauth_settings_map[$provider];
+ $oauth = OauthSetting::find($oauthData['id']);
+
+ $oauth->fill([
+ 'enabled' => $oauthData['enabled'],
+ 'client_id' => $oauthData['client_id'],
+ 'client_secret' => $oauthData['client_secret'],
+ 'redirect_uri' => $oauthData['redirect_uri'],
+ 'tenant' => $oauthData['tenant'],
+ 'base_url' => $oauthData['base_url'],
+ ]);
+
if (! $oauth->couldBeEnabled()) {
$oauth->update(['enabled' => false]);
throw new \Exception('OAuth settings are not complete for '.$oauth->provider.'. Please fill in all required fields.');
}
$oauth->save();
+
+ // Update the array with fresh data
+ $this->oauth_settings_map[$provider] = [
+ 'id' => $oauth->id,
+ 'provider' => $oauth->provider,
+ 'enabled' => $oauth->enabled,
+ 'client_id' => $oauth->client_id,
+ 'client_secret' => $oauth->client_secret,
+ 'redirect_uri' => $oauth->redirect_uri,
+ 'tenant' => $oauth->tenant,
+ 'base_url' => $oauth->base_url,
+ ];
+
$this->dispatch('success', 'OAuth settings for '.$oauth->provider.' updated successfully!');
} else {
- foreach (array_values($this->oauth_settings_map) as &$setting) {
- $setting->save();
+ foreach (array_values($this->oauth_settings_map) as $settingData) {
+ $oauth = OauthSetting::find($settingData['id']);
+ $oauth->update([
+ 'enabled' => $settingData['enabled'],
+ 'client_id' => $settingData['client_id'],
+ 'client_secret' => $settingData['client_secret'],
+ 'redirect_uri' => $settingData['redirect_uri'],
+ 'tenant' => $settingData['tenant'],
+ 'base_url' => $settingData['base_url'],
+ ]);
}
}
}
diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php
index 4b152dd11..84502872e 100644
--- a/resources/views/components/navbar.blade.php
+++ b/resources/views/components/navbar.blade.php
@@ -79,7 +79,7 @@
}">
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index a1ff81e84..0a7909761 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -47,7 +47,7 @@
diff --git a/resources/views/livewire/project/edit.blade.php b/resources/views/livewire/project/edit.blade.php
index 591643402..dfaf20ed9 100644
--- a/resources/views/livewire/project/edit.blade.php
+++ b/resources/views/livewire/project/edit.blade.php
@@ -1,19 +1,19 @@
{{ data_get_str($project, 'name')->limit(10) }} > Edit | Coolify
-
-
+
\ No newline at end of file
diff --git a/resources/views/livewire/project/index.blade.php b/resources/views/livewire/project/index.blade.php
index fd6e4c422..e1e1a22bc 100644
--- a/resources/views/livewire/project/index.blade.php
+++ b/resources/views/livewire/project/index.blade.php
@@ -11,29 +11,38 @@
@endcan
All your projects are here.
-
-
-
+
+ @foreach ($projects as $project)
+
diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php
index c54f69dde..2aff4fc1c 100644
--- a/resources/views/livewire/project/shared/get-logs.blade.php
+++ b/resources/views/livewire/project/shared/get-logs.blade.php
@@ -34,7 +34,9 @@
}
}">
- @if ($resource?->type() === 'application' || str($resource?->type())->startsWith('standalone'))
+ @if ($displayName)
+
{{ $displayName }}
+ @elseif ($resource?->type() === 'application' || str($resource?->type())->startsWith('standalone'))
{{ $container }}
@else
{{ str($container)->beforeLast('-')->headline() }}
diff --git a/resources/views/livewire/server/new/by-ip.blade.php b/resources/views/livewire/server/new/by-ip.blade.php
index 94d0fccbd..e41342ca2 100644
--- a/resources/views/livewire/server/new/by-ip.blade.php
+++ b/resources/views/livewire/server/new/by-ip.blade.php
@@ -31,45 +31,9 @@ class="font-bold underline" target="_blank"
helper="Build servers are used to build your applications, so you cannot deploy applications to it."
label="Use it as a build server?" />
-
-
Swarm (experimental)
-
- @if ($is_swarm_worker || $is_build_server)
-
- @else
-
- @endif
- @if ($is_swarm_manager || $is_build_server)
-
- @else
-
- @endif
- @if ($is_swarm_worker && count($swarm_managers) > 0)
-
-
- @foreach ($swarm_managers as $server)
- @if ($loop->first)
- {{ $server->name }}
- @else
- {{ $server->name }}
- @endif
- @endforeach
-
-
- @endif
-
Continue
@endif
-
+
\ No newline at end of file
diff --git a/resources/views/livewire/server/proxy/logs.blade.php b/resources/views/livewire/server/proxy/logs.blade.php
index ea4a68674..17fe65f4c 100644
--- a/resources/views/livewire/server/proxy/logs.blade.php
+++ b/resources/views/livewire/server/proxy/logs.blade.php
@@ -7,7 +7,7 @@
Logs
-
+
diff --git a/resources/views/livewire/server/security/patches.blade.php b/resources/views/livewire/server/security/patches.blade.php
index fd7b61ad9..7627cf91f 100644
--- a/resources/views/livewire/server/security/patches.blade.php
+++ b/resources/views/livewire/server/security/patches.blade.php
@@ -56,48 +56,53 @@
step2ButtonText="Update All
Packages" />
-
-
-
- Package
- @if ($packageManager !== 'dnf')
- Current Version
- @endif
- New Version
- Action
-
-
-
- @foreach ($updates as $update)
+
+
+
-
- @if (data_get_str($update, 'package')->contains('docker') || data_get_str($update, 'package')->contains('kernel'))
-
-
-
-
-
-
-
-
- @endif
- {{ data_get($update, 'package') }}
-
- @if ($packageManager !== 'dnf')
- {{ data_get($update, 'current_version') }}
- @endif
- {{ data_get($update, 'new_version') }}
-
- Update
-
+ Package
+ Version
+ Action
- @endforeach
-
-
+
+
+ @foreach ($updates as $update)
+
+
+
+ @if (data_get_str($update, 'package')->contains('docker') || data_get_str($update, 'package')->contains('kernel'))
+
+
+
+
+
+
+
+
+ @endif
+
{{ data_get($update, 'package') }}
+
+
+
+
+ {{ data_get($update, 'new_version') }}
+ @if ($packageManager !== 'dnf' && data_get($update, 'current_version'))
+
+ @endif
+
+
+
+ Update
+
+
+ @endforeach
+
+
+
@endif
@endif
diff --git a/resources/views/livewire/server/show.blade.php b/resources/views/livewire/server/show.blade.php
index 5f99ad97f..4cce11e20 100644
--- a/resources/views/livewire/server/show.blade.php
+++ b/resources/views/livewire/server/show.blade.php
@@ -337,7 +337,7 @@ class="w-full input opacity-50 cursor-not-allowed"
Sentinel Logs
+ container="coolify-sentinel" displayName="Sentinel" lazy />
Logs
@@ -353,7 +353,7 @@ class="w-full input opacity-50 cursor-not-allowed"
Sentinel Logs
+ container="coolify-sentinel" displayName="Sentinel" lazy />
Logs
diff --git a/resources/views/livewire/settings-oauth.blade.php b/resources/views/livewire/settings-oauth.blade.php
index 6a967504d..7650a5654 100644
--- a/resources/views/livewire/settings-oauth.blade.php
+++ b/resources/views/livewire/settings-oauth.blade.php
@@ -16,33 +16,33 @@
@foreach ($oauth_settings_map as $oauth_setting)
-
{{ ucfirst($oauth_setting->provider) }}
+
{{ ucfirst($oauth_setting['provider']) }}
-
+
-
-
-
- @if ($oauth_setting->provider == 'azure')
-
+ @if ($oauth_setting['provider'] == 'azure')
+
@endif
- @if ($oauth_setting->provider == 'google')
-
@endif
@if (
- $oauth_setting->provider == 'authentik' ||
- $oauth_setting->provider == 'clerk' ||
- $oauth_setting->provider == 'zitadel' ||
- $oauth_setting->provider == 'gitlab')
-
@endif
From 28fc3feab00d99bade5d4beeef959b8df011667e Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Mon, 27 Oct 2025 15:44:27 +0100
Subject: [PATCH 2/6] fix: remove wire:ignore from modal and add wire:key to
EditCompose component
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Remove wire:ignore from modal-input.blade.php wrapper to allow child Livewire components to be properly tracked
- Add unique wire:key to EditCompose component for proper identification when teleported
- Fixes 'Unable to call component method' error when saving compose files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude
---
resources/views/components/modal-input.blade.php | 2 +-
resources/views/livewire/project/service/stack-form.blade.php | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/resources/views/components/modal-input.blade.php b/resources/views/components/modal-input.blade.php
index b031740ca..b8f582588 100644
--- a/resources/views/components/modal-input.blade.php
+++ b/resources/views/components/modal-input.blade.php
@@ -13,7 +13,7 @@
+ class="relative w-auto h-auto">
@if ($content)
{{ $content }}
diff --git a/resources/views/livewire/project/service/stack-form.blade.php b/resources/views/livewire/project/service/stack-form.blade.php
index 5a8a3e420..ed38cda16 100644
--- a/resources/views/livewire/project/service/stack-form.blade.php
+++ b/resources/views/livewire/project/service/stack-form.blade.php
@@ -8,7 +8,7 @@
Save
@can('update', $service)
-
+
@endcan
From 974a8bdf647c0aea240469b979856a868c0499e6 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Mon, 27 Oct 2025 15:51:14 +0100
Subject: [PATCH 3/6] fix: add wire:ignore directive to modal component for
improved functionality
---
resources/views/components/modal-input.blade.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/resources/views/components/modal-input.blade.php b/resources/views/components/modal-input.blade.php
index b8f582588..b031740ca 100644
--- a/resources/views/components/modal-input.blade.php
+++ b/resources/views/components/modal-input.blade.php
@@ -13,7 +13,7 @@
+ class="relative w-auto h-auto" wire:ignore>
@if ($content)
{{ $content }}
From f0db097a905df9e64b1c1a99abe52047cbea5dfc Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Mon, 27 Oct 2025 15:52:05 +0100
Subject: [PATCH 4/6] fix: clean up formatting and remove unnecessary key
binding in stack form component
---
.../project/service/stack-form.blade.php | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/resources/views/livewire/project/service/stack-form.blade.php b/resources/views/livewire/project/service/stack-form.blade.php
index ed38cda16..8972345bc 100644
--- a/resources/views/livewire/project/service/stack-form.blade.php
+++ b/resources/views/livewire/project/service/stack-form.blade.php
@@ -5,21 +5,24 @@
@if (isDev())
{{ $service->compose_parsing_version }}
@endif
-
Save
+
Save
@can('update', $service)
-
+
@endcan
Configuration
-
+
-
@if ($fields->count() > 0)
@@ -36,10 +39,11 @@ class="font-bold">{{ data_get($field, 'serviceName') }}{{ data_get($field
@endif
-
@endforeach
@endif
-
+
\ No newline at end of file
From 8a3dc19d19dffae0f213cb1eba522c12bd73d417 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Mon, 27 Oct 2025 17:01:24 +0100
Subject: [PATCH 5/6] Update app/Livewire/SettingsOauth.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---
app/Livewire/SettingsOauth.php | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/app/Livewire/SettingsOauth.php b/app/Livewire/SettingsOauth.php
index f17730a10..77d3b36fc 100644
--- a/app/Livewire/SettingsOauth.php
+++ b/app/Livewire/SettingsOauth.php
@@ -50,6 +50,10 @@ private function updateOauthSettings(?string $provider = null)
$oauthData = $this->oauth_settings_map[$provider];
$oauth = OauthSetting::find($oauthData['id']);
+ if (!$oauth) {
+ throw new \Exception('OAuth setting for '.$provider.' not found. It may have been deleted.');
+ }
+
$oauth->fill([
'enabled' => $oauthData['enabled'],
'client_id' => $oauthData['client_id'],
From b1a68df65caef6df06c9495a817ff4c340a44d39 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Mon, 27 Oct 2025 17:04:33 +0100
Subject: [PATCH 6/6] fix: add null checks and validation to OAuth bulk update
method
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add null check before updating OAuth settings to prevent calling methods on null
- Apply couldBeEnabled() validation for all settings in bulk update (not just instant save)
- Disable OAuth providers that fail validation and collect error messages
- Surface all validation errors to the user instead of silently failing
- Update oauth_settings_map with fresh data after saving each setting
This ensures bulk updates follow the same validation logic as instant-save paths
and prevents bypassing model validation by directly calling update.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude
---
app/Livewire/SettingsOauth.php | 35 ++++++++++++++++++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/app/Livewire/SettingsOauth.php b/app/Livewire/SettingsOauth.php
index 77d3b36fc..6f949b716 100644
--- a/app/Livewire/SettingsOauth.php
+++ b/app/Livewire/SettingsOauth.php
@@ -50,7 +50,7 @@ private function updateOauthSettings(?string $provider = null)
$oauthData = $this->oauth_settings_map[$provider];
$oauth = OauthSetting::find($oauthData['id']);
- if (!$oauth) {
+ if (! $oauth) {
throw new \Exception('OAuth setting for '.$provider.' not found. It may have been deleted.');
}
@@ -83,9 +83,17 @@ private function updateOauthSettings(?string $provider = null)
$this->dispatch('success', 'OAuth settings for '.$oauth->provider.' updated successfully!');
} else {
+ $errors = [];
foreach (array_values($this->oauth_settings_map) as $settingData) {
$oauth = OauthSetting::find($settingData['id']);
- $oauth->update([
+
+ if (! $oauth) {
+ $errors[] = "OAuth setting for provider '{$settingData['provider']}' not found. It may have been deleted.";
+
+ continue;
+ }
+
+ $oauth->fill([
'enabled' => $settingData['enabled'],
'client_id' => $settingData['client_id'],
'client_secret' => $settingData['client_secret'],
@@ -93,6 +101,29 @@ private function updateOauthSettings(?string $provider = null)
'tenant' => $settingData['tenant'],
'base_url' => $settingData['base_url'],
]);
+
+ if ($settingData['enabled'] && ! $oauth->couldBeEnabled()) {
+ $oauth->enabled = false;
+ $errors[] = "OAuth settings are incomplete for '{$oauth->provider}'. Required fields are missing. The provider has been disabled.";
+ }
+
+ $oauth->save();
+
+ // Update the array with fresh data
+ $this->oauth_settings_map[$oauth->provider] = [
+ 'id' => $oauth->id,
+ 'provider' => $oauth->provider,
+ 'enabled' => $oauth->enabled,
+ 'client_id' => $oauth->client_id,
+ 'client_secret' => $oauth->client_secret,
+ 'redirect_uri' => $oauth->redirect_uri,
+ 'tenant' => $oauth->tenant,
+ 'base_url' => $oauth->base_url,
+ ];
+ }
+
+ if (! empty($errors)) {
+ $this->dispatch('error', implode(' ', $errors));
}
}
}