mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-14 08:42:38 -04:00
Livewire rebuilds listener names from the session team on every event call. After a team switch in another tab, open pages still listen on the old team channel, so events failed with EventHandlerDoesNotExist (500), or ran handlers that load resources scoped to the new team (404). Store the team at mount and build team channel listeners from it. Events for a team that is no longer the session team are ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
237 lines
8.3 KiB
PHP
237 lines
8.3 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Project\Database\Keydb;
|
|
|
|
use App\Actions\Database\StartDatabaseProxy;
|
|
use App\Actions\Database\StopDatabaseProxy;
|
|
use App\Models\Server;
|
|
use App\Models\StandaloneKeydb;
|
|
use App\Support\ValidationPatterns;
|
|
use App\Traits\ListensToTeamChannel;
|
|
use Exception;
|
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
use Livewire\Component;
|
|
|
|
class General extends Component
|
|
{
|
|
use AuthorizesRequests;
|
|
use ListensToTeamChannel;
|
|
|
|
public ?Server $server = null;
|
|
|
|
public StandaloneKeydb $database;
|
|
|
|
public string $name;
|
|
|
|
public ?string $description = null;
|
|
|
|
public ?string $keydbConf = null;
|
|
|
|
public string $keydbPassword;
|
|
|
|
public string $image;
|
|
|
|
public ?string $portsMappings = null;
|
|
|
|
public ?bool $isPublic = null;
|
|
|
|
public mixed $publicPort = null;
|
|
|
|
public mixed $publicPortTimeout = 3600;
|
|
|
|
public ?string $customDockerRunOptions = null;
|
|
|
|
public bool $isLogDrainEnabled = false;
|
|
|
|
public bool $isPasswordHiddenForMember = false;
|
|
|
|
public function getListeners(): array
|
|
{
|
|
return $this->teamChannelListeners([
|
|
'DatabaseProxyStopped' => 'databaseProxyStopped',
|
|
]);
|
|
}
|
|
|
|
public function mount()
|
|
{
|
|
try {
|
|
$this->authorize('view', $this->database);
|
|
$this->syncData();
|
|
$this->server = data_get($this->database, 'destination.server');
|
|
if (! $this->server) {
|
|
$this->dispatch('error', 'Database destination server is not configured.');
|
|
|
|
return;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
|
|
$this->isPasswordHiddenForMember = auth()->user()?->isMember() ?? false;
|
|
}
|
|
|
|
protected function rules(): array
|
|
{
|
|
return [
|
|
'name' => ValidationPatterns::nameRules(),
|
|
'description' => ValidationPatterns::descriptionRules(),
|
|
'keydbConf' => 'nullable|string',
|
|
'keydbPassword' => ValidationPatterns::databasePasswordRules(
|
|
enforcePattern: $this->keydbPassword !== $this->database->keydb_password,
|
|
),
|
|
'image' => 'required|string',
|
|
'portsMappings' => ValidationPatterns::portMappingRules(),
|
|
'isPublic' => 'nullable|boolean',
|
|
'publicPort' => 'nullable|integer|min:1|max:65535',
|
|
'publicPortTimeout' => 'nullable|integer|min:1',
|
|
'customDockerRunOptions' => 'nullable|string',
|
|
'isLogDrainEnabled' => 'nullable|boolean',
|
|
];
|
|
}
|
|
|
|
protected function messages(): array
|
|
{
|
|
return array_merge(
|
|
ValidationPatterns::combinedMessages(),
|
|
ValidationPatterns::portMappingMessages(),
|
|
[
|
|
...ValidationPatterns::databasePasswordMessages('keydbPassword', 'KeyDB Password'),
|
|
'image.required' => 'The Docker Image field is required.',
|
|
'image.string' => 'The Docker Image must be a string.',
|
|
'publicPort.integer' => 'The Public Port must be an integer.',
|
|
'publicPort.min' => 'The Public Port must be at least 1.',
|
|
'publicPort.max' => 'The Public Port must not exceed 65535.',
|
|
'publicPortTimeout.integer' => 'The Public Port Timeout must be an integer.',
|
|
'publicPortTimeout.min' => 'The Public Port Timeout must be at least 1.',
|
|
]
|
|
);
|
|
}
|
|
|
|
private function syncData(bool $toModel = false): void
|
|
{
|
|
if ($toModel) {
|
|
$this->validate();
|
|
$this->database->name = $this->name;
|
|
$this->database->description = $this->description;
|
|
$this->database->keydb_conf = $this->keydbConf;
|
|
$this->database->keydb_password = $this->keydbPassword;
|
|
$this->database->image = $this->image;
|
|
$this->database->ports_mappings = $this->portsMappings;
|
|
$this->database->is_public = $this->isPublic;
|
|
$this->database->public_port = $this->publicPort ?: null;
|
|
$this->database->public_port_timeout = $this->publicPortTimeout ?: null;
|
|
$this->database->custom_docker_run_options = $this->customDockerRunOptions;
|
|
$this->database->is_log_drain_enabled = $this->isLogDrainEnabled;
|
|
$this->database->save();
|
|
} else {
|
|
$this->name = $this->database->name;
|
|
$this->description = $this->database->description;
|
|
$this->keydbConf = $this->database->keydb_conf;
|
|
$canSeeCredentials = auth()->user()?->can('update', $this->database) ?? false;
|
|
$this->keydbPassword = $canSeeCredentials ? $this->database->keydb_password : '';
|
|
$this->image = $this->database->image;
|
|
$this->portsMappings = $this->database->ports_mappings;
|
|
$this->isPublic = $this->database->is_public;
|
|
$this->publicPort = $this->database->public_port;
|
|
$this->publicPortTimeout = $this->database->public_port_timeout;
|
|
$this->customDockerRunOptions = $this->database->custom_docker_run_options;
|
|
$this->isLogDrainEnabled = $this->database->is_log_drain_enabled;
|
|
}
|
|
}
|
|
|
|
public function instantSaveAdvanced()
|
|
{
|
|
try {
|
|
$this->authorize('update', $this->database);
|
|
|
|
if (! $this->server->isLogDrainEnabled()) {
|
|
$this->isLogDrainEnabled = false;
|
|
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
|
|
|
return;
|
|
}
|
|
$this->syncData(true);
|
|
|
|
$this->dispatch('success', 'Database updated.');
|
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
|
} catch (Exception $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function instantSave()
|
|
{
|
|
try {
|
|
$this->authorize('update', $this->database);
|
|
|
|
if ($this->isPublic && ! $this->publicPort) {
|
|
$this->dispatch('error', 'Public port is required.');
|
|
$this->isPublic = false;
|
|
|
|
return;
|
|
}
|
|
if ($this->isPublic && ! str($this->database->status)->startsWith('running')) {
|
|
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
|
$this->isPublic = false;
|
|
|
|
return;
|
|
}
|
|
$this->syncData(true);
|
|
if ($this->isPublic) {
|
|
StartDatabaseProxy::run($this->database);
|
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
|
} else {
|
|
StopDatabaseProxy::run($this->database);
|
|
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
|
}
|
|
$this->dispatch('databaseUpdated');
|
|
} catch (\Throwable $e) {
|
|
$this->authorize('update', $this->database);
|
|
$this->isPublic = ! $this->isPublic;
|
|
$this->syncData(true);
|
|
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function databaseProxyStopped(): void
|
|
{
|
|
$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');
|
|
}
|
|
|
|
public function submit()
|
|
{
|
|
try {
|
|
$this->authorize('manageEnvironment', $this->database);
|
|
|
|
if ($this->portsMappings) {
|
|
$this->portsMappings = str($this->portsMappings)->replace(' ', '')->trim()->toString();
|
|
}
|
|
if (str($this->publicPort)->isEmpty()) {
|
|
$this->publicPort = null;
|
|
}
|
|
$this->syncData(true);
|
|
$this->dispatch('success', 'Database updated.');
|
|
$this->dispatch('databaseUpdated');
|
|
} catch (Exception $e) {
|
|
return handleError($e, $this);
|
|
} finally {
|
|
if (is_null($this->database->config_hash)) {
|
|
$this->database->isConfigurationChanged(true);
|
|
} else {
|
|
$this->dispatch('configurationChanged');
|
|
}
|
|
}
|
|
}
|
|
|
|
public function refresh(): void
|
|
{
|
|
$this->database->refresh();
|
|
$this->syncData();
|
|
}
|
|
}
|