diff --git a/app/Livewire/Project/Database/Keydb/General.php b/app/Livewire/Project/Database/Keydb/General.php index 91952533f..016dc4e01 100644 --- a/app/Livewire/Project/Database/Keydb/General.php +++ b/app/Livewire/Project/Database/Keydb/General.php @@ -58,7 +58,6 @@ public function getListeners() return [ "echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped', "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh', - 'refresh' => '$refresh', ]; } diff --git a/app/Livewire/Project/Database/Mariadb/General.php b/app/Livewire/Project/Database/Mariadb/General.php index 3ec6a9954..d565c6166 100644 --- a/app/Livewire/Project/Database/Mariadb/General.php +++ b/app/Livewire/Project/Database/Mariadb/General.php @@ -18,12 +18,38 @@ class General extends Component { use AuthorizesRequests; - protected $listeners = ['refresh']; - public Server $server; public StandaloneMariadb $database; + public string $name; + + public ?string $description = null; + + public string $mariadbRootPassword; + + public string $mariadbUser; + + public string $mariadbPassword; + + public string $mariadbDatabase; + + public ?string $mariadbConf = null; + + public string $image; + + public ?string $portsMappings = null; + + public ?bool $isPublic = null; + + public ?int $publicPort = null; + + public bool $isLogDrainEnabled = false; + + public ?string $customDockerRunOptions = null; + + public bool $enableSsl = false; + public ?string $db_url = null; public ?string $db_url_public = null; @@ -36,27 +62,26 @@ public function getListeners() return [ "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh', - 'refresh' => '$refresh', ]; } protected function rules(): array { return [ - 'database.name' => ValidationPatterns::nameRules(), - 'database.description' => ValidationPatterns::descriptionRules(), - 'database.mariadb_root_password' => 'required', - 'database.mariadb_user' => 'required', - 'database.mariadb_password' => 'required', - 'database.mariadb_database' => 'required', - 'database.mariadb_conf' => 'nullable', - 'database.image' => 'required', - 'database.ports_mappings' => 'nullable', - 'database.is_public' => 'nullable|boolean', - 'database.public_port' => 'nullable|integer', - 'database.is_log_drain_enabled' => 'nullable|boolean', - 'database.custom_docker_run_options' => 'nullable', - 'database.enable_ssl' => 'boolean', + 'name' => ValidationPatterns::nameRules(), + 'description' => ValidationPatterns::descriptionRules(), + 'mariadbRootPassword' => 'required', + 'mariadbUser' => 'required', + 'mariadbPassword' => 'required', + 'mariadbDatabase' => 'required', + 'mariadbConf' => 'nullable', + 'image' => 'required', + 'portsMappings' => 'nullable', + 'isPublic' => 'nullable|boolean', + 'publicPort' => 'nullable|integer', + 'isLogDrainEnabled' => 'nullable|boolean', + 'customDockerRunOptions' => 'nullable', + 'enableSsl' => 'boolean', ]; } @@ -65,45 +90,90 @@ protected function messages(): array return array_merge( ValidationPatterns::combinedMessages(), [ - 'database.name.required' => 'The Name field is required.', - 'database.name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', - 'database.description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', - 'database.mariadb_root_password.required' => 'The Root Password field is required.', - 'database.mariadb_user.required' => 'The MariaDB User field is required.', - 'database.mariadb_password.required' => 'The MariaDB Password field is required.', - 'database.mariadb_database.required' => 'The MariaDB Database field is required.', - 'database.image.required' => 'The Docker Image field is required.', - 'database.public_port.integer' => 'The Public Port must be an integer.', + 'name.required' => 'The Name field is required.', + 'name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', + 'description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', + 'mariadbRootPassword.required' => 'The Root Password field is required.', + 'mariadbUser.required' => 'The MariaDB User field is required.', + 'mariadbPassword.required' => 'The MariaDB Password field is required.', + 'mariadbDatabase.required' => 'The MariaDB Database field is required.', + 'image.required' => 'The Docker Image field is required.', + 'publicPort.integer' => 'The Public Port must be an integer.', ] ); } protected $validationAttributes = [ - 'database.name' => 'Name', - 'database.description' => 'Description', - 'database.mariadb_root_password' => 'Root Password', - 'database.mariadb_user' => 'User', - 'database.mariadb_password' => 'Password', - 'database.mariadb_database' => 'Database', - 'database.mariadb_conf' => 'MariaDB Configuration', - 'database.image' => 'Image', - 'database.ports_mappings' => 'Port Mapping', - 'database.is_public' => 'Is Public', - 'database.public_port' => 'Public Port', - 'database.custom_docker_run_options' => 'Custom Docker Options', - 'database.enable_ssl' => 'Enable SSL', + 'name' => 'Name', + 'description' => 'Description', + 'mariadbRootPassword' => 'Root Password', + 'mariadbUser' => 'User', + 'mariadbPassword' => 'Password', + 'mariadbDatabase' => 'Database', + 'mariadbConf' => 'MariaDB Configuration', + 'image' => 'Image', + 'portsMappings' => 'Port Mapping', + 'isPublic' => 'Is Public', + 'publicPort' => 'Public Port', + 'customDockerRunOptions' => 'Custom Docker Options', + 'enableSsl' => 'Enable SSL', ]; public function mount() { - $this->db_url = $this->database->internal_db_url; - $this->db_url_public = $this->database->external_db_url; - $this->server = data_get($this->database, 'destination.server'); + try { + $this->syncData(); + $this->server = data_get($this->database, 'destination.server'); - $existingCert = $this->database->sslCertificates()->first(); + $existingCert = $this->database->sslCertificates()->first(); - if ($existingCert) { - $this->certificateValidUntil = $existingCert->valid_until; + if ($existingCert) { + $this->certificateValidUntil = $existingCert->valid_until; + } + } catch (Exception $e) { + return handleError($e, $this); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->database->name = $this->name; + $this->database->description = $this->description; + $this->database->mariadb_root_password = $this->mariadbRootPassword; + $this->database->mariadb_user = $this->mariadbUser; + $this->database->mariadb_password = $this->mariadbPassword; + $this->database->mariadb_database = $this->mariadbDatabase; + $this->database->mariadb_conf = $this->mariadbConf; + $this->database->image = $this->image; + $this->database->ports_mappings = $this->portsMappings; + $this->database->is_public = $this->isPublic; + $this->database->public_port = $this->publicPort; + $this->database->is_log_drain_enabled = $this->isLogDrainEnabled; + $this->database->custom_docker_run_options = $this->customDockerRunOptions; + $this->database->enable_ssl = $this->enableSsl; + $this->database->save(); + + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; + } else { + $this->name = $this->database->name; + $this->description = $this->database->description; + $this->mariadbRootPassword = $this->database->mariadb_root_password; + $this->mariadbUser = $this->database->mariadb_user; + $this->mariadbPassword = $this->database->mariadb_password; + $this->mariadbDatabase = $this->database->mariadb_database; + $this->mariadbConf = $this->database->mariadb_conf; + $this->image = $this->database->image; + $this->portsMappings = $this->database->ports_mappings; + $this->isPublic = $this->database->is_public; + $this->publicPort = $this->database->public_port; + $this->isLogDrainEnabled = $this->database->is_log_drain_enabled; + $this->customDockerRunOptions = $this->database->custom_docker_run_options; + $this->enableSsl = $this->database->enable_ssl; + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; } } @@ -113,12 +183,12 @@ public function instantSaveAdvanced() $this->authorize('update', $this->database); if (! $this->server->isLogDrainEnabled()) { - $this->database->is_log_drain_enabled = false; + $this->isLogDrainEnabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); return; } - $this->database->save(); + $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) { @@ -131,11 +201,10 @@ public function submit() try { $this->authorize('update', $this->database); - if (str($this->database->public_port)->isEmpty()) { - $this->database->public_port = null; + if (str($this->publicPort)->isEmpty()) { + $this->publicPort = null; } - $this->validate(); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'Database updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -153,16 +222,16 @@ public function instantSave() try { $this->authorize('update', $this->database); - if ($this->database->is_public && ! $this->database->public_port) { + if ($this->isPublic && ! $this->publicPort) { $this->dispatch('error', 'Public port is required.'); - $this->database->is_public = false; + $this->isPublic = false; return; } - if ($this->database->is_public) { + if ($this->isPublic) { if (! str($this->database->status)->startsWith('running')) { $this->dispatch('error', 'Database must be started to be publicly accessible.'); - $this->database->is_public = false; + $this->isPublic = false; return; } @@ -172,10 +241,9 @@ public function instantSave() StopDatabaseProxy::run($this->database); $this->dispatch('success', 'Database is no longer publicly accessible.'); } - $this->db_url_public = $this->database->external_db_url; - $this->database->save(); + $this->syncData(true); } catch (\Throwable $e) { - $this->database->is_public = ! $this->database->is_public; + $this->isPublic = ! $this->isPublic; return handleError($e, $this); } @@ -186,7 +254,7 @@ public function instantSaveSSL() try { $this->authorize('update', $this->database); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'SSL configuration updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -230,6 +298,7 @@ public function regenerateSslCertificate() public function refresh(): void { $this->database->refresh(); + $this->syncData(); } public function render() diff --git a/app/Livewire/Project/Database/Mongodb/General.php b/app/Livewire/Project/Database/Mongodb/General.php index be52cfa7a..f5ecfba1c 100644 --- a/app/Livewire/Project/Database/Mongodb/General.php +++ b/app/Livewire/Project/Database/Mongodb/General.php @@ -18,12 +18,38 @@ class General extends Component { use AuthorizesRequests; - protected $listeners = ['refresh']; - public Server $server; public StandaloneMongodb $database; + public string $name; + + public ?string $description = null; + + public ?string $mongoConf = null; + + public string $mongoInitdbRootUsername; + + public string $mongoInitdbRootPassword; + + public string $mongoInitdbDatabase; + + public string $image; + + public ?string $portsMappings = null; + + public ?bool $isPublic = null; + + public ?int $publicPort = null; + + public bool $isLogDrainEnabled = false; + + public ?string $customDockerRunOptions = null; + + public bool $enableSsl = false; + + public ?string $sslMode = null; + public ?string $db_url = null; public ?string $db_url_public = null; @@ -36,27 +62,26 @@ public function getListeners() return [ "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh', - 'refresh' => '$refresh', ]; } protected function rules(): array { return [ - 'database.name' => ValidationPatterns::nameRules(), - 'database.description' => ValidationPatterns::descriptionRules(), - 'database.mongo_conf' => 'nullable', - 'database.mongo_initdb_root_username' => 'required', - 'database.mongo_initdb_root_password' => 'required', - 'database.mongo_initdb_database' => 'required', - 'database.image' => 'required', - 'database.ports_mappings' => 'nullable', - 'database.is_public' => 'nullable|boolean', - 'database.public_port' => 'nullable|integer', - 'database.is_log_drain_enabled' => 'nullable|boolean', - 'database.custom_docker_run_options' => 'nullable', - 'database.enable_ssl' => 'boolean', - 'database.ssl_mode' => 'nullable|string|in:allow,prefer,require,verify-full', + 'name' => ValidationPatterns::nameRules(), + 'description' => ValidationPatterns::descriptionRules(), + 'mongoConf' => 'nullable', + 'mongoInitdbRootUsername' => 'required', + 'mongoInitdbRootPassword' => 'required', + 'mongoInitdbDatabase' => 'required', + 'image' => 'required', + 'portsMappings' => 'nullable', + 'isPublic' => 'nullable|boolean', + 'publicPort' => 'nullable|integer', + 'isLogDrainEnabled' => 'nullable|boolean', + 'customDockerRunOptions' => 'nullable', + 'enableSsl' => 'boolean', + 'sslMode' => 'nullable|string|in:allow,prefer,require,verify-full', ]; } @@ -65,45 +90,90 @@ protected function messages(): array return array_merge( ValidationPatterns::combinedMessages(), [ - 'database.name.required' => 'The Name field is required.', - 'database.name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', - 'database.description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', - 'database.mongo_initdb_root_username.required' => 'The Root Username field is required.', - 'database.mongo_initdb_root_password.required' => 'The Root Password field is required.', - 'database.mongo_initdb_database.required' => 'The MongoDB Database field is required.', - 'database.image.required' => 'The Docker Image field is required.', - 'database.public_port.integer' => 'The Public Port must be an integer.', - 'database.ssl_mode.in' => 'The SSL Mode must be one of: allow, prefer, require, verify-full.', + 'name.required' => 'The Name field is required.', + 'name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', + 'description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', + 'mongoInitdbRootUsername.required' => 'The Root Username field is required.', + 'mongoInitdbRootPassword.required' => 'The Root Password field is required.', + 'mongoInitdbDatabase.required' => 'The MongoDB Database field is required.', + 'image.required' => 'The Docker Image field is required.', + 'publicPort.integer' => 'The Public Port must be an integer.', + 'sslMode.in' => 'The SSL Mode must be one of: allow, prefer, require, verify-full.', ] ); } protected $validationAttributes = [ - 'database.name' => 'Name', - 'database.description' => 'Description', - 'database.mongo_conf' => 'Mongo Configuration', - 'database.mongo_initdb_root_username' => 'Root Username', - 'database.mongo_initdb_root_password' => 'Root Password', - 'database.mongo_initdb_database' => 'Database', - 'database.image' => 'Image', - 'database.ports_mappings' => 'Port Mapping', - 'database.is_public' => 'Is Public', - 'database.public_port' => 'Public Port', - 'database.custom_docker_run_options' => 'Custom Docker Run Options', - 'database.enable_ssl' => 'Enable SSL', - 'database.ssl_mode' => 'SSL Mode', + 'name' => 'Name', + 'description' => 'Description', + 'mongoConf' => 'Mongo Configuration', + 'mongoInitdbRootUsername' => 'Root Username', + 'mongoInitdbRootPassword' => 'Root Password', + 'mongoInitdbDatabase' => 'Database', + 'image' => 'Image', + 'portsMappings' => 'Port Mapping', + 'isPublic' => 'Is Public', + 'publicPort' => 'Public Port', + 'customDockerRunOptions' => 'Custom Docker Run Options', + 'enableSsl' => 'Enable SSL', + 'sslMode' => 'SSL Mode', ]; public function mount() { - $this->db_url = $this->database->internal_db_url; - $this->db_url_public = $this->database->external_db_url; - $this->server = data_get($this->database, 'destination.server'); + try { + $this->syncData(); + $this->server = data_get($this->database, 'destination.server'); - $existingCert = $this->database->sslCertificates()->first(); + $existingCert = $this->database->sslCertificates()->first(); - if ($existingCert) { - $this->certificateValidUntil = $existingCert->valid_until; + if ($existingCert) { + $this->certificateValidUntil = $existingCert->valid_until; + } + } catch (Exception $e) { + return handleError($e, $this); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->database->name = $this->name; + $this->database->description = $this->description; + $this->database->mongo_conf = $this->mongoConf; + $this->database->mongo_initdb_root_username = $this->mongoInitdbRootUsername; + $this->database->mongo_initdb_root_password = $this->mongoInitdbRootPassword; + $this->database->mongo_initdb_database = $this->mongoInitdbDatabase; + $this->database->image = $this->image; + $this->database->ports_mappings = $this->portsMappings; + $this->database->is_public = $this->isPublic; + $this->database->public_port = $this->publicPort; + $this->database->is_log_drain_enabled = $this->isLogDrainEnabled; + $this->database->custom_docker_run_options = $this->customDockerRunOptions; + $this->database->enable_ssl = $this->enableSsl; + $this->database->ssl_mode = $this->sslMode; + $this->database->save(); + + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; + } else { + $this->name = $this->database->name; + $this->description = $this->database->description; + $this->mongoConf = $this->database->mongo_conf; + $this->mongoInitdbRootUsername = $this->database->mongo_initdb_root_username; + $this->mongoInitdbRootPassword = $this->database->mongo_initdb_root_password; + $this->mongoInitdbDatabase = $this->database->mongo_initdb_database; + $this->image = $this->database->image; + $this->portsMappings = $this->database->ports_mappings; + $this->isPublic = $this->database->is_public; + $this->publicPort = $this->database->public_port; + $this->isLogDrainEnabled = $this->database->is_log_drain_enabled; + $this->customDockerRunOptions = $this->database->custom_docker_run_options; + $this->enableSsl = $this->database->enable_ssl; + $this->sslMode = $this->database->ssl_mode; + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; } } @@ -113,12 +183,12 @@ public function instantSaveAdvanced() $this->authorize('update', $this->database); if (! $this->server->isLogDrainEnabled()) { - $this->database->is_log_drain_enabled = false; + $this->isLogDrainEnabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); return; } - $this->database->save(); + $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) { @@ -131,14 +201,13 @@ public function submit() try { $this->authorize('update', $this->database); - if (str($this->database->public_port)->isEmpty()) { - $this->database->public_port = null; + if (str($this->publicPort)->isEmpty()) { + $this->publicPort = null; } - if (str($this->database->mongo_conf)->isEmpty()) { - $this->database->mongo_conf = null; + if (str($this->mongoConf)->isEmpty()) { + $this->mongoConf = null; } - $this->validate(); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'Database updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -156,16 +225,16 @@ public function instantSave() try { $this->authorize('update', $this->database); - if ($this->database->is_public && ! $this->database->public_port) { + if ($this->isPublic && ! $this->publicPort) { $this->dispatch('error', 'Public port is required.'); - $this->database->is_public = false; + $this->isPublic = false; return; } - if ($this->database->is_public) { + if ($this->isPublic) { if (! str($this->database->status)->startsWith('running')) { $this->dispatch('error', 'Database must be started to be publicly accessible.'); - $this->database->is_public = false; + $this->isPublic = false; return; } @@ -175,16 +244,15 @@ public function instantSave() StopDatabaseProxy::run($this->database); $this->dispatch('success', 'Database is no longer publicly accessible.'); } - $this->db_url_public = $this->database->external_db_url; - $this->database->save(); + $this->syncData(true); } catch (\Throwable $e) { - $this->database->is_public = ! $this->database->is_public; + $this->isPublic = ! $this->isPublic; return handleError($e, $this); } } - public function updatedDatabaseSslMode() + public function updatedSslMode() { $this->instantSaveSSL(); } @@ -194,7 +262,7 @@ public function instantSaveSSL() try { $this->authorize('update', $this->database); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'SSL configuration updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -238,6 +306,7 @@ public function regenerateSslCertificate() public function refresh(): void { $this->database->refresh(); + $this->syncData(); } public function render() diff --git a/app/Livewire/Project/Database/Mysql/General.php b/app/Livewire/Project/Database/Mysql/General.php index e6b0ead24..22c6eb39e 100644 --- a/app/Livewire/Project/Database/Mysql/General.php +++ b/app/Livewire/Project/Database/Mysql/General.php @@ -18,12 +18,40 @@ class General extends Component { use AuthorizesRequests; - protected $listeners = ['refresh']; - public StandaloneMysql $database; public Server $server; + public string $name; + + public ?string $description = null; + + public string $mysqlRootPassword; + + public string $mysqlUser; + + public string $mysqlPassword; + + public string $mysqlDatabase; + + public ?string $mysqlConf = null; + + public string $image; + + public ?string $portsMappings = null; + + public ?bool $isPublic = null; + + public ?int $publicPort = null; + + public bool $isLogDrainEnabled = false; + + public ?string $customDockerRunOptions = null; + + public bool $enableSsl = false; + + public ?string $sslMode = null; + public ?string $db_url = null; public ?string $db_url_public = null; @@ -36,28 +64,27 @@ public function getListeners() return [ "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh', - 'refresh' => '$refresh', ]; } protected function rules(): array { return [ - 'database.name' => ValidationPatterns::nameRules(), - 'database.description' => ValidationPatterns::descriptionRules(), - 'database.mysql_root_password' => 'required', - 'database.mysql_user' => 'required', - 'database.mysql_password' => 'required', - 'database.mysql_database' => 'required', - 'database.mysql_conf' => 'nullable', - 'database.image' => 'required', - 'database.ports_mappings' => 'nullable', - 'database.is_public' => 'nullable|boolean', - 'database.public_port' => 'nullable|integer', - 'database.is_log_drain_enabled' => 'nullable|boolean', - 'database.custom_docker_run_options' => 'nullable', - 'database.enable_ssl' => 'boolean', - 'database.ssl_mode' => 'nullable|string|in:PREFERRED,REQUIRED,VERIFY_CA,VERIFY_IDENTITY', + 'name' => ValidationPatterns::nameRules(), + 'description' => ValidationPatterns::descriptionRules(), + 'mysqlRootPassword' => 'required', + 'mysqlUser' => 'required', + 'mysqlPassword' => 'required', + 'mysqlDatabase' => 'required', + 'mysqlConf' => 'nullable', + 'image' => 'required', + 'portsMappings' => 'nullable', + 'isPublic' => 'nullable|boolean', + 'publicPort' => 'nullable|integer', + 'isLogDrainEnabled' => 'nullable|boolean', + 'customDockerRunOptions' => 'nullable', + 'enableSsl' => 'boolean', + 'sslMode' => 'nullable|string|in:PREFERRED,REQUIRED,VERIFY_CA,VERIFY_IDENTITY', ]; } @@ -66,47 +93,94 @@ protected function messages(): array return array_merge( ValidationPatterns::combinedMessages(), [ - 'database.name.required' => 'The Name field is required.', - 'database.name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', - 'database.description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', - 'database.mysql_root_password.required' => 'The Root Password field is required.', - 'database.mysql_user.required' => 'The MySQL User field is required.', - 'database.mysql_password.required' => 'The MySQL Password field is required.', - 'database.mysql_database.required' => 'The MySQL Database field is required.', - 'database.image.required' => 'The Docker Image field is required.', - 'database.public_port.integer' => 'The Public Port must be an integer.', - 'database.ssl_mode.in' => 'The SSL Mode must be one of: PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY.', + 'name.required' => 'The Name field is required.', + 'name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', + 'description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', + 'mysqlRootPassword.required' => 'The Root Password field is required.', + 'mysqlUser.required' => 'The MySQL User field is required.', + 'mysqlPassword.required' => 'The MySQL Password field is required.', + 'mysqlDatabase.required' => 'The MySQL Database field is required.', + 'image.required' => 'The Docker Image field is required.', + 'publicPort.integer' => 'The Public Port must be an integer.', + 'sslMode.in' => 'The SSL Mode must be one of: PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY.', ] ); } protected $validationAttributes = [ - 'database.name' => 'Name', - 'database.description' => 'Description', - 'database.mysql_root_password' => 'Root Password', - 'database.mysql_user' => 'User', - 'database.mysql_password' => 'Password', - 'database.mysql_database' => 'Database', - 'database.mysql_conf' => 'MySQL Configuration', - 'database.image' => 'Image', - 'database.ports_mappings' => 'Port Mapping', - 'database.is_public' => 'Is Public', - 'database.public_port' => 'Public Port', - 'database.custom_docker_run_options' => 'Custom Docker Run Options', - 'database.enable_ssl' => 'Enable SSL', - 'database.ssl_mode' => 'SSL Mode', + 'name' => 'Name', + 'description' => 'Description', + 'mysqlRootPassword' => 'Root Password', + 'mysqlUser' => 'User', + 'mysqlPassword' => 'Password', + 'mysqlDatabase' => 'Database', + 'mysqlConf' => 'MySQL Configuration', + 'image' => 'Image', + 'portsMappings' => 'Port Mapping', + 'isPublic' => 'Is Public', + 'publicPort' => 'Public Port', + 'customDockerRunOptions' => 'Custom Docker Run Options', + 'enableSsl' => 'Enable SSL', + 'sslMode' => 'SSL Mode', ]; public function mount() { - $this->db_url = $this->database->internal_db_url; - $this->db_url_public = $this->database->external_db_url; - $this->server = data_get($this->database, 'destination.server'); + try { + $this->syncData(); + $this->server = data_get($this->database, 'destination.server'); - $existingCert = $this->database->sslCertificates()->first(); + $existingCert = $this->database->sslCertificates()->first(); - if ($existingCert) { - $this->certificateValidUntil = $existingCert->valid_until; + if ($existingCert) { + $this->certificateValidUntil = $existingCert->valid_until; + } + } catch (Exception $e) { + return handleError($e, $this); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->database->name = $this->name; + $this->database->description = $this->description; + $this->database->mysql_root_password = $this->mysqlRootPassword; + $this->database->mysql_user = $this->mysqlUser; + $this->database->mysql_password = $this->mysqlPassword; + $this->database->mysql_database = $this->mysqlDatabase; + $this->database->mysql_conf = $this->mysqlConf; + $this->database->image = $this->image; + $this->database->ports_mappings = $this->portsMappings; + $this->database->is_public = $this->isPublic; + $this->database->public_port = $this->publicPort; + $this->database->is_log_drain_enabled = $this->isLogDrainEnabled; + $this->database->custom_docker_run_options = $this->customDockerRunOptions; + $this->database->enable_ssl = $this->enableSsl; + $this->database->ssl_mode = $this->sslMode; + $this->database->save(); + + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; + } else { + $this->name = $this->database->name; + $this->description = $this->database->description; + $this->mysqlRootPassword = $this->database->mysql_root_password; + $this->mysqlUser = $this->database->mysql_user; + $this->mysqlPassword = $this->database->mysql_password; + $this->mysqlDatabase = $this->database->mysql_database; + $this->mysqlConf = $this->database->mysql_conf; + $this->image = $this->database->image; + $this->portsMappings = $this->database->ports_mappings; + $this->isPublic = $this->database->is_public; + $this->publicPort = $this->database->public_port; + $this->isLogDrainEnabled = $this->database->is_log_drain_enabled; + $this->customDockerRunOptions = $this->database->custom_docker_run_options; + $this->enableSsl = $this->database->enable_ssl; + $this->sslMode = $this->database->ssl_mode; + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; } } @@ -116,12 +190,12 @@ public function instantSaveAdvanced() $this->authorize('update', $this->database); if (! $this->server->isLogDrainEnabled()) { - $this->database->is_log_drain_enabled = false; + $this->isLogDrainEnabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); return; } - $this->database->save(); + $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) { @@ -134,11 +208,10 @@ public function submit() try { $this->authorize('update', $this->database); - if (str($this->database->public_port)->isEmpty()) { - $this->database->public_port = null; + if (str($this->publicPort)->isEmpty()) { + $this->publicPort = null; } - $this->validate(); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'Database updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -156,16 +229,16 @@ public function instantSave() try { $this->authorize('update', $this->database); - if ($this->database->is_public && ! $this->database->public_port) { + if ($this->isPublic && ! $this->publicPort) { $this->dispatch('error', 'Public port is required.'); - $this->database->is_public = false; + $this->isPublic = false; return; } - if ($this->database->is_public) { + if ($this->isPublic) { if (! str($this->database->status)->startsWith('running')) { $this->dispatch('error', 'Database must be started to be publicly accessible.'); - $this->database->is_public = false; + $this->isPublic = false; return; } @@ -175,16 +248,15 @@ public function instantSave() StopDatabaseProxy::run($this->database); $this->dispatch('success', 'Database is no longer publicly accessible.'); } - $this->db_url_public = $this->database->external_db_url; - $this->database->save(); + $this->syncData(true); } catch (\Throwable $e) { - $this->database->is_public = ! $this->database->is_public; + $this->isPublic = ! $this->isPublic; return handleError($e, $this); } } - public function updatedDatabaseSslMode() + public function updatedSslMode() { $this->instantSaveSSL(); } @@ -194,7 +266,7 @@ public function instantSaveSSL() try { $this->authorize('update', $this->database); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'SSL configuration updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -238,6 +310,7 @@ public function regenerateSslCertificate() public function refresh(): void { $this->database->refresh(); + $this->syncData(); } public function render() diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index 06c16a658..5314d1084 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -22,6 +22,40 @@ class General extends Component public Server $server; + public string $name; + + public ?string $description = null; + + public string $postgresUser; + + public string $postgresPassword; + + public string $postgresDb; + + public ?string $postgresInitdbArgs = null; + + public ?string $postgresHostAuthMethod = null; + + public ?string $postgresConf = null; + + public ?array $initScripts = null; + + public string $image; + + public ?string $portsMappings = null; + + public ?bool $isPublic = null; + + public ?int $publicPort = null; + + public bool $isLogDrainEnabled = false; + + public ?string $customDockerRunOptions = null; + + public bool $enableSsl = false; + + public ?string $sslMode = null; + public string $new_filename; public string $new_content; @@ -38,7 +72,6 @@ public function getListeners() return [ "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh', - 'refresh' => '$refresh', 'save_init_script', 'delete_init_script', ]; @@ -47,23 +80,23 @@ public function getListeners() protected function rules(): array { return [ - 'database.name' => ValidationPatterns::nameRules(), - 'database.description' => ValidationPatterns::descriptionRules(), - 'database.postgres_user' => 'required', - 'database.postgres_password' => 'required', - 'database.postgres_db' => 'required', - 'database.postgres_initdb_args' => 'nullable', - 'database.postgres_host_auth_method' => 'nullable', - 'database.postgres_conf' => 'nullable', - 'database.init_scripts' => 'nullable', - 'database.image' => 'required', - 'database.ports_mappings' => 'nullable', - 'database.is_public' => 'nullable|boolean', - 'database.public_port' => 'nullable|integer', - 'database.is_log_drain_enabled' => 'nullable|boolean', - 'database.custom_docker_run_options' => 'nullable', - 'database.enable_ssl' => 'boolean', - 'database.ssl_mode' => 'nullable|string|in:allow,prefer,require,verify-ca,verify-full', + 'name' => ValidationPatterns::nameRules(), + 'description' => ValidationPatterns::descriptionRules(), + 'postgresUser' => 'required', + 'postgresPassword' => 'required', + 'postgresDb' => 'required', + 'postgresInitdbArgs' => 'nullable', + 'postgresHostAuthMethod' => 'nullable', + 'postgresConf' => 'nullable', + 'initScripts' => 'nullable', + 'image' => 'required', + 'portsMappings' => 'nullable', + 'isPublic' => 'nullable|boolean', + 'publicPort' => 'nullable|integer', + 'isLogDrainEnabled' => 'nullable|boolean', + 'customDockerRunOptions' => 'nullable', + 'enableSsl' => 'boolean', + 'sslMode' => 'nullable|string|in:allow,prefer,require,verify-ca,verify-full', ]; } @@ -72,48 +105,99 @@ protected function messages(): array return array_merge( ValidationPatterns::combinedMessages(), [ - 'database.name.required' => 'The Name field is required.', - 'database.name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', - 'database.description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', - 'database.postgres_user.required' => 'The Postgres User field is required.', - 'database.postgres_password.required' => 'The Postgres Password field is required.', - 'database.postgres_db.required' => 'The Postgres Database field is required.', - 'database.image.required' => 'The Docker Image field is required.', - 'database.public_port.integer' => 'The Public Port must be an integer.', - 'database.ssl_mode.in' => 'The SSL Mode must be one of: allow, prefer, require, verify-ca, verify-full.', + 'name.required' => 'The Name field is required.', + 'name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', + 'description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', + 'postgresUser.required' => 'The Postgres User field is required.', + 'postgresPassword.required' => 'The Postgres Password field is required.', + 'postgresDb.required' => 'The Postgres Database field is required.', + 'image.required' => 'The Docker Image field is required.', + 'publicPort.integer' => 'The Public Port must be an integer.', + 'sslMode.in' => 'The SSL Mode must be one of: allow, prefer, require, verify-ca, verify-full.', ] ); } protected $validationAttributes = [ - 'database.name' => 'Name', - 'database.description' => 'Description', - 'database.postgres_user' => 'Postgres User', - 'database.postgres_password' => 'Postgres Password', - 'database.postgres_db' => 'Postgres DB', - 'database.postgres_initdb_args' => 'Postgres Initdb Args', - 'database.postgres_host_auth_method' => 'Postgres Host Auth Method', - 'database.postgres_conf' => 'Postgres Configuration', - 'database.init_scripts' => 'Init Scripts', - 'database.image' => 'Image', - 'database.ports_mappings' => 'Port Mapping', - 'database.is_public' => 'Is Public', - 'database.public_port' => 'Public Port', - 'database.custom_docker_run_options' => 'Custom Docker Run Options', - 'database.enable_ssl' => 'Enable SSL', - 'database.ssl_mode' => 'SSL Mode', + 'name' => 'Name', + 'description' => 'Description', + 'postgresUser' => 'Postgres User', + 'postgresPassword' => 'Postgres Password', + 'postgresDb' => 'Postgres DB', + 'postgresInitdbArgs' => 'Postgres Initdb Args', + 'postgresHostAuthMethod' => 'Postgres Host Auth Method', + 'postgresConf' => 'Postgres Configuration', + 'initScripts' => 'Init Scripts', + 'image' => 'Image', + 'portsMappings' => 'Port Mapping', + 'isPublic' => 'Is Public', + 'publicPort' => 'Public Port', + 'customDockerRunOptions' => 'Custom Docker Run Options', + 'enableSsl' => 'Enable SSL', + 'sslMode' => 'SSL Mode', ]; public function mount() { - $this->db_url = $this->database->internal_db_url; - $this->db_url_public = $this->database->external_db_url; - $this->server = data_get($this->database, 'destination.server'); + try { + $this->syncData(); + $this->server = data_get($this->database, 'destination.server'); - $existingCert = $this->database->sslCertificates()->first(); + $existingCert = $this->database->sslCertificates()->first(); - if ($existingCert) { - $this->certificateValidUntil = $existingCert->valid_until; + if ($existingCert) { + $this->certificateValidUntil = $existingCert->valid_until; + } + } catch (Exception $e) { + return handleError($e, $this); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->database->name = $this->name; + $this->database->description = $this->description; + $this->database->postgres_user = $this->postgresUser; + $this->database->postgres_password = $this->postgresPassword; + $this->database->postgres_db = $this->postgresDb; + $this->database->postgres_initdb_args = $this->postgresInitdbArgs; + $this->database->postgres_host_auth_method = $this->postgresHostAuthMethod; + $this->database->postgres_conf = $this->postgresConf; + $this->database->init_scripts = $this->initScripts; + $this->database->image = $this->image; + $this->database->ports_mappings = $this->portsMappings; + $this->database->is_public = $this->isPublic; + $this->database->public_port = $this->publicPort; + $this->database->is_log_drain_enabled = $this->isLogDrainEnabled; + $this->database->custom_docker_run_options = $this->customDockerRunOptions; + $this->database->enable_ssl = $this->enableSsl; + $this->database->ssl_mode = $this->sslMode; + $this->database->save(); + + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; + } else { + $this->name = $this->database->name; + $this->description = $this->database->description; + $this->postgresUser = $this->database->postgres_user; + $this->postgresPassword = $this->database->postgres_password; + $this->postgresDb = $this->database->postgres_db; + $this->postgresInitdbArgs = $this->database->postgres_initdb_args; + $this->postgresHostAuthMethod = $this->database->postgres_host_auth_method; + $this->postgresConf = $this->database->postgres_conf; + $this->initScripts = $this->database->init_scripts; + $this->image = $this->database->image; + $this->portsMappings = $this->database->ports_mappings; + $this->isPublic = $this->database->is_public; + $this->publicPort = $this->database->public_port; + $this->isLogDrainEnabled = $this->database->is_log_drain_enabled; + $this->customDockerRunOptions = $this->database->custom_docker_run_options; + $this->enableSsl = $this->database->enable_ssl; + $this->sslMode = $this->database->ssl_mode; + $this->db_url = $this->database->internal_db_url; + $this->db_url_public = $this->database->external_db_url; } } @@ -123,12 +207,12 @@ public function instantSaveAdvanced() $this->authorize('update', $this->database); if (! $this->server->isLogDrainEnabled()) { - $this->database->is_log_drain_enabled = false; + $this->isLogDrainEnabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); return; } - $this->database->save(); + $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) { @@ -136,7 +220,7 @@ public function instantSaveAdvanced() } } - public function updatedDatabaseSslMode() + public function updatedSslMode() { $this->instantSaveSSL(); } @@ -146,10 +230,8 @@ public function instantSaveSSL() try { $this->authorize('update', $this->database); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'SSL configuration updated.'); - $this->db_url = $this->database->internal_db_url; - $this->db_url_public = $this->database->external_db_url; } catch (Exception $e) { return handleError($e, $this); } @@ -194,16 +276,16 @@ public function instantSave() try { $this->authorize('update', $this->database); - if ($this->database->is_public && ! $this->database->public_port) { + if ($this->isPublic && ! $this->publicPort) { $this->dispatch('error', 'Public port is required.'); - $this->database->is_public = false; + $this->isPublic = false; return; } - if ($this->database->is_public) { + if ($this->isPublic) { if (! str($this->database->status)->startsWith('running')) { $this->dispatch('error', 'Database must be started to be publicly accessible.'); - $this->database->is_public = false; + $this->isPublic = false; return; } @@ -213,10 +295,9 @@ public function instantSave() StopDatabaseProxy::run($this->database); $this->dispatch('success', 'Database is no longer publicly accessible.'); } - $this->db_url_public = $this->database->external_db_url; - $this->database->save(); + $this->syncData(true); } catch (\Throwable $e) { - $this->database->is_public = ! $this->database->is_public; + $this->isPublic = ! $this->isPublic; return handleError($e, $this); } @@ -226,7 +307,7 @@ public function save_init_script($script) { $this->authorize('update', $this->database); - $initScripts = collect($this->database->init_scripts ?? []); + $initScripts = collect($this->initScripts ?? []); $existingScript = $initScripts->firstWhere('filename', $script['filename']); $oldScript = $initScripts->firstWhere('index', $script['index']); @@ -262,7 +343,7 @@ public function save_init_script($script) $initScripts->push($script); } - $this->database->init_scripts = $initScripts->values() + $this->initScripts = $initScripts->values() ->map(function ($item, $index) { $item['index'] = $index; @@ -270,7 +351,7 @@ public function save_init_script($script) }) ->all(); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'Init script saved and updated.'); } @@ -278,7 +359,7 @@ public function delete_init_script($script) { $this->authorize('update', $this->database); - $collection = collect($this->database->init_scripts); + $collection = collect($this->initScripts); $found = $collection->firstWhere('filename', $script['filename']); if ($found) { $container_name = $this->database->uuid; @@ -303,8 +384,8 @@ public function delete_init_script($script) }) ->all(); - $this->database->init_scripts = $updatedScripts; - $this->database->save(); + $this->initScripts = $updatedScripts; + $this->syncData(true); $this->dispatch('refresh')->self(); $this->dispatch('success', 'Init script deleted from the database and the server.'); } @@ -318,23 +399,23 @@ public function save_new_init_script() 'new_filename' => 'required|string', 'new_content' => 'required|string', ]); - $found = collect($this->database->init_scripts)->firstWhere('filename', $this->new_filename); + $found = collect($this->initScripts)->firstWhere('filename', $this->new_filename); if ($found) { $this->dispatch('error', 'Filename already exists.'); return; } - if (! isset($this->database->init_scripts)) { - $this->database->init_scripts = []; + if (! isset($this->initScripts)) { + $this->initScripts = []; } - $this->database->init_scripts = array_merge($this->database->init_scripts, [ + $this->initScripts = array_merge($this->initScripts, [ [ - 'index' => count($this->database->init_scripts), + 'index' => count($this->initScripts), 'filename' => $this->new_filename, 'content' => $this->new_content, ], ]); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'Init script added.'); $this->new_content = ''; $this->new_filename = ''; @@ -345,11 +426,10 @@ public function submit() try { $this->authorize('update', $this->database); - if (str($this->database->public_port)->isEmpty()) { - $this->database->public_port = null; + if (str($this->publicPort)->isEmpty()) { + $this->publicPort = null; } - $this->validate(); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'Database updated.'); } catch (Exception $e) { return handleError($e, $this); diff --git a/app/Livewire/Project/Database/Redis/General.php b/app/Livewire/Project/Database/Redis/General.php index 4cb93e836..a24a977ad 100644 --- a/app/Livewire/Project/Database/Redis/General.php +++ b/app/Livewire/Project/Database/Redis/General.php @@ -22,15 +22,35 @@ class General extends Component public StandaloneRedis $database; - public string $redis_username; + public string $name; - public ?string $redis_password; + public ?string $description = null; - public string $redis_version; + public ?string $redisConf = null; - public ?string $db_url = null; + public string $image; - public ?string $db_url_public = null; + public ?string $portsMappings = null; + + public ?bool $isPublic = null; + + public ?int $publicPort = null; + + public bool $isLogDrainEnabled = false; + + public ?string $customDockerRunOptions = null; + + public string $redisUsername; + + public string $redisPassword; + + public string $redisVersion; + + public ?string $dbUrl = null; + + public ?string $dbUrlPublic = null; + + public bool $enableSsl = false; public ?Carbon $certificateValidUntil = null; @@ -41,25 +61,24 @@ public function getListeners() return [ "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh', 'envsUpdated' => 'refresh', - 'refresh', ]; } protected function rules(): array { return [ - 'database.name' => ValidationPatterns::nameRules(), - 'database.description' => ValidationPatterns::descriptionRules(), - 'database.redis_conf' => 'nullable', - 'database.image' => 'required', - 'database.ports_mappings' => 'nullable', - 'database.is_public' => 'nullable|boolean', - 'database.public_port' => 'nullable|integer', - 'database.is_log_drain_enabled' => 'nullable|boolean', - 'database.custom_docker_run_options' => 'nullable', - 'redis_username' => 'required', - 'redis_password' => 'required', - 'database.enable_ssl' => 'boolean', + 'name' => ValidationPatterns::nameRules(), + 'description' => ValidationPatterns::descriptionRules(), + 'redisConf' => 'nullable', + 'image' => 'required', + 'portsMappings' => 'nullable', + 'isPublic' => 'nullable|boolean', + 'publicPort' => 'nullable|integer', + 'isLogDrainEnabled' => 'nullable|boolean', + 'customDockerRunOptions' => 'nullable', + 'redisUsername' => 'required', + 'redisPassword' => 'required', + 'enableSsl' => 'boolean', ]; } @@ -68,39 +87,81 @@ protected function messages(): array return array_merge( ValidationPatterns::combinedMessages(), [ - 'database.name.required' => 'The Name field is required.', - 'database.name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', - 'database.description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', - 'database.image.required' => 'The Docker Image field is required.', - 'database.public_port.integer' => 'The Public Port must be an integer.', - 'redis_username.required' => 'The Redis Username field is required.', - 'redis_password.required' => 'The Redis Password field is required.', + 'name.required' => 'The Name field is required.', + 'name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().', + 'description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.', + 'image.required' => 'The Docker Image field is required.', + 'publicPort.integer' => 'The Public Port must be an integer.', + 'redisUsername.required' => 'The Redis Username field is required.', + 'redisPassword.required' => 'The Redis Password field is required.', ] ); } protected $validationAttributes = [ - 'database.name' => 'Name', - 'database.description' => 'Description', - 'database.redis_conf' => 'Redis Configuration', - 'database.image' => 'Image', - 'database.ports_mappings' => 'Port Mapping', - 'database.is_public' => 'Is Public', - 'database.public_port' => 'Public Port', - 'database.custom_docker_run_options' => 'Custom Docker Options', - 'redis_username' => 'Redis Username', - 'redis_password' => 'Redis Password', - 'database.enable_ssl' => 'Enable SSL', + 'name' => 'Name', + 'description' => 'Description', + 'redisConf' => 'Redis Configuration', + 'image' => 'Image', + 'portsMappings' => 'Port Mapping', + 'isPublic' => 'Is Public', + 'publicPort' => 'Public Port', + 'customDockerRunOptions' => 'Custom Docker Options', + 'redisUsername' => 'Redis Username', + 'redisPassword' => 'Redis Password', + 'enableSsl' => 'Enable SSL', ]; public function mount() { - $this->server = data_get($this->database, 'destination.server'); - $this->refreshView(); - $existingCert = $this->database->sslCertificates()->first(); + try { + $this->syncData(); + $this->server = data_get($this->database, 'destination.server'); - if ($existingCert) { - $this->certificateValidUntil = $existingCert->valid_until; + $existingCert = $this->database->sslCertificates()->first(); + + if ($existingCert) { + $this->certificateValidUntil = $existingCert->valid_until; + } + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->database->name = $this->name; + $this->database->description = $this->description; + $this->database->redis_conf = $this->redisConf; + $this->database->image = $this->image; + $this->database->ports_mappings = $this->portsMappings; + $this->database->is_public = $this->isPublic; + $this->database->public_port = $this->publicPort; + $this->database->is_log_drain_enabled = $this->isLogDrainEnabled; + $this->database->custom_docker_run_options = $this->customDockerRunOptions; + $this->database->enable_ssl = $this->enableSsl; + $this->database->save(); + + $this->dbUrl = $this->database->internal_db_url; + $this->dbUrlPublic = $this->database->external_db_url; + } else { + $this->name = $this->database->name; + $this->description = $this->database->description; + $this->redisConf = $this->database->redis_conf; + $this->image = $this->database->image; + $this->portsMappings = $this->database->ports_mappings; + $this->isPublic = $this->database->is_public; + $this->publicPort = $this->database->public_port; + $this->isLogDrainEnabled = $this->database->is_log_drain_enabled; + $this->customDockerRunOptions = $this->database->custom_docker_run_options; + $this->enableSsl = $this->database->enable_ssl; + $this->dbUrl = $this->database->internal_db_url; + $this->dbUrlPublic = $this->database->external_db_url; + $this->redisVersion = $this->database->getRedisVersion(); + $this->redisUsername = $this->database->redis_username; + $this->redisPassword = $this->database->redis_password; } } @@ -110,12 +171,12 @@ public function instantSaveAdvanced() $this->authorize('update', $this->database); if (! $this->server->isLogDrainEnabled()) { - $this->database->is_log_drain_enabled = false; + $this->isLogDrainEnabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); return; } - $this->database->save(); + $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) { @@ -128,20 +189,19 @@ public function submit() try { $this->authorize('manageEnvironment', $this->database); - $this->validate(); + $this->syncData(true); - if (version_compare($this->redis_version, '6.0', '>=')) { + if (version_compare($this->redisVersion, '6.0', '>=')) { $this->database->runtime_environment_variables()->updateOrCreate( ['key' => 'REDIS_USERNAME'], - ['value' => $this->redis_username, 'resourceable_id' => $this->database->id] + ['value' => $this->redisUsername, 'resourceable_id' => $this->database->id] ); } $this->database->runtime_environment_variables()->updateOrCreate( ['key' => 'REDIS_PASSWORD'], - ['value' => $this->redis_password, 'resourceable_id' => $this->database->id] + ['value' => $this->redisPassword, 'resourceable_id' => $this->database->id] ); - $this->database->save(); $this->dispatch('success', 'Database updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -155,16 +215,16 @@ public function instantSave() try { $this->authorize('update', $this->database); - if ($this->database->is_public && ! $this->database->public_port) { + if ($this->isPublic && ! $this->publicPort) { $this->dispatch('error', 'Public port is required.'); - $this->database->is_public = false; + $this->isPublic = false; return; } - if ($this->database->is_public) { + if ($this->isPublic) { if (! str($this->database->status)->startsWith('running')) { $this->dispatch('error', 'Database must be started to be publicly accessible.'); - $this->database->is_public = false; + $this->isPublic = false; return; } @@ -174,10 +234,11 @@ public function instantSave() StopDatabaseProxy::run($this->database); $this->dispatch('success', 'Database is no longer publicly accessible.'); } - $this->db_url_public = $this->database->external_db_url; - $this->database->save(); + $this->dbUrlPublic = $this->database->external_db_url; + $this->syncData(true); } catch (\Throwable $e) { - $this->database->is_public = ! $this->database->is_public; + $this->isPublic = ! $this->isPublic; + $this->syncData(true); return handleError($e, $this); } @@ -188,7 +249,7 @@ public function instantSaveSSL() try { $this->authorize('update', $this->database); - $this->database->save(); + $this->syncData(true); $this->dispatch('success', 'SSL configuration updated.'); } catch (Exception $e) { return handleError($e, $this); @@ -232,16 +293,7 @@ public function regenerateSslCertificate() public function refresh(): void { $this->database->refresh(); - $this->refreshView(); - } - - private function refreshView() - { - $this->db_url = $this->database->internal_db_url; - $this->db_url_public = $this->database->external_db_url; - $this->redis_version = $this->database->getRedisVersion(); - $this->redis_username = $this->database->redis_username; - $this->redis_password = $this->database->redis_password; + $this->syncData(); } public function render() diff --git a/resources/views/livewire/project/database/mariadb/general.blade.php b/resources/views/livewire/project/database/mariadb/general.blade.php index fdc659408..b428c3144 100644 --- a/resources/views/livewire/project/database/mariadb/general.blade.php +++ b/resources/views/livewire/project/database/mariadb/general.blade.php @@ -7,9 +7,9 @@
- - - + +
If you change the values in the database, please sync it here, otherwise @@ -17,32 +17,32 @@
@if ($database->started_at)
- - -
-
@else
- - -
-
@@ -51,13 +51,13 @@

Network

-
@@ -75,7 +75,7 @@

SSL Configuration

- @if ($database->enable_ssl && $certificateValidUntil) + @if ($enableSsl && $certificateValidUntil) Valid until: @if (now()->gt($certificateValidUntil)) {{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired @@ -102,12 +102,12 @@
@if (str($database->status)->contains('exited')) - @else - @endif @@ -134,18 +134,18 @@ @endif
-
- +
-

Advanced

diff --git a/resources/views/livewire/project/database/mongodb/general.blade.php b/resources/views/livewire/project/database/mongodb/general.blade.php index 2892e721e..871ac55c4 100644 --- a/resources/views/livewire/project/database/mongodb/general.blade.php +++ b/resources/views/livewire/project/database/mongodb/general.blade.php @@ -7,9 +7,9 @@
- - - + +
If you change the values in the database, please sync it here, otherwise @@ -17,36 +17,36 @@
@if ($database->started_at)
- - -
@else
- - -
@endif + id="customDockerRunOptions" label="Custom Docker Options" canGate="update" :canResource="$database" />

Network

-
@@ -64,7 +64,7 @@

SSL Configuration

- @if ($database->enable_ssl) + @if ($enableSsl) Valid until: @if (now()->gt($certificateValidUntil)) {{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired @@ -91,20 +91,20 @@
@if (str($database->status)->contains('exited')) - @else - @endif
- @if ($database->enable_ssl) + @if ($enableSsl)
@if (str($database->status)->contains('exited')) - @@ -115,7 +115,7 @@ @else - @@ -148,18 +148,18 @@ @endif
-
- +
-

Advanced

diff --git a/resources/views/livewire/project/database/mysql/general.blade.php b/resources/views/livewire/project/database/mysql/general.blade.php index c04119f9f..512a3eb1b 100644 --- a/resources/views/livewire/project/database/mysql/general.blade.php +++ b/resources/views/livewire/project/database/mysql/general.blade.php @@ -7,9 +7,9 @@
- - - + +
If you change the values in the database, please sync it here, otherwise @@ -17,29 +17,29 @@
@if ($database->started_at)
- - -
-
@else
- - -
-
@@ -48,12 +48,12 @@ + id="customDockerRunOptions" label="Custom Docker Options" canGate="update" :canResource="$database" />

Network

-

SSL Configuration

- @if ($database->enable_ssl && $certificateValidUntil) + @if ($enableSsl && $certificateValidUntil) Valid until: @if (now()->gt($certificateValidUntil)) {{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired @@ -97,18 +97,18 @@
@if (str($database->status)->contains('exited')) - + @else - @endif
- @if ($database->enable_ssl) + @if ($enableSsl)
@if (str($database->status)->contains('exited')) - @@ -118,7 +118,7 @@ @else - @@ -151,16 +151,16 @@ @endif
- +
- +
- +

Advanced

+ instantSave="instantSaveAdvanced" id="isLogDrainEnabled" label="Drain Logs" canGate="update" :canResource="$database" />
diff --git a/resources/views/livewire/project/database/postgresql/general.blade.php b/resources/views/livewire/project/database/postgresql/general.blade.php index d93170edf..290d18fca 100644 --- a/resources/views/livewire/project/database/postgresql/general.blade.php +++ b/resources/views/livewire/project/database/postgresql/general.blade.php @@ -21,9 +21,9 @@
- - - + +
If you change the values in the database, please sync it here, otherwise @@ -31,40 +31,40 @@
@if ($database->started_at)
- - -
@else
- - -
@endif
+ id="postgresInitdbArgs" placeholder="If empty, use default. See in docker docs." /> + id="postgresHostAuthMethod" placeholder="If empty, use default. See in docker docs." />
+ id="customDockerRunOptions" label="Custom Docker Options" canGate="update" :canResource="$database" />

Network

-
@@ -81,7 +81,7 @@

SSL Configuration

- @if ($database->enable_ssl && $certificateValidUntil) + @if ($enableSsl && $certificateValidUntil) @endif
- @if ($database->enable_ssl && $certificateValidUntil) + @if ($enableSsl && $certificateValidUntil) Valid until: @if (now()->gt($certificateValidUntil)) {{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired @@ -107,20 +107,20 @@
@if ($database->isExited()) - @else - @endif
- @if ($database->enable_ssl) + @if ($enableSsl)
@if ($database->isExited()) - @@ -131,7 +131,7 @@ @else - @@ -161,16 +161,16 @@ @endif
-
- +
+ id="postgresConf" canGate="update" :canResource="$database" />
@@ -178,7 +178,7 @@

Advanced

@@ -201,7 +201,7 @@ @endcan
- @forelse(data_get($database,'init_scripts', []) as $script) + @forelse($initScripts ?? [] as $script) @empty
No initialization scripts found.
diff --git a/resources/views/livewire/project/database/redis/general.blade.php b/resources/views/livewire/project/database/redis/general.blade.php index 66e913be0..7ffc8f218 100644 --- a/resources/views/livewire/project/database/redis/general.blade.php +++ b/resources/views/livewire/project/database/redis/general.blade.php @@ -7,9 +7,9 @@
- - - + +
@@ -19,19 +19,19 @@ automations won't work.
Changing them here will not change the values in the database.
- @if (version_compare($redis_version, '6.0', '>=')) - =')) + @endif -
@else
You can only change the username and password in the database after initial start.
- @if (version_compare($redis_version, '6.0', '>=')) - =')) + @endif - Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.

Check the docs." placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k" - id="database.custom_docker_run_options" label="Custom Docker Options" canGate="update" :canResource="$database" /> + id="customDockerRunOptions" label="Custom Docker Options" canGate="update" :canResource="$database" />

Network

-
- @if ($db_url_public) + type="password" readonly wire:model="dbUrl" canGate="update" :canResource="$database" /> + @if ($dbUrlPublic) + type="password" readonly wire:model="dbUrlPublic" canGate="update" :canResource="$database" /> @endif

SSL Configuration

- @if ($database->enable_ssl && $certificateValidUntil) + @if ($enableSsl && $certificateValidUntil) Valid until: @if (now()->gt($certificateValidUntil)) {{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired @@ -98,12 +98,12 @@
@if (str($database->status)->contains('exited')) - @else - @endif @@ -117,23 +117,23 @@

Proxy

- @if (data_get($database, 'is_public')) + @if ($isPublic) Proxy Logs - Logs @endif
-
- +

⚠️ Important: Coolify automatically applies the requirepass directive using the password shown in the Password field above. If you override requirepass in your custom configuration, make sure it matches the password field to avoid authentication issues.

🔗 Tip: View the full Redis default configuration to see what options are available." - label="Custom Redis Configuration" rows="10" id="database.redis_conf" canGate="update" + label="Custom Redis Configuration" rows="10" id="redisConf" canGate="update" :canResource="$database" /> @@ -149,7 +149,7 @@

Advanced