diff --git a/app/Actions/Database/StartDatabaseProxy.php b/app/Actions/Database/StartDatabaseProxy.php index 744bbaa50..21fd6eb97 100644 --- a/app/Actions/Database/StartDatabaseProxy.php +++ b/app/Actions/Database/StartDatabaseProxy.php @@ -27,6 +27,8 @@ class StartDatabaseProxy $server = data_get($database, 'destination.server'); $containerName = data_get($database, 'uuid'); $proxyContainerName = "{$database->uuid}-proxy"; + $isSSLEnabled = $database->enable_ssl ?? false; + if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) { $databaseType = $database->databaseType(); $network = $database->service->uuid; @@ -42,6 +44,12 @@ class StartDatabaseProxy 'standalone-mongodb' => 27017, default => throw new \Exception("Unsupported database type: $databaseType"), }; + if ($isSSLEnabled) { + $internalPort = match ($databaseType) { + 'standalone-redis', 'standalone-keydb', 'standalone-dragonfly' => 6380, + default => throw new \Exception("Unsupported database type: $databaseType"), + }; + } $configuration_dir = database_proxy_dir($database->uuid); if (isDev()) { diff --git a/app/Actions/Service/StartService.php b/app/Actions/Service/StartService.php index d48594e62..dfef6a566 100644 --- a/app/Actions/Service/StartService.php +++ b/app/Actions/Service/StartService.php @@ -19,6 +19,7 @@ class StartService StopService::run(service: $service, dockerCleanup: false); } $service->saveComposeConfigs(); + $service->isConfigurationChanged(save: true); $commands[] = 'cd '.$service->workdir(); $commands[] = "echo 'Saved configuration files to {$service->workdir()}.'"; if ($pullLatestImages) { diff --git a/app/Jobs/CleanupInstanceStuffsJob.php b/app/Jobs/CleanupInstanceStuffsJob.php index 53e344daf..60ae58489 100644 --- a/app/Jobs/CleanupInstanceStuffsJob.php +++ b/app/Jobs/CleanupInstanceStuffsJob.php @@ -23,7 +23,7 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho public function middleware(): array { - return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()]; + return [(new WithoutOverlapping('cleanup-instance-stuffs'))->expireAfter(60)->dontRelease()]; } public function handle(): void diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index 00dfd150a..519728ab0 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -31,7 +31,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue public function middleware(): array { - return [(new WithoutOverlapping('docker-cleanup-'.$this->server->uuid))->dontRelease()]; + return [(new WithoutOverlapping('docker-cleanup-'.$this->server->uuid))->expireAfter(600)->dontRelease()]; } public function __construct(public Server $server, public bool $manualCleanup = false) {} diff --git a/app/Jobs/PushServerUpdateJob.php b/app/Jobs/PushServerUpdateJob.php index 65c72c8c9..61206da6f 100644 --- a/app/Jobs/PushServerUpdateJob.php +++ b/app/Jobs/PushServerUpdateJob.php @@ -70,7 +70,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue public function middleware(): array { - return [(new WithoutOverlapping('push-server-update-'.$this->server->uuid))->dontRelease()]; + return [(new WithoutOverlapping('push-server-update-'.$this->server->uuid))->expireAfter(30)->dontRelease()]; } public function backoff(): int diff --git a/app/Jobs/RestartProxyJob.php b/app/Jobs/RestartProxyJob.php index 49cfbba53..dba4f4ac8 100644 --- a/app/Jobs/RestartProxyJob.php +++ b/app/Jobs/RestartProxyJob.php @@ -23,7 +23,7 @@ class RestartProxyJob implements ShouldBeEncrypted, ShouldQueue public function middleware(): array { - return [(new WithoutOverlapping('restart-proxy-'.$this->server->uuid))->dontRelease()]; + return [(new WithoutOverlapping('restart-proxy-'.$this->server->uuid))->expireAfter(60)->dontRelease()]; } public function __construct(public Server $server) {} diff --git a/app/Jobs/ServerCheckJob.php b/app/Jobs/ServerCheckJob.php index dcbac9ac3..499035237 100644 --- a/app/Jobs/ServerCheckJob.php +++ b/app/Jobs/ServerCheckJob.php @@ -28,7 +28,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue public function middleware(): array { - return [(new WithoutOverlapping('server-check-'.$this->server->uuid))->dontRelease()]; + return [(new WithoutOverlapping('server-check-'.$this->server->uuid))->expireAfter(60)->dontRelease()]; } public function __construct(public Server $server) {} diff --git a/app/Jobs/ServerPatchCheckJob.php b/app/Jobs/ServerPatchCheckJob.php index 7d1b853b1..18999c009 100644 --- a/app/Jobs/ServerPatchCheckJob.php +++ b/app/Jobs/ServerPatchCheckJob.php @@ -19,11 +19,11 @@ class ServerPatchCheckJob implements ShouldBeEncrypted, ShouldQueue public $tries = 3; - public $timeout = 600; // 10 minutes timeout + public $timeout = 600; public function middleware(): array { - return [(new WithoutOverlapping('server-patch-check-'.$this->server->uuid))->dontRelease()]; + return [(new WithoutOverlapping('server-patch-check-'.$this->server->uuid))->expireAfter(600)->dontRelease()]; } public function __construct(public Server $server) {} diff --git a/app/Livewire/Project/CloneMe.php b/app/Livewire/Project/CloneMe.php index fefaff4f2..a7c44577c 100644 --- a/app/Livewire/Project/CloneMe.php +++ b/app/Livewire/Project/CloneMe.php @@ -56,7 +56,6 @@ class CloneMe extends Component $this->project_id = $this->project->id; $this->servers = currentTeam() ->servers() - ->with('destinations') ->get() ->reject(fn ($server) => $server->isBuildServer()); $this->newName = str($this->project->name.'-clone-'.(string) new Cuid2)->slug(); diff --git a/app/Livewire/Project/Service/Heading.php b/app/Livewire/Project/Service/Heading.php index d78fbe9ed..3492da324 100644 --- a/app/Livewire/Project/Service/Heading.php +++ b/app/Livewire/Project/Service/Heading.php @@ -63,7 +63,7 @@ class Heading extends Component $this->service->databases->each(function ($database) { $database->refresh(); }); - if (is_null($this->service->config_hash) || $this->service->isConfigurationChanged()) { + if (is_null($this->service->config_hash)) { $this->service->isConfigurationChanged(true); } $this->dispatch('configurationChanged'); diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php index 535ac6c67..966d626b1 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -170,6 +170,7 @@ class Show extends Component $this->syncData(true); $this->dispatch('success', 'Environment variable updated.'); $this->dispatch('envsUpdated'); + $this->dispatch('configurationChanged'); } catch (\Exception $e) { return handleError($e); } diff --git a/app/Livewire/Project/Shared/Terminal.php b/app/Livewire/Project/Shared/Terminal.php index 819d364e2..de2deeed4 100644 --- a/app/Livewire/Project/Shared/Terminal.php +++ b/app/Livewire/Project/Shared/Terminal.php @@ -68,11 +68,16 @@ class Terminal extends Component // Escape the identifier for shell usage $escapedIdentifier = escapeshellarg($identifier); - $command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$escapedIdentifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'"); + $shellCommand = 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && '. + 'if [ -f ~/.profile ]; then . ~/.profile; fi && '. + 'if [ -n "$SHELL" ] && [ -x "$SHELL" ]; then exec $SHELL; else sh; fi'; + $command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$escapedIdentifier} sh -c '{$shellCommand}'"); } else { - $command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi'); + $shellCommand = 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && '. + 'if [ -f ~/.profile ]; then . ~/.profile; fi && '. + 'if [ -n "$SHELL" ] && [ -x "$SHELL" ]; then exec $SHELL; else sh; fi'; + $command = SshMultiplexingHelper::generateSshCommand($server, $shellCommand); } - // ssh command is sent back to frontend then to websocket // this is done because the websocket connection is not available here // a better solution would be to remove websocket on NodeJS and work with something like diff --git a/app/Livewire/Settings/Advanced.php b/app/Livewire/Settings/Advanced.php new file mode 100644 index 000000000..4425b414d --- /dev/null +++ b/app/Livewire/Settings/Advanced.php @@ -0,0 +1,118 @@ +route('dashboard'); + } + $this->server = Server::findOrFail(0); + $this->settings = instanceSettings(); + $this->custom_dns_servers = $this->settings->custom_dns_servers; + $this->allowed_ips = $this->settings->allowed_ips; + $this->do_not_track = $this->settings->do_not_track; + $this->is_registration_enabled = $this->settings->is_registration_enabled; + $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled; + $this->is_api_enabled = $this->settings->is_api_enabled; + $this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation; + $this->is_sponsorship_popup_enabled = $this->settings->is_sponsorship_popup_enabled; + } + + public function submit() + { + try { + $this->validate(); + + $this->custom_dns_servers = str($this->custom_dns_servers)->replaceEnd(',', '')->trim(); + $this->custom_dns_servers = str($this->custom_dns_servers)->trim()->explode(',')->map(function ($dns) { + return str($dns)->trim()->lower(); + })->unique()->implode(','); + + $this->allowed_ips = str($this->allowed_ips)->replaceEnd(',', '')->trim(); + $this->allowed_ips = str($this->allowed_ips)->trim()->explode(',')->map(function ($ip) { + return str($ip)->trim(); + })->unique()->implode(','); + + $this->instantSave(); + } catch (\Exception $e) { + return handleError($e, $this); + } + } + + public function instantSave() + { + try { + $this->settings->is_registration_enabled = $this->is_registration_enabled; + $this->settings->do_not_track = $this->do_not_track; + $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled; + $this->settings->custom_dns_servers = $this->custom_dns_servers; + $this->settings->is_api_enabled = $this->is_api_enabled; + $this->settings->allowed_ips = $this->allowed_ips; + $this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled; + $this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation; + $this->settings->save(); + $this->dispatch('success', 'Settings updated!'); + } catch (\Exception $e) { + return handleError($e, $this); + } + } + + public function toggleTwoStepConfirmation($password): bool + { + if (! Hash::check($password, Auth::user()->password)) { + $this->addError('password', 'The provided password is incorrect.'); + + return false; + } + + $this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation = true; + $this->settings->save(); + $this->dispatch('success', 'Two step confirmation has been disabled.'); + + return true; + } + + public function render() + { + return view('livewire.settings.advanced'); + } +} diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php index f9df7ee33..bce343224 100644 --- a/app/Livewire/Settings/Index.php +++ b/app/Livewire/Settings/Index.php @@ -2,11 +2,8 @@ namespace App\Livewire\Settings; -use App\Jobs\CheckForUpdatesJob; use App\Models\InstanceSettings; use App\Models\Server; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Attributes\Computed; use Livewire\Attributes\Validate; use Livewire\Component; @@ -15,10 +12,7 @@ class Index extends Component { public InstanceSettings $settings; - protected Server $server; - - #[Validate('boolean')] - public bool $is_auto_update_enabled; + public Server $server; #[Validate('nullable|string|max:255')] public ?string $fqdn = null; @@ -29,48 +23,18 @@ class Index extends Component #[Validate('required|integer|min:1025|max:65535')] public int $public_port_max; - #[Validate('nullable|string')] - public ?string $custom_dns_servers = null; - #[Validate('nullable|string|max:255')] public ?string $instance_name = null; - #[Validate('nullable|string')] - public ?string $allowed_ips = null; - #[Validate('nullable|string')] public ?string $public_ipv4 = null; #[Validate('nullable|string')] public ?string $public_ipv6 = null; - #[Validate('string')] - public string $auto_update_frequency; - - #[Validate('string|required')] - public string $update_check_frequency; - #[Validate('required|string|timezone')] public string $instance_timezone; - #[Validate('boolean')] - public bool $do_not_track; - - #[Validate('boolean')] - public bool $is_registration_enabled; - - #[Validate('boolean')] - public bool $is_dns_validation_enabled; - - #[Validate('boolean')] - public bool $is_api_enabled; - - #[Validate('boolean')] - public bool $disable_two_step_confirmation; - - #[Validate('boolean')] - public bool $is_sponsorship_popup_enabled; - public function render() { return view('livewire.settings.index'); @@ -80,27 +44,16 @@ class Index extends Component { if (! isInstanceAdmin()) { return redirect()->route('dashboard'); - } else { - $this->settings = instanceSettings(); - $this->fqdn = $this->settings->fqdn; - $this->public_port_min = $this->settings->public_port_min; - $this->public_port_max = $this->settings->public_port_max; - $this->custom_dns_servers = $this->settings->custom_dns_servers; - $this->instance_name = $this->settings->instance_name; - $this->allowed_ips = $this->settings->allowed_ips; - $this->public_ipv4 = $this->settings->public_ipv4; - $this->public_ipv6 = $this->settings->public_ipv6; - $this->do_not_track = $this->settings->do_not_track; - $this->is_auto_update_enabled = $this->settings->is_auto_update_enabled; - $this->is_registration_enabled = $this->settings->is_registration_enabled; - $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled; - $this->is_api_enabled = $this->settings->is_api_enabled; - $this->auto_update_frequency = $this->settings->auto_update_frequency; - $this->update_check_frequency = $this->settings->update_check_frequency; - $this->instance_timezone = $this->settings->instance_timezone; - $this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation; - $this->is_sponsorship_popup_enabled = $this->settings->is_sponsorship_popup_enabled; } + $this->settings = instanceSettings(); + $this->server = Server::findOrFail(0); + $this->fqdn = $this->settings->fqdn; + $this->public_port_min = $this->settings->public_port_min; + $this->public_port_max = $this->settings->public_port_max; + $this->instance_name = $this->settings->instance_name; + $this->public_ipv4 = $this->settings->public_ipv4; + $this->public_ipv6 = $this->settings->public_ipv6; + $this->instance_timezone = $this->settings->instance_timezone; } #[Computed] @@ -115,30 +68,13 @@ class Index extends Component public function instantSave($isSave = true) { $this->validate(); - if ($this->settings->is_auto_update_enabled === true) { - $this->validate([ - 'auto_update_frequency' => ['required', 'string'], - ]); - } - $this->settings->fqdn = $this->fqdn; $this->settings->public_port_min = $this->public_port_min; $this->settings->public_port_max = $this->public_port_max; - $this->settings->custom_dns_servers = $this->custom_dns_servers; $this->settings->instance_name = $this->instance_name; - $this->settings->allowed_ips = $this->allowed_ips; $this->settings->public_ipv4 = $this->public_ipv4; $this->settings->public_ipv6 = $this->public_ipv6; - $this->settings->do_not_track = $this->do_not_track; - $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled; - $this->settings->is_registration_enabled = $this->is_registration_enabled; - $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled; - $this->settings->is_api_enabled = $this->is_api_enabled; - $this->settings->auto_update_frequency = $this->auto_update_frequency; - $this->settings->update_check_frequency = $this->update_check_frequency; - $this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation; $this->settings->instance_timezone = $this->instance_timezone; - $this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled; if ($isSave) { $this->settings->save(); $this->dispatch('success', 'Settings updated!'); @@ -149,7 +85,6 @@ class Index extends Component { try { $error_show = false; - $this->server = Server::findOrFail(0); $this->resetErrorBag(); if (! validate_timezone($this->instance_timezone)) { @@ -166,46 +101,15 @@ class Index extends Component } $this->validate(); - if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) { - $this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.'); - if (empty($this->auto_update_frequency)) { - $this->auto_update_frequency = '0 0 * * *'; - } - - return; - } - - if (! validate_cron_expression($this->update_check_frequency)) { - $this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.'); - if (empty($this->update_check_frequency)) { - $this->update_check_frequency = '0 * * * *'; - } - - return; - } - - if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) { - if (! validate_dns_entry($this->settings->fqdn, $this->server)) { - $this->dispatch('error', "Validating DNS failed.

Make sure you have added the DNS records correctly.

{$this->settings->fqdn}->{$this->server->ip}

Check this documentation for further help."); + if ($this->settings->is_dns_validation_enabled && $this->fqdn) { + if (! validate_dns_entry($this->fqdn, $this->server)) { + $this->dispatch('error', "Validating DNS failed.

Make sure you have added the DNS records correctly.

{$this->fqdn}->{$this->server->ip}

Check this documentation for further help."); $error_show = true; } } - if ($this->settings->fqdn) { - check_domain_usage(domain: $this->settings->fqdn); + if ($this->fqdn) { + check_domain_usage(domain: $this->fqdn); } - $this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim(); - $this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) { - return str($dns)->trim()->lower(); - }); - $this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique(); - $this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(','); - - $this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim(); - $this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) { - return str($ip)->trim(); - }); - $this->settings->allowed_ips = $this->settings->allowed_ips->unique(); - $this->settings->allowed_ips = $this->settings->allowed_ips->implode(','); $this->instantSave(isSave: false); @@ -218,31 +122,4 @@ class Index extends Component return handleError($e, $this); } } - - public function checkManually() - { - CheckForUpdatesJob::dispatchSync(); - $this->dispatch('updateAvailable'); - $settings = instanceSettings(); - if ($settings->new_version_available) { - $this->dispatch('success', 'New version available!'); - } else { - $this->dispatch('success', 'No new version available.'); - } - } - - public function toggleTwoStepConfirmation($password): bool - { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return false; - } - - $this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation = true; - $this->settings->save(); - $this->dispatch('success', 'Two step confirmation has been disabled.'); - - return true; - } } diff --git a/app/Livewire/Settings/Updates.php b/app/Livewire/Settings/Updates.php new file mode 100644 index 000000000..fe20763b6 --- /dev/null +++ b/app/Livewire/Settings/Updates.php @@ -0,0 +1,101 @@ +server = Server::findOrFail(0); + + $this->settings = instanceSettings(); + $this->auto_update_frequency = $this->settings->auto_update_frequency; + $this->update_check_frequency = $this->settings->update_check_frequency; + $this->is_auto_update_enabled = $this->settings->is_auto_update_enabled; + } + + public function instantSave() + { + try { + if ($this->settings->is_auto_update_enabled === true) { + $this->validate([ + 'auto_update_frequency' => ['required', 'string'], + ]); + } + $this->settings->auto_update_frequency = $this->auto_update_frequency; + $this->settings->update_check_frequency = $this->update_check_frequency; + $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled; + $this->settings->save(); + $this->dispatch('success', 'Settings updated!'); + } catch (\Exception $e) { + return handleError($e, $this); + } + } + + public function submit() + { + try { + $this->resetErrorBag(); + $this->validate(); + + if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) { + $this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.'); + if (empty($this->auto_update_frequency)) { + $this->auto_update_frequency = '0 0 * * *'; + } + + return; + } + + if (! validate_cron_expression($this->update_check_frequency)) { + $this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.'); + if (empty($this->update_check_frequency)) { + $this->update_check_frequency = '0 * * * *'; + } + + return; + } + + $this->instantSave(); + $this->server->setupDynamicProxyConfiguration(); + } catch (\Exception $e) { + return handleError($e, $this); + } + } + + public function checkManually() + { + CheckForUpdatesJob::dispatchSync(); + $this->dispatch('updateAvailable'); + $settings = instanceSettings(); + if ($settings->new_version_available) { + $this->dispatch('success', 'New version available!'); + } else { + $this->dispatch('success', 'No new version available.'); + } + } + + public function render() + { + return view('livewire.settings.updates'); + } +} diff --git a/app/Livewire/SettingsBackup.php b/app/Livewire/SettingsBackup.php index bb5ed0aa8..57cb79fca 100644 --- a/app/Livewire/SettingsBackup.php +++ b/app/Livewire/SettingsBackup.php @@ -46,32 +46,31 @@ class SettingsBackup extends Component { if (! isInstanceAdmin()) { return redirect()->route('dashboard'); - } else { - $settings = instanceSettings(); - $this->server = Server::findOrFail(0); - $this->database = StandalonePostgresql::whereName('coolify-db')->first(); - $s3s = S3Storage::whereTeamId(0)->get() ?? []; - if ($this->database) { - $this->uuid = $this->database->uuid; - $this->name = $this->database->name; - $this->description = $this->database->description; - $this->postgres_user = $this->database->postgres_user; - $this->postgres_password = $this->database->postgres_password; - - if ($this->database->status !== 'running') { - $this->database->status = 'running'; - $this->database->save(); - } - $this->backup = $this->database->scheduledBackups->first(); - if ($this->backup && ! $this->server->isFunctional()) { - $this->backup->enabled = false; - $this->backup->save(); - } - $this->executions = $this->backup->executions; - } - $this->settings = $settings; - $this->s3s = $s3s; } + $settings = instanceSettings(); + $this->server = Server::findOrFail(0); + $this->database = StandalonePostgresql::whereName('coolify-db')->first(); + $s3s = S3Storage::whereTeamId(0)->get() ?? []; + if ($this->database) { + $this->uuid = $this->database->uuid; + $this->name = $this->database->name; + $this->description = $this->database->description; + $this->postgres_user = $this->database->postgres_user; + $this->postgres_password = $this->database->postgres_password; + + if ($this->database->status !== 'running') { + $this->database->status = 'running'; + $this->database->save(); + } + $this->backup = $this->database->scheduledBackups->first(); + if ($this->backup && ! $this->server->isFunctional()) { + $this->backup->enabled = false; + $this->backup->save(); + } + $this->executions = $this->backup->executions; + } + $this->settings = $settings; + $this->s3s = $s3s; } public function addCoolifyDatabase() diff --git a/app/Livewire/Storage/Form.php b/app/Livewire/Storage/Form.php index 8ca0020c7..ad1627863 100644 --- a/app/Livewire/Storage/Form.php +++ b/app/Livewire/Storage/Form.php @@ -31,7 +31,7 @@ class Form extends Component 'storage.endpoint' => 'Endpoint', ]; - public function test_s3_connection() + public function testConnection() { try { $this->storage->testConnection(shouldSave: true); @@ -45,6 +45,8 @@ class Form extends Component public function delete() { try { + $this->authorize('delete', $this->storage); + $this->storage->delete(); return redirect()->route('storage.index'); @@ -57,7 +59,7 @@ class Form extends Component { $this->validate(); try { - $this->test_s3_connection(); + $this->testConnection(); } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Models/Server.php b/app/Models/Server.php index eac5bbcc1..41ecdafb8 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -887,7 +887,7 @@ $schema://$host { public function muxFilename() { - return $this->uuid; + return 'mux_'.$this->uuid; } public function team() diff --git a/app/Policies/S3StoragePolicy.php b/app/Policies/S3StoragePolicy.php new file mode 100644 index 000000000..28f5f8426 --- /dev/null +++ b/app/Policies/S3StoragePolicy.php @@ -0,0 +1,66 @@ +teams()->where('id', $storage->team_id)->exists(); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Server $server): bool + { + return $user->teams()->get()->firstWhere('id', $server->team_id) !== null; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, S3Storage $storage): bool + { + return $user->teams()->where('id', $storage->team_id)->exists(); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, S3Storage $storage): bool + { + return false; + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, S3Storage $storage): bool + { + return false; + } +} diff --git a/config/constants.php b/config/constants.php index 2ab7596ff..946c41f60 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.420.1', + 'version' => '4.0.0-beta.420.2', 'helper_version' => '1.0.8', 'realtime_version' => '1.0.9', 'self_hosted' => env('SELF_HOSTED', true), diff --git a/other/nightly/versions.json b/other/nightly/versions.json index 9fde1e53b..8d362115e 100644 --- a/other/nightly/versions.json +++ b/other/nightly/versions.json @@ -1,10 +1,10 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.420" + "version": "4.0.0-beta.420.2" }, "nightly": { - "version": "4.0.0-beta.421" + "version": "4.0.0-beta.420.3" }, "helper": { "version": "1.0.8" diff --git a/public/svgs/excalidraw.svg b/public/svgs/excalidraw.svg new file mode 100644 index 000000000..ee996762e --- /dev/null +++ b/public/svgs/excalidraw.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/css/utilities.css b/resources/css/utilities.css index fe0cd10ed..d09d7f49c 100644 --- a/resources/css/utilities.css +++ b/resources/css/utilities.css @@ -42,7 +42,7 @@ } @utility button { - @apply flex gap-2 justify-center items-center px-2 py-1 text-sm text-black normal-case rounded-sm border outline-0 cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-none disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300; + @apply flex gap-2 justify-center items-center px-2 py-1 text-sm text-black normal-case rounded-sm border outline-0 cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-transparent disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300; } @utility alert-success { diff --git a/resources/views/components/settings/navbar.blade.php b/resources/views/components/settings/navbar.blade.php index a54dc1886..395c0953e 100644 --- a/resources/views/components/settings/navbar.blade.php +++ b/resources/views/components/settings/navbar.blade.php @@ -5,19 +5,19 @@ diff --git a/resources/views/components/settings/sidebar.blade.php b/resources/views/components/settings/sidebar.blade.php new file mode 100644 index 000000000..ef2f0d5d2 --- /dev/null +++ b/resources/views/components/settings/sidebar.blade.php @@ -0,0 +1,8 @@ +
+ General + Advanced + Updates +
diff --git a/resources/views/components/team/navbar.blade.php b/resources/views/components/team/navbar.blade.php index aa88aad51..89bdf8654 100644 --- a/resources/views/components/team/navbar.blade.php +++ b/resources/views/components/team/navbar.blade.php @@ -9,16 +9,16 @@