fix(livewire): scope DatabaseProxyStopped to proxy fields, harden status trait

Clickhouse, Dragonfly, and Keydb still called syncData() inside the
DatabaseProxyStopped broadcast handler, clobbering in-progress edits to
name/description/credentials. Refresh only is_public/public_port/
public_port_timeout instead, matching the pattern used elsewhere.

Also null-guard HasDatabaseStatusInfo::getListeners() against an absent
Auth::user()/currentTeam(), add explicit return types on getListeners()
and render(), and convert inline comments in the SSL refresh test to a
PHPDoc block.
This commit is contained in:
Aditya Tripathi 2026-05-21 10:24:49 +00:00
parent e7e65831a7
commit 7a3fcd37d5
5 changed files with 37 additions and 18 deletions

View file

@ -192,9 +192,12 @@ public function instantSave()
}
}
public function databaseProxyStopped()
public function databaseProxyStopped(): void
{
$this->syncData();
$this->database->refresh();
$this->isPublic = $this->database->is_public;
$this->publicPort = $this->database->public_port;
$this->publicPortTimeout = $this->database->public_port_timeout;
$this->dispatch('databaseUpdated');
}

View file

@ -184,9 +184,12 @@ public function instantSave()
}
}
public function databaseProxyStopped()
public function databaseProxyStopped(): void
{
$this->syncData();
$this->database->refresh();
$this->isPublic = $this->database->is_public;
$this->publicPort = $this->database->public_port;
$this->publicPortTimeout = $this->database->public_port_timeout;
$this->dispatch('databaseUpdated');
}

View file

@ -189,9 +189,12 @@ public function instantSave()
}
}
public function databaseProxyStopped()
public function databaseProxyStopped(): void
{
$this->syncData();
$this->database->refresh();
$this->isPublic = $this->database->is_public;
$this->publicPort = $this->database->public_port;
$this->publicPortTimeout = $this->database->public_port_timeout;
$this->dispatch('databaseUpdated');
}

View file

@ -5,6 +5,7 @@
use App\Helpers\SslHelper;
use Carbon\Carbon;
use Exception;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Auth;
/**
@ -51,16 +52,23 @@ protected function showPublicUrlPlaceholder(): bool
return false;
}
public function getListeners()
public function getListeners(): array
{
$userId = Auth::id();
$teamId = Auth::user()->currentTeam()->id;
$listeners = ['databaseUpdated' => 'refresh'];
return [
"echo-private:user.{$userId},DatabaseStatusChanged" => 'refresh',
"echo-private:team.{$teamId},ServiceChecked" => 'refresh',
'databaseUpdated' => 'refresh',
];
$user = Auth::user();
if (! $user) {
return $listeners;
}
$listeners["echo-private:user.{$user->id},DatabaseStatusChanged"] = 'refresh';
$team = $user->currentTeam();
if ($team) {
$listeners["echo-private:team.{$team->id},ServiceChecked"] = 'refresh';
}
return $listeners;
}
public function mount(): void
@ -150,7 +158,7 @@ public function regenerateSslCertificate(): void
}
}
public function render()
public function render(): View
{
return view('livewire.project.database.status-info', [
'label' => $this->databaseLabel(),

View file

@ -78,11 +78,13 @@
->not->toHaveKey("echo-private:team.{$this->team->id},ServiceStatusChanged");
})->with('database-general-forms-without-broadcasts');
/**
* Resolve a Livewire component's listeners regardless of whether the subclass
* exposes getListeners() publicly or only declares a $listeners array the
* HandlesEvents trait keeps getListeners() protected by default.
*/
function resolveLivewireListeners(object $component): array
{
// Livewire's HandlesEvents trait declares getListeners() as protected,
// so subclasses that override it as public are callable directly, but
// subclasses that rely on $listeners are not. Reflection handles both.
$method = new ReflectionMethod($component, 'getListeners');
$method->setAccessible(true);