From b83b97f7e6aada62dcd1885d5329b0264b08ad37 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 3 Nov 2026 22:37:38 +0100 Subject: [PATCH] fix(server): show the SSH error when a new private key cannot connect Closes #7980 Co-Authored-By: Claude Opus 5.5 --- app/Livewire/Server/PrivateKey/Show.php | 12 +++++++++--- app/Models/Server.php | 5 +---- tests/Feature/ServerPrivateKeyDropdownTest.php | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app/Livewire/Server/PrivateKey/Show.php b/app/Livewire/Server/PrivateKey/Show.php index 72b9d31b02..1f35a9ec6c 100644 --- a/app/Livewire/Server/PrivateKey/Show.php +++ b/app/Livewire/Server/PrivateKey/Show.php @@ -62,7 +62,7 @@ class Show extends Component $this->server->save(); ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(justCheckingNewKey: true); if (! $uptime) { - throw new \Exception($error); + throw new \Exception($this->unreachableMessage($error)); } }); $this->loadPrivateKeys(); @@ -113,8 +113,7 @@ class Show extends Component $this->dispatch('success', 'Server is reachable.'); $this->dispatch('refreshServerShow'); } else { - $sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8'); - $this->dispatch('error', 'Server is not reachable.

Check this documentation for further help.

Error: '.$sanitizedError); + $this->dispatch('error', $this->unreachableMessage($error)); return; } @@ -123,6 +122,13 @@ class Show extends Component } } + private function unreachableMessage(?string $error): string + { + $sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8'); + + return 'Server is not reachable.

Check this documentation for further help.

Error: '.$sanitizedError; + } + public function render() { return view('livewire.server.private-key.show'); diff --git a/app/Models/Server.php b/app/Models/Server.php index 58b1b3f0b0..31c851af99 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -2539,10 +2539,7 @@ $siteAddress { return ['uptime' => true, 'error' => null]; } catch (\Throwable $e) { - if ($justCheckingNewKey) { - return ['uptime' => false, 'error' => 'This key is not valid for this server.']; - } - if ($this->settings->is_reachable === true) { + if (! $justCheckingNewKey && $this->settings->is_reachable === true) { $this->settings->is_reachable = false; $this->settings->save(); ServerReachabilityChanged::dispatch($this); diff --git a/tests/Feature/ServerPrivateKeyDropdownTest.php b/tests/Feature/ServerPrivateKeyDropdownTest.php index a8d9aaeced..8c3da60795 100644 --- a/tests/Feature/ServerPrivateKeyDropdownTest.php +++ b/tests/Feature/ServerPrivateKeyDropdownTest.php @@ -145,3 +145,19 @@ describe('after the user switches the session team', function () { ->and($keys->pluck('id'))->toContain($this->currentPrivateKey->id); }); }); + +test('shows the SSH error when the new key cannot connect and keeps the old key', function () { + Process::fake(['*' => Process::result(errorOutput: 'root@host.docker.internal: Permission denied (publickey,password).', exitCode: 255)]); + Storage::fake('ssh-mux'); + $newKey = PrivateKey::factory()->create([ + 'team_id' => $this->team->id, + 'private_key' => PrivateKey::generateNewKeyPair('ed25519')['private_key'], + ]); + + Livewire::test(Show::class, ['server_uuid' => $this->server->uuid]) + ->call('setPrivateKey', $newKey->id) + ->assertDispatched('error', fn (string $event, array $params): bool => str_contains($params[0], 'Permission denied (publickey,password).') + && str_contains($params[0], 'https://coolify.io/docs/knowledge-base/server/openssh')); + + expect($this->server->fresh()->private_key_id)->toBe($this->currentPrivateKey->id); +});