Merge pull request #5416 from coollabsio/next

v4.0.0-beta.400
This commit is contained in:
Andras Bacsai 2025-03-27 10:37:36 +01:00 committed by GitHub
commit 6f648e4672
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 574 additions and 325 deletions

View file

@ -183,7 +183,7 @@ public function handle(StandaloneMariadb $database)
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
if ($this->database->enable_ssl) { if ($this->database->enable_ssl) {
$docker_compose['services'][$container_name]['command'] = [ $docker_compose['services'][$container_name]['command'] = [
'mysqld', 'mariadbd',
'--ssl-cert=/etc/mysql/certs/server.crt', '--ssl-cert=/etc/mysql/certs/server.crt',
'--ssl-key=/etc/mysql/certs/server.key', '--ssl-key=/etc/mysql/certs/server.key',
'--ssl-ca=/etc/mysql/certs/coolify-ca.crt', '--ssl-ca=/etc/mysql/certs/coolify-ca.crt',

View file

@ -30,10 +30,6 @@ public function handle(Server $server, $fromUI = false): bool
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) { if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
return false; return false;
} }
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
if (! $uptime) {
throw new \Exception($error);
}
if (! $server->isProxyShouldRun()) { if (! $server->isProxyShouldRun()) {
if ($fromUI) { if ($fromUI) {
throw new \Exception('Proxy should not run. You selected the Custom Proxy.'); throw new \Exception('Proxy should not run. You selected the Custom Proxy.');
@ -68,6 +64,38 @@ public function handle(Server $server, $fromUI = false): bool
$portsToCheck = ['80', '443']; $portsToCheck = ['80', '443'];
foreach ($portsToCheck as $port) {
// Try multiple methods to check port availability
$commands = [
// Method 1: Check /proc/net/tcp directly (convert port to hex)
"cat /proc/net/tcp | grep -q '00000000:".str_pad(dechex($port), 4, '0', STR_PAD_LEFT)."'",
// Method 2: Use ss command (modern alternative to netstat)
"ss -tuln | grep -q ':$port '",
// Method 3: Use lsof if available
"lsof -i :$port >/dev/null 2>&1",
// Method 4: Use fuser if available
"fuser $port/tcp >/dev/null 2>&1",
];
$portInUse = false;
foreach ($commands as $command) {
try {
instant_remote_process([$command], $server);
$portInUse = true;
break;
} catch (\Throwable $e) {
continue;
}
}
if ($portInUse) {
if ($fromUI) {
throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br><br>Docs: <a target='_blank' class='dark:text-white hover:underline' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' class='dark:text-white hover:underline' href='https://coolify.io/discord'>https://coolify.io/discord</a>");
} else {
return false;
}
}
}
try { try {
if ($server->proxyType() !== ProxyTypes::NONE->value) { if ($server->proxyType() !== ProxyTypes::NONE->value) {
$proxyCompose = CheckConfiguration::run($server); $proxyCompose = CheckConfiguration::run($server);
@ -94,16 +122,6 @@ public function handle(Server $server, $fromUI = false): bool
if (count($portsToCheck) === 0) { if (count($portsToCheck) === 0) {
return false; return false;
} }
foreach ($portsToCheck as $port) {
$connection = @fsockopen($ip, $port);
if (is_resource($connection) && fclose($connection)) {
if ($fromUI) {
throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
} else {
return false;
}
}
}
return true; return true;
} }

View file

@ -60,10 +60,12 @@ class General extends Component
public function getListeners() public function getListeners()
{ {
$userId = Auth::id();
$teamId = Auth::user()->currentTeam()->id; $teamId = Auth::user()->currentTeam()->id;
return [ return [
"echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped', "echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped',
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
]; ];
} }
@ -226,6 +228,7 @@ public function regenerateSslCertificate()
caKey: $caCert->ssl_private_key, caKey: $caCert->ssl_private_key,
configurationDir: $existingCert->configuration_dir, configurationDir: $existingCert->configuration_dir,
mountPath: $existingCert->mount_path, mountPath: $existingCert->mount_path,
isPemKeyFileRequired: true,
); );
$this->dispatch('success', 'SSL certificates regenerated. Restart database to apply changes.'); $this->dispatch('success', 'SSL certificates regenerated. Restart database to apply changes.');

View file

@ -31,8 +31,8 @@ public function activityFinished()
$this->database->update([ $this->database->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$this->dispatch('refresh');
$this->check_status(); $this->check_status();
if (is_null($this->database->config_hash) || $this->database->isConfigurationChanged()) { if (is_null($this->database->config_hash) || $this->database->isConfigurationChanged()) {
$this->database->isConfigurationChanged(true); $this->database->isConfigurationChanged(true);
$this->dispatch('configurationChanged'); $this->dispatch('configurationChanged');
@ -44,7 +44,7 @@ public function activityFinished()
public function check_status($showNotification = false) public function check_status($showNotification = false)
{ {
if ($this->database->destination->server->isFunctional()) { if ($this->database->destination->server->isFunctional()) {
GetContainersStatus::dispatch($this->database->destination->server); GetContainersStatus::run($this->database->destination->server);
} }
if ($showNotification) { if ($showNotification) {
@ -63,6 +63,7 @@ public function stop()
$this->database->status = 'exited'; $this->database->status = 'exited';
$this->database->save(); $this->database->save();
$this->check_status(); $this->check_status();
$this->dispatch('refresh');
} }
public function restart() public function restart()

View file

@ -63,10 +63,13 @@ class General extends Component
public function getListeners() public function getListeners()
{ {
$userId = Auth::id();
$teamId = Auth::user()->currentTeam()->id; $teamId = Auth::user()->currentTeam()->id;
return [ return [
"echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped', "echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped',
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
'refresh' => '$refresh',
]; ];
} }
@ -231,6 +234,7 @@ public function regenerateSslCertificate()
caKey: $caCert->ssl_private_key, caKey: $caCert->ssl_private_key,
configurationDir: $existingCert->configuration_dir, configurationDir: $existingCert->configuration_dir,
mountPath: $existingCert->mount_path, mountPath: $existingCert->mount_path,
isPemKeyFileRequired: true,
); );
$this->dispatch('success', 'SSL certificates regenerated. Restart database to apply changes.'); $this->dispatch('success', 'SSL certificates regenerated. Restart database to apply changes.');

View file

@ -10,6 +10,7 @@
use App\Models\StandaloneMariadb; use App\Models\StandaloneMariadb;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Support\Facades\Auth;
use Livewire\Component; use Livewire\Component;
class General extends Component class General extends Component
@ -26,6 +27,16 @@ class General extends Component
public ?Carbon $certificateValidUntil = null; public ?Carbon $certificateValidUntil = null;
public function getListeners()
{
$userId = Auth::id();
return [
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
'refresh' => '$refresh',
];
}
protected $rules = [ protected $rules = [
'database.name' => 'required', 'database.name' => 'required',
'database.description' => 'nullable', 'database.description' => 'nullable',
@ -173,6 +184,7 @@ public function regenerateSslCertificate()
caKey: $caCert->ssl_private_key, caKey: $caCert->ssl_private_key,
configurationDir: $existingCert->configuration_dir, configurationDir: $existingCert->configuration_dir,
mountPath: $existingCert->mount_path, mountPath: $existingCert->mount_path,
isPemKeyFileRequired: true,
); );
$this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.'); $this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.');

View file

@ -10,6 +10,7 @@
use App\Models\StandaloneMongodb; use App\Models\StandaloneMongodb;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Support\Facades\Auth;
use Livewire\Component; use Livewire\Component;
class General extends Component class General extends Component
@ -26,6 +27,16 @@ class General extends Component
public ?Carbon $certificateValidUntil = null; public ?Carbon $certificateValidUntil = null;
public function getListeners()
{
$userId = Auth::id();
return [
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
'refresh' => '$refresh',
];
}
protected $rules = [ protected $rules = [
'database.name' => 'required', 'database.name' => 'required',
'database.description' => 'nullable', 'database.description' => 'nullable',
@ -181,6 +192,7 @@ public function regenerateSslCertificate()
caKey: $caCert->ssl_private_key, caKey: $caCert->ssl_private_key,
configurationDir: $existingCert->configuration_dir, configurationDir: $existingCert->configuration_dir,
mountPath: $existingCert->mount_path, mountPath: $existingCert->mount_path,
isPemKeyFileRequired: true,
); );
$this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.'); $this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.');

View file

@ -10,6 +10,7 @@
use App\Models\StandaloneMysql; use App\Models\StandaloneMysql;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Support\Facades\Auth;
use Livewire\Component; use Livewire\Component;
class General extends Component class General extends Component
@ -26,6 +27,16 @@ class General extends Component
public ?Carbon $certificateValidUntil = null; public ?Carbon $certificateValidUntil = null;
public function getListeners()
{
$userId = Auth::id();
return [
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
'refresh' => '$refresh',
];
}
protected $rules = [ protected $rules = [
'database.name' => 'required', 'database.name' => 'required',
'database.description' => 'nullable', 'database.description' => 'nullable',
@ -180,6 +191,7 @@ public function regenerateSslCertificate()
caKey: $caCert->ssl_private_key, caKey: $caCert->ssl_private_key,
configurationDir: $existingCert->configuration_dir, configurationDir: $existingCert->configuration_dir,
mountPath: $existingCert->mount_path, mountPath: $existingCert->mount_path,
isPemKeyFileRequired: true,
); );
$this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.'); $this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.');

View file

@ -10,6 +10,7 @@
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Support\Facades\Auth;
use Livewire\Component; use Livewire\Component;
class General extends Component class General extends Component
@ -30,8 +31,11 @@ class General extends Component
public function getListeners() public function getListeners()
{ {
$userId = Auth::id();
return [ return [
'refresh', "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
'refresh' => '$refresh',
'save_init_script', 'save_init_script',
'delete_init_script', 'delete_init_script',
]; ];
@ -144,6 +148,7 @@ public function regenerateSslCertificate()
caKey: $caCert->ssl_private_key, caKey: $caCert->ssl_private_key,
configurationDir: $existingCert->configuration_dir, configurationDir: $existingCert->configuration_dir,
mountPath: $existingCert->mount_path, mountPath: $existingCert->mount_path,
isPemKeyFileRequired: true,
); );
$this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.'); $this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.');
@ -204,7 +209,7 @@ public function save_init_script($script)
$delete_command = "rm -f $old_file_path"; $delete_command = "rm -f $old_file_path";
try { try {
instant_remote_process([$delete_command], $this->server); instant_remote_process([$delete_command], $this->server);
} catch (\Exception $e) { } catch (Exception $e) {
$this->dispatch('error', 'Failed to remove old init script from server: '.$e->getMessage()); $this->dispatch('error', 'Failed to remove old init script from server: '.$e->getMessage());
return; return;
@ -245,7 +250,7 @@ public function delete_init_script($script)
$command = "rm -f $file_path"; $command = "rm -f $file_path";
try { try {
instant_remote_process([$command], $this->server); instant_remote_process([$command], $this->server);
} catch (\Exception $e) { } catch (Exception $e) {
$this->dispatch('error', 'Failed to remove init script from server: '.$e->getMessage()); $this->dispatch('error', 'Failed to remove init script from server: '.$e->getMessage());
return; return;
@ -262,16 +267,11 @@ public function delete_init_script($script)
$this->database->init_scripts = $updatedScripts; $this->database->init_scripts = $updatedScripts;
$this->database->save(); $this->database->save();
$this->refresh(); $this->dispatch('refresh')->self();
$this->dispatch('success', 'Init script deleted from the database and the server.'); $this->dispatch('success', 'Init script deleted from the database and the server.');
} }
} }
public function refresh(): void
{
$this->database->refresh();
}
public function save_new_init_script() public function save_new_init_script()
{ {
$this->validate([ $this->validate([

View file

@ -10,15 +10,11 @@
use App\Models\StandaloneRedis; use App\Models\StandaloneRedis;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Support\Facades\Auth;
use Livewire\Component; use Livewire\Component;
class General extends Component class General extends Component
{ {
protected $listeners = [
'envsUpdated' => 'refresh',
'refresh',
];
public Server $server; public Server $server;
public StandaloneRedis $database; public StandaloneRedis $database;
@ -35,6 +31,17 @@ class General extends Component
public ?Carbon $certificateValidUntil = null; public ?Carbon $certificateValidUntil = null;
public function getListeners()
{
$userId = Auth::id();
return [
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
'envsUpdated' => 'refresh',
'refresh',
];
}
protected $rules = [ protected $rules = [
'database.name' => 'required', 'database.name' => 'required',
'database.description' => 'nullable', 'database.description' => 'nullable',
@ -181,6 +188,7 @@ public function regenerateSslCertificate()
caKey: $caCert->ssl_private_key, caKey: $caCert->ssl_private_key,
configurationDir: $existingCert->configuration_dir, configurationDir: $existingCert->configuration_dir,
mountPath: $existingCert->mount_path, mountPath: $existingCert->mount_path,
isPemKeyFileRequired: true,
); );
$this->dispatch('success', 'SSL certificates regenerated. Restart database to apply changes.'); $this->dispatch('success', 'SSL certificates regenerated. Restart database to apply changes.');

View file

@ -70,10 +70,6 @@ public function team()
public function isEnabled() public function isEnabled()
{ {
if (isCloud()) {
return true;
}
return $this->smtp_enabled || $this->resend_enabled || $this->use_instance_email_settings; return $this->smtp_enabled || $this->resend_enabled || $this->use_instance_email_settings;
} }
} }

View file

@ -248,9 +248,9 @@ protected function internalDbUrl(): Attribute
$encodedPass = rawurlencode($this->mongo_initdb_root_password); $encodedPass = rawurlencode($this->mongo_initdb_root_password);
$url = "mongodb://{$encodedUser}:{$encodedPass}@{$this->uuid}:27017/?directConnection=true"; $url = "mongodb://{$encodedUser}:{$encodedPass}@{$this->uuid}:27017/?directConnection=true";
if ($this->enable_ssl) { if ($this->enable_ssl) {
$url .= '&tls=true'; $url .= '&tls=true&tlsCAFile=/etc/mongo/certs/ca.pem';
if (in_array($this->ssl_mode, ['verify-full'])) { if (in_array($this->ssl_mode, ['verify-full'])) {
$url .= '&tlsCAFile=/etc/ssl/certs/coolify-ca.crt'; $url .= '&tlsCertificateKeyFile=/etc/mongo/certs/server.pem';
} }
} }
@ -268,9 +268,9 @@ protected function externalDbUrl(): Attribute
$encodedPass = rawurlencode($this->mongo_initdb_root_password); $encodedPass = rawurlencode($this->mongo_initdb_root_password);
$url = "mongodb://{$encodedUser}:{$encodedPass}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true"; $url = "mongodb://{$encodedUser}:{$encodedPass}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
if ($this->enable_ssl) { if ($this->enable_ssl) {
$url .= '&tls=true'; $url .= '&tls=true&tlsCAFile=/etc/mongo/certs/ca.pem';
if (in_array($this->ssl_mode, ['verify-full'])) { if (in_array($this->ssl_mode, ['verify-full'])) {
$url .= '&tlsCAFile=/etc/ssl/certs/coolify-ca.crt'; $url .= '&tlsCertificateKeyFile=/etc/mongo/certs/server.pem';
} }
} }

View file

@ -83,7 +83,7 @@ public function toTelegram(): array
'buttons' => [ 'buttons' => [
[ [
'text' => 'Go to your dashboard', 'text' => 'Go to your dashboard',
'url' => base_url(), 'url' => isDev() ? 'https://staging-but-dev.coolify.io' : base_url(),
], ],
], ],
]; ];

View file

@ -18,6 +18,7 @@ public function __construct(
public ?string $label = null, public ?string $label = null,
public ?string $helper = null, public ?string $helper = null,
public bool $required = false, public bool $required = false,
public bool $disabled = false,
public string $defaultClass = 'select w-full' public string $defaultClass = 'select w-full'
) { ) {
// //

View file

@ -2,7 +2,7 @@
return [ return [
'coolify' => [ 'coolify' => [
'version' => '4.0.0-beta.399', 'version' => '4.0.0-beta.400',
'helper_version' => '1.0.7', 'helper_version' => '1.0.7',
'realtime_version' => '1.0.6', 'realtime_version' => '1.0.6',
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),

View file

@ -19,21 +19,53 @@ public function up(): void
}); });
if (DB::table('local_file_volumes')->exists()) { if (DB::table('local_file_volumes')->exists()) {
DB::beginTransaction();
DB::table('local_file_volumes') DB::table('local_file_volumes')
->orderBy('id') ->orderBy('id')
->chunk(100, function ($volumes) { ->chunk(100, function ($volumes) {
foreach ($volumes as $volume) { foreach ($volumes as $volume) {
try { try {
$fs_path = $volume->fs_path;
$mount_path = $volume->mount_path;
$content = $volume->content;
// Check if fields are already encrypted by attempting to decrypt
try {
if ($fs_path) {
Crypt::decryptString($fs_path);
}
} catch (\Exception $e) {
$fs_path = $fs_path ? Crypt::encryptString($fs_path) : null;
}
try {
if ($mount_path) {
Crypt::decryptString($mount_path);
}
} catch (\Exception $e) {
$mount_path = $mount_path ? Crypt::encryptString($mount_path) : null;
}
try {
if ($content) {
Crypt::decryptString($content);
}
} catch (\Exception $e) {
$content = $content ? Crypt::encryptString($content) : null;
}
DB::table('local_file_volumes')->where('id', $volume->id)->update([ DB::table('local_file_volumes')->where('id', $volume->id)->update([
'fs_path' => $volume->fs_path ? Crypt::encryptString($volume->fs_path) : null, 'fs_path' => $fs_path,
'mount_path' => $volume->mount_path ? Crypt::encryptString($volume->mount_path) : null, 'mount_path' => $mount_path,
'content' => $volume->content ? Crypt::encryptString($volume->content) : null, 'content' => $content,
]); ]);
echo "Updated volume {$volume->id}\n";
} catch (\Exception $e) { } catch (\Exception $e) {
echo "Error encrypting local file volume fields: {$e->getMessage()}\n";
Log::error('Error encrypting local file volume fields: '.$e->getMessage()); Log::error('Error encrypting local file volume fields: '.$e->getMessage());
} }
} }
}); });
DB::commit();
} }
} }
@ -49,21 +81,53 @@ public function down(): void
}); });
if (DB::table('local_file_volumes')->exists()) { if (DB::table('local_file_volumes')->exists()) {
DB::beginTransaction();
DB::table('local_file_volumes') DB::table('local_file_volumes')
->orderBy('id') ->orderBy('id')
->chunk(100, function ($volumes) { ->chunk(100, function ($volumes) {
foreach ($volumes as $volume) { foreach ($volumes as $volume) {
try { try {
$fs_path = $volume->fs_path;
$mount_path = $volume->mount_path;
$content = $volume->content;
// Check if fields are already decrypted by attempting to decrypt
try {
if ($fs_path) {
Crypt::decryptString($fs_path);
}
} catch (\Exception $e) {
$fs_path = $fs_path ? Crypt::decryptString($fs_path) : null;
}
try {
if ($mount_path) {
Crypt::decryptString($mount_path);
}
} catch (\Exception $e) {
$mount_path = $mount_path ? Crypt::decryptString($mount_path) : null;
}
try {
if ($content) {
Crypt::decryptString($content);
}
} catch (\Exception $e) {
$content = $content ? Crypt::decryptString($content) : null;
}
DB::table('local_file_volumes')->where('id', $volume->id)->update([ DB::table('local_file_volumes')->where('id', $volume->id)->update([
'fs_path' => $volume->fs_path ? Crypt::decryptString($volume->fs_path) : null, 'fs_path' => $fs_path,
'mount_path' => $volume->mount_path ? Crypt::decryptString($volume->mount_path) : null, 'mount_path' => $mount_path,
'content' => $volume->content ? Crypt::decryptString($volume->content) : null, 'content' => $content,
]); ]);
echo "Updated volume {$volume->id}\n";
} catch (\Exception $e) { } catch (\Exception $e) {
echo "Error decrypting local file volume fields: {$e->getMessage()}\n";
Log::error('Error decrypting local file volume fields: '.$e->getMessage()); Log::error('Error decrypting local file volume fields: '.$e->getMessage());
} }
} }
}); });
DB::commit();
} }
} }
}; };

View file

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('standalone_mongodbs', function (Blueprint $table) {
$table->boolean('enable_ssl')->default(false)->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('standalone_mongodbs', function (Blueprint $table) {
$table->boolean('enable_ssl')->default(true)->change();
});
}
};

View file

@ -30,9 +30,11 @@ fi
# Make sure coolify network exists # Make sure coolify network exists
# It is created when starting Coolify with docker compose # It is created when starting Coolify with docker compose
if ! docker network create --attachable --ipv6 coolify 2>/dev/null; then if ! docker network inspect coolify >/dev/null 2>&1; then
echo "Failed to create coolify network with ipv6. Trying without ipv6..." if ! docker network create --attachable --ipv6 coolify 2>/dev/null; then
docker network create --attachable coolify 2>/dev/null echo "Failed to create coolify network with ipv6. Trying without ipv6..."
docker network create --attachable coolify 2>/dev/null
fi
fi fi
# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null # docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null

View file

@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.397" "version": "4.0.0-beta.400"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.398" "version": "4.0.0-beta.401"
}, },
"helper": { "helper": {
"version": "1.0.7" "version": "1.0.7"

226
package-lock.json generated
View file

@ -15,14 +15,14 @@
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "5.2.1", "@vitejs/plugin-vue": "5.2.1",
"autoprefixer": "10.4.20", "autoprefixer": "10.4.20",
"axios": "1.7.9", "axios": "1.8.2",
"laravel-echo": "2.0.2", "laravel-echo": "2.0.2",
"laravel-vite-plugin": "^1.2.0", "laravel-vite-plugin": "^1.2.0",
"postcss": "8.5.3", "postcss": "8.5.3",
"pusher-js": "8.4.0", "pusher-js": "8.4.0",
"tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar": "^3.1.0",
"tailwindcss": "3.4.17", "tailwindcss": "3.4.17",
"vite": "^6.1.1", "vite": "^6.2.3",
"vue": "3.5.13" "vue": "3.5.13"
} }
}, },
@ -88,9 +88,9 @@
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -105,9 +105,9 @@
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz",
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -122,9 +122,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz",
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -139,9 +139,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz",
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -156,9 +156,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz",
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -173,9 +173,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz",
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -190,9 +190,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz",
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -207,9 +207,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz",
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -224,9 +224,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz",
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -241,9 +241,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz",
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -258,9 +258,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz",
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -275,9 +275,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz",
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@ -292,9 +292,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz",
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@ -309,9 +309,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz",
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -326,9 +326,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz",
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -343,9 +343,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz",
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -360,9 +360,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz",
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -377,9 +377,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-arm64": { "node_modules/@esbuild/netbsd-arm64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz",
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -394,9 +394,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz",
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -411,9 +411,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-arm64": { "node_modules/@esbuild/openbsd-arm64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz",
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -428,9 +428,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz",
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -445,9 +445,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz",
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -462,9 +462,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz",
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -479,9 +479,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz",
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -496,9 +496,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz",
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1220,9 +1220,9 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.7.9", "version": "1.8.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -1520,9 +1520,9 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.24.2", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@ -1533,31 +1533,31 @@
"node": ">=18" "node": ">=18"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/aix-ppc64": "0.24.2", "@esbuild/aix-ppc64": "0.25.1",
"@esbuild/android-arm": "0.24.2", "@esbuild/android-arm": "0.25.1",
"@esbuild/android-arm64": "0.24.2", "@esbuild/android-arm64": "0.25.1",
"@esbuild/android-x64": "0.24.2", "@esbuild/android-x64": "0.25.1",
"@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-arm64": "0.25.1",
"@esbuild/darwin-x64": "0.24.2", "@esbuild/darwin-x64": "0.25.1",
"@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-arm64": "0.25.1",
"@esbuild/freebsd-x64": "0.24.2", "@esbuild/freebsd-x64": "0.25.1",
"@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm": "0.25.1",
"@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-arm64": "0.25.1",
"@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-ia32": "0.25.1",
"@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-loong64": "0.25.1",
"@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-mips64el": "0.25.1",
"@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-ppc64": "0.25.1",
"@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-riscv64": "0.25.1",
"@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-s390x": "0.25.1",
"@esbuild/linux-x64": "0.24.2", "@esbuild/linux-x64": "0.25.1",
"@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-arm64": "0.25.1",
"@esbuild/netbsd-x64": "0.24.2", "@esbuild/netbsd-x64": "0.25.1",
"@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-arm64": "0.25.1",
"@esbuild/openbsd-x64": "0.24.2", "@esbuild/openbsd-x64": "0.25.1",
"@esbuild/sunos-x64": "0.24.2", "@esbuild/sunos-x64": "0.25.1",
"@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-arm64": "0.25.1",
"@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-ia32": "0.25.1",
"@esbuild/win32-x64": "0.24.2" "@esbuild/win32-x64": "0.25.1"
} }
}, },
"node_modules/escalade": { "node_modules/escalade": {
@ -2802,14 +2802,14 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.1.1", "version": "6.2.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz",
"integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==", "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.24.2", "esbuild": "^0.25.0",
"postcss": "^8.5.2", "postcss": "^8.5.3",
"rollup": "^4.30.1" "rollup": "^4.30.1"
}, },
"bin": { "bin": {

View file

@ -9,14 +9,14 @@
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "5.2.1", "@vitejs/plugin-vue": "5.2.1",
"autoprefixer": "10.4.20", "autoprefixer": "10.4.20",
"axios": "1.7.9", "axios": "1.8.2",
"laravel-echo": "2.0.2", "laravel-echo": "2.0.2",
"laravel-vite-plugin": "^1.2.0", "laravel-vite-plugin": "^1.2.0",
"postcss": "8.5.3", "postcss": "8.5.3",
"pusher-js": "8.4.0", "pusher-js": "8.4.0",
"tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar": "^3.1.0",
"tailwindcss": "3.4.17", "tailwindcss": "3.4.17",
"vite": "^6.1.1", "vite": "^6.2.3",
"vue": "3.5.13" "vue": "3.5.13"
}, },
"dependencies": { "dependencies": {

View file

@ -46,7 +46,7 @@ .select {
.input, .input,
.select { .select {
@apply text-black dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300 block w-full py-1.5 rounded border-0 text-sm ring-1 ring-inset; @apply text-black dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300 block w-full py-1.5 rounded border-0 text-sm ring-1 ring-inset disabled:dark:bg-coolgray-100/40 disabled:dark:ring-transparent disabled:bg-neutral-200 disabled:text-neutral-500;
} }
.select { .select {
@ -97,6 +97,7 @@ label {
@apply dark:text-neutral-400; @apply dark:text-neutral-400;
} }
table { table {
@apply min-w-full divide-y dark:divide-coolgray-200 divide-neutral-300; @apply min-w-full divide-y dark:divide-coolgray-200 divide-neutral-300;
} }

View file

@ -1,6 +1,7 @@
<div class="w-full"> <div class="w-full">
@if ($label) @if ($label)
<label class="flex gap-1 items-center mb-1 text-sm font-medium">{{ $label }} <label
class="flex gap-1 items-center mb-1 text-sm font-medium {{ $disabled ? 'text-neutral-600' : '' }}">{{ $label }}
@if ($required) @if ($required)
<x-highlighted text="*" /> <x-highlighted text="*" />
@endif @endif
@ -9,7 +10,7 @@
@endif @endif
</label> </label>
@endif @endif
<select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required) <select {{ $attributes->merge(['class' => $defaultClass]) }} @disabled($disabled) @required($required)
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300' wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled" name={{ $id }} wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled" name={{ $id }}
@if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model={{ $id }} @endif> @if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model={{ $id }} @endif>

View file

@ -76,8 +76,16 @@
</span> </span>
@endif @endif
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.checkbox id="enable_ssl" label="Enable SSL" wire:model.live="enable_ssl" <div class="w-64">
instantSave="instantSaveSSL" /> @if (str($database->status)->contains('exited'))
<x-forms.checkbox id="enable_ssl" label="Enable SSL" wire:model.live="enable_ssl"
instantSave="instantSaveSSL" />
@else
<x-forms.checkbox id="enable_ssl" label="Enable SSL" wire:model.live="enable_ssl"
instantSave="instantSaveSSL" disabled
helper="Database should be stopped to change this settings." />
@endif
</div>
</div> </div>
</div> </div>
<div> <div>

View file

@ -53,34 +53,39 @@
<div class="flex items-center justify-between py-2"> <div class="flex items-center justify-between py-2">
<div class="flex items-center justify-between w-full"> <div class="flex items-center justify-between w-full">
<h3>SSL Configuration</h3> <h3>SSL Configuration</h3>
@if($database->enable_ssl && $certificateValidUntil) @if ($database->enable_ssl && $certificateValidUntil)
<x-modal-confirmation <x-modal-confirmation title="Regenerate SSL Certificates"
title="Regenerate SSL Certificates" buttonTitle="Regenerate SSL Certificates" :actions="[
buttonTitle="Regenerate SSL Certificates"
:actions="[
'The SSL certificate of this database will be regenerated.', 'The SSL certificate of this database will be regenerated.',
'You must restart the database after regenerating the certificate to start using the new certificate.' 'You must restart the database after regenerating the certificate to start using the new certificate.',
]" ]"
submitAction="regenerateSslCertificate" submitAction="regenerateSslCertificate" :confirmWithText="false" :confirmWithPassword="false" />
:confirmWithText="false"
:confirmWithPassword="false"
/>
@endif @endif
</div> </div>
</div> </div>
@if($database->enable_ssl && $certificateValidUntil) @if ($database->enable_ssl && $certificateValidUntil)
<span class="text-sm">Valid until: <span class="text-sm">Valid until:
@if(now()->gt($certificateValidUntil)) @if (now()->gt($certificateValidUntil))
<span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired</span> <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired</span>
@elseif(now()->addDays(30)->gt($certificateValidUntil)) @elseif(now()->addDays(30)->gt($certificateValidUntil))
<span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expiring soon</span> <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expiring
@else soon</span>
<span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }}</span> @else
@endif <span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }}</span>
@endif
</span> </span>
@endif @endif
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.checkbox id="enable_ssl" label="Enable SSL" wire:model.live="enable_ssl" instantSave="instantSaveSSL" /> <div class="w-64">
@if (str($database->status)->contains('exited'))
<x-forms.checkbox id="enable_ssl" label="Enable SSL" wire:model.live="enable_ssl"
instantSave="instantSaveSSL" />
@else
<x-forms.checkbox id="enable_ssl" label="Enable SSL" wire:model.live="enable_ssl"
instantSave="instantSaveSSL" disabled
helper="Database should be stopped to change this settings." />
@endif
</div>
</div> </div>
</div> </div>
<div> <div>

View file

@ -70,36 +70,44 @@
<div class="flex items-center justify-between py-2"> <div class="flex items-center justify-between py-2">
<div class="flex items-center justify-between w-full"> <div class="flex items-center justify-between w-full">
<h3>SSL Configuration</h3> <h3>SSL Configuration</h3>
@if($database->enable_ssl && $certificateValidUntil) @if ($database->enable_ssl && $certificateValidUntil)
<x-modal-confirmation <x-modal-confirmation title="Regenerate SSL Certificates"
title="Regenerate SSL Certificates" buttonTitle="Regenerate SSL Certificates" :actions="[
buttonTitle="Regenerate SSL Certificates" 'The SSL certificate of this database will be regenerated.',
:actions="['The SSL certificate of this database will be regenerated.','You must restart the database after regenerating the certificate to start using the new certificate.']" 'You must restart the database after regenerating the certificate to start using the new certificate.',
submitAction="regenerateSslCertificate" ]"
:confirmWithText="false" submitAction="regenerateSslCertificate" :confirmWithText="false" :confirmWithPassword="false" />
:confirmWithPassword="false"
/>
@endif @endif
</div> </div>
</div> </div>
@if($database->enable_ssl && $certificateValidUntil) @if ($database->enable_ssl && $certificateValidUntil)
<span class="text-sm">Valid until: <span class="text-sm">Valid until:
@if(now()->gt($certificateValidUntil)) @if (now()->gt($certificateValidUntil))
<span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired</span> <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired</span>
@elseif(now()->addDays(30)->gt($certificateValidUntil)) @elseif(now()->addDays(30)->gt($certificateValidUntil))
<span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expiring soon</span> <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expiring
@else soon</span>
<span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }}</span> @else
@endif <span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }}</span>
@endif
</span> </span>
@endif @endif
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL" wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" /> <div class="w-64">
@if (str($database->status)->contains('exited'))
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" />
@else
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" disabled
helper="Database should be stopped to change this settings." />
@endif
</div>
</div> </div>
</div> </div>
<div> <div>
<div class="flex flex-col py-2 w-64"> <div class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2"> <div class="flex items-center gap-2 pb-2">

View file

@ -85,52 +85,71 @@
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL" wire:model.live="database.enable_ssl" <div class="w-64">
instantSave="instantSaveSSL" /> @if (str($database->status)->contains('exited'))
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" />
@else
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" disabled
helper="Database should be stopped to change this settings." />
@endif
</div>
@if ($database->enable_ssl) @if ($database->enable_ssl)
<div class="mx-2"> <div class="mx-2">
<x-forms.select id="database.ssl_mode" label="SSL Mode" wire:model.live="database.ssl_mode" @if (str($database->status)->contains('exited'))
instantSave="instantSaveSSL" <x-forms.select id="database.ssl_mode" label="SSL Mode" wire:model.live="database.ssl_mode"
helper="Choose the SSL verification mode for MongoDB connections"> instantSave="instantSaveSSL"
<option value="allow" title="Allow insecure connections">allow (insecure)</option> helper="Choose the SSL verification mode for MongoDB connections">
<option value="prefer" title="Prefer secure connections">prefer (secure)</option> <option value="allow" title="Allow insecure connections">allow (insecure)</option>
<option value="require" title="Require secure connections">require (secure)</option> <option value="prefer" title="Prefer secure connections">prefer (secure)</option>
<option value="verify-full" title="Verify full certificate">verify-full (secure)</option> <option value="require" title="Require secure connections">require (secure)</option>
</x-forms.select> <option value="verify-full" title="Verify full certificate">verify-full (secure)
</option>
</x-forms.select>
@else
<x-forms.select id="database.ssl_mode" label="SSL Mode" instantSave="instantSaveSSL"
disabled helper="Database should be stopped to change this settings.">
<option value="allow" title="Allow insecure connections">allow (insecure)</option>
<option value="prefer" title="Prefer secure connections">prefer (secure)</option>
<option value="require" title="Require secure connections">require (secure)</option>
<option value="verify-full" title="Verify full certificate">verify-full (secure)
</option>
</x-forms.select>
@endif
</div> </div>
@endif @endif
</div> </div>
</div>
<div> <div>
<div class="flex flex-col py-2 w-64"> <div class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2"> <div class="flex items-center gap-2 pb-2">
<div class="flex items-center"> <div class="flex items-center">
<h3>Proxy</h3> <h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" /> <x-loading wire:loading wire:target="instantSave" />
</div>
@if (data_get($database, 'is_public'))
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div> </div>
@if (data_get($database, 'is_public')) <x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div> </div>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" /> <x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
<x-forms.textarea label="Custom MongoDB Configuration" rows="10" id="database.mongo_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
</div> </div>
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
<x-forms.textarea label="Custom MongoDB Configuration" rows="10" id="database.mongo_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
</div>
</form> </form>
</div> </div>

View file

@ -95,19 +95,38 @@
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL" wire:model.live="database.enable_ssl" <div class="w-64">
instantSave="instantSaveSSL" /> @if (str($database->status)->contains('exited'))
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" />
@else
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" disabled
helper="Database should be stopped to change this settings." />
@endif
</div>
@if ($database->enable_ssl) @if ($database->enable_ssl)
<div class="mx-2"> <div class="mx-2">
<x-forms.select id="database.ssl_mode" label="SSL Mode" wire:model.live="database.ssl_mode" @if (str($database->status)->contains('exited'))
instantSave="instantSaveSSL" <x-forms.select id="database.ssl_mode" label="SSL Mode" wire:model.live="database.ssl_mode"
helper="Choose the SSL verification mode for MySQL connections"> instantSave="instantSaveSSL"
<option value="PREFERRED" title="Prefer secure connections">Prefer (secure)</option> helper="Choose the SSL verification mode for MySQL connections">
<option value="REQUIRED" title="Require secure connections">Require (secure)</option> <option value="PREFERRED" title="Prefer secure connections">Prefer (secure)</option>
<option value="VERIFY_CA" title="Verify CA certificate">Verify CA (secure)</option> <option value="REQUIRED" title="Require secure connections">Require (secure)</option>
<option value="VERIFY_IDENTITY" title="Verify full certificate">Verify Full (secure) <option value="VERIFY_CA" title="Verify CA certificate">Verify CA (secure)</option>
</option> <option value="VERIFY_IDENTITY" title="Verify full certificate">Verify Full (secure)
</x-forms.select> </option>
</x-forms.select>
@else
<x-forms.select id="database.ssl_mode" label="SSL Mode" instantSave="instantSaveSSL"
disabled helper="Database should be stopped to change this settings.">
<option value="PREFERRED" title="Prefer secure connections">Prefer (secure)</option>
<option value="REQUIRED" title="Require secure connections">Require (secure)</option>
<option value="VERIFY_CA" title="Verify CA certificate">Verify CA (secure)</option>
<option value="VERIFY_IDENTITY" title="Verify full certificate">Verify Full (secure)
</option>
</x-forms.select>
@endif
</div> </div>
@endif @endif
</div> </div>

View file

@ -102,52 +102,72 @@
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL" wire:model.live="database.enable_ssl" <div class="w-64" wire:key='enable_ssl'>
instantSave="instantSaveSSL" /> @if ($database->isExited())
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" />
@else
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" disabled
helper="Database should be stopped to change this settings." />
@endif
</div>
@if ($database->enable_ssl) @if ($database->enable_ssl)
<div class="mx-2"> <div class="mx-2">
<x-forms.select id="database.ssl_mode" label="SSL Mode" wire:model.live="database.ssl_mode" @if ($database->isExited())
instantSave="instantSaveSSL" <x-forms.select id="database.ssl_mode" label="SSL Mode"
helper="Choose the SSL verification mode for PostgreSQL connections"> wire:model.live="database.ssl_mode" instantSave="instantSaveSSL"
<option value="allow" title="Allow insecure connections">allow (insecure)</option> helper="Choose the SSL verification mode for PostgreSQL connections">
<option value="prefer" title="Prefer secure connections">prefer (secure)</option> <option value="allow" title="Allow insecure connections">allow (insecure)</option>
<option value="require" title="Require secure connections">require (secure)</option> <option value="prefer" title="Prefer secure connections">prefer (secure)</option>
<option value="verify-ca" title="Verify CA certificate">verify-ca (secure)</option> <option value="require" title="Require secure connections">require (secure)</option>
<option value="verify-full" title="Verify full certificate">verify-full (secure)</option> <option value="verify-ca" title="Verify CA certificate">verify-ca (secure)</option>
</x-forms.select> <option value="verify-full" title="Verify full certificate">verify-full (secure)
</option>
</x-forms.select>
@else
<x-forms.select id="database.ssl_mode" label="SSL Mode" instantSave="instantSaveSSL"
disabled helper="Database should be stopped to change this settings.">
<option value="allow" title="Allow insecure connections">allow (insecure)</option>
<option value="prefer" title="Prefer secure connections">prefer (secure)</option>
<option value="require" title="Require secure connections">require (secure)</option>
<option value="verify-ca" title="Verify CA certificate">verify-ca (secure)</option>
<option value="verify-full" title="Verify full certificate">verify-full (secure)
</option>
</x-forms.select>
@endif
</div> </div>
@endif @endif
</div>
</div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex items-center justify-between py-2"> <div class="flex items-center justify-between py-2">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h3>Proxy</h3> <h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" /> <x-loading wire:loading wire:target="instantSave" />
</div>
@if (data_get($database, 'is_public'))
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<div class="flex flex-col gap-2">
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
</div> </div>
@if (data_get($database, 'is_public'))
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<div class="flex flex-col gap-2">
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
</div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.textarea label="Custom PostgreSQL Configuration" rows="10" id="database.postgres_conf" /> <x-forms.textarea label="Custom PostgreSQL Configuration" rows="10"
</div> id="database.postgres_conf" />
</div>
</form> </form>
<div class="flex flex-col gap-4 pt-4"> <div class="flex flex-col gap-4 pt-4">

View file

@ -53,34 +53,39 @@
<div class="flex items-center justify-between py-2"> <div class="flex items-center justify-between py-2">
<div class="flex items-center justify-between w-full"> <div class="flex items-center justify-between w-full">
<h3>SSL Configuration</h3> <h3>SSL Configuration</h3>
@if($database->enable_ssl && $certificateValidUntil) @if ($database->enable_ssl && $certificateValidUntil)
<x-modal-confirmation <x-modal-confirmation title="Regenerate SSL Certificates"
title="Regenerate SSL Certificates" buttonTitle="Regenerate SSL Certificates" :actions="[
buttonTitle="Regenerate SSL Certificates"
:actions="[
'The SSL certificate of this database will be regenerated.', 'The SSL certificate of this database will be regenerated.',
'You must restart the database after regenerating the certificate to start using the new certificate.' 'You must restart the database after regenerating the certificate to start using the new certificate.',
]" ]"
submitAction="regenerateSslCertificate" submitAction="regenerateSslCertificate" :confirmWithText="false" :confirmWithPassword="false" />
:confirmWithText="false"
:confirmWithPassword="false"
/>
@endif @endif
</div> </div>
</div> </div>
@if($database->enable_ssl && $certificateValidUntil) @if ($database->enable_ssl && $certificateValidUntil)
<span class="text-sm">Valid until: <span class="text-sm">Valid until:
@if(now()->gt($certificateValidUntil)) @if (now()->gt($certificateValidUntil))
<span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired</span> <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expired</span>
@elseif(now()->addDays(30)->gt($certificateValidUntil)) @elseif(now()->addDays(30)->gt($certificateValidUntil))
<span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expiring soon</span> <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} - Expiring
@else soon</span>
<span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }}</span> @else
@endif <span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }}</span>
@endif
</span> </span>
@endif @endif
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL" wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" /> <div class="w-64" wire:key='enable_ssl'>
@if (str($database->status)->contains('exited'))
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" />
@else
<x-forms.checkbox id="database.enable_ssl" label="Enable SSL"
wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" disabled
helper="Database should be stopped to change this settings." />
@endif
</div>
</div> </div>
</div> </div>
<div> <div>

View file

@ -30,9 +30,11 @@ fi
# Make sure coolify network exists # Make sure coolify network exists
# It is created when starting Coolify with docker compose # It is created when starting Coolify with docker compose
if ! docker network create --attachable --ipv6 coolify 2>/dev/null; then if ! docker network inspect coolify >/dev/null 2>&1; then
echo "Failed to create coolify network with ipv6. Trying without ipv6..." if ! docker network create --attachable --ipv6 coolify 2>/dev/null; then
docker network create --attachable coolify 2>/dev/null echo "Failed to create coolify network with ipv6. Trying without ipv6..."
docker network create --attachable coolify 2>/dev/null
fi
fi fi
# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null # docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null

View file

@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.399" "version": "4.0.0-beta.400"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.400" "version": "4.0.0-beta.401"
}, },
"helper": { "helper": {
"version": "1.0.7" "version": "1.0.7"