diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index daee357d5..99206606d 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -27,7 +27,7 @@ class StartProxy $server->save(); if ($server->isSwarm()) { $commands = $commands->merge([ - "mkdir -p $proxy_path && cd $proxy_path", + "mkdir -p $proxy_path/dynamic && cd $proxy_path", "echo 'Creating required Docker Compose file.'", "echo 'Starting coolify-proxy.'", "cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy", @@ -35,7 +35,7 @@ class StartProxy ]); } else { $commands = $commands->merge([ - "mkdir -p $proxy_path && cd $proxy_path", + "mkdir -p $proxy_path/dynamic && cd $proxy_path", "echo 'Creating required Docker Compose file.'", "echo 'Pulling docker image.'", 'docker compose pull', diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 457cc04ad..93dad8e8a 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -488,7 +488,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted } else { $this->application_deployment_queue->addLogEntry("Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch}."); } - ray('asddf'); $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->clone_repository(); diff --git a/app/Jobs/ScheduledTaskJob.php b/app/Jobs/ScheduledTaskJob.php index 227807b41..4a38a005b 100644 --- a/app/Jobs/ScheduledTaskJob.php +++ b/app/Jobs/ScheduledTaskJob.php @@ -39,7 +39,7 @@ class ScheduledTaskJob implements ShouldQueue } else if ($application = $task->application()->first()) { $this->resource = $application; } else { - throw new \Exception('ScheduledTaskJob failed: No resource found.'); + throw new \RuntimeException('ScheduledTaskJob failed: No resource found.'); } $this->team = Team::find($task->team_id); } diff --git a/app/Livewire/Project/DeleteEnvironment.php b/app/Livewire/Project/DeleteEnvironment.php index 0f2b59c93..c64ebd4b2 100644 --- a/app/Livewire/Project/DeleteEnvironment.php +++ b/app/Livewire/Project/DeleteEnvironment.php @@ -9,6 +9,7 @@ class DeleteEnvironment extends Component { public array $parameters; public int $environment_id; + public bool $disabled = false; public function mount() { diff --git a/app/Livewire/Project/DeleteProject.php b/app/Livewire/Project/DeleteProject.php index 7ac4aa281..543b45784 100644 --- a/app/Livewire/Project/DeleteProject.php +++ b/app/Livewire/Project/DeleteProject.php @@ -9,6 +9,7 @@ class DeleteProject extends Component { public array $parameters; public int $project_id; + public bool $disabled = false; public function mount() { diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php index 32c174520..afbdd47bc 100644 --- a/app/Livewire/Project/Service/Configuration.php +++ b/app/Livewire/Project/Service/Configuration.php @@ -8,7 +8,7 @@ use Livewire\Component; class Configuration extends Component { - public Service $service; + public ?Service $service = null; public $applications; public $databases; public array $parameters; diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index b0f1c97c7..f19cf8d53 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -17,8 +17,8 @@ class Danger extends Component { $this->modalId = new Cuid2(7); $parameters = get_route_parameters(); - $this->projectUuid = $parameters['project_uuid']; - $this->environmentName = $parameters['environment_name']; + $this->projectUuid = data_get($parameters, 'project_uuid'); + $this->environmentName = data_get($parameters, 'environment_name'); } public function delete() diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php index 78ed3c780..923f0d455 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php @@ -32,6 +32,13 @@ class Add extends Component public function submit() { $this->validate(); + if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) { + $type = str($this->value)->after("{{")->before(".")->value; + if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + return; + } + } $this->dispatch('saveKey', [ 'key' => $this->key, 'value' => $this->value, diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 77644519a..c30011a4a 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -71,12 +71,26 @@ class All extends Component continue; } $found->value = $variable; + if (str($found->value)->startsWith('{{') && str($found->value)->endsWith('}}')) { + $type = str($found->value)->after("{{")->before(".")->value; + if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + return; + } + } $found->save(); continue; } else { $environment = new EnvironmentVariable(); $environment->key = $key; $environment->value = $variable; + if (str($environment->value)->startsWith('{{') && str($environment->value)->endsWith('}}')) { + $type = str($environment->value)->after("{{")->before(".")->value; + if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + return; + } + } $environment->is_build_time = false; $environment->is_preview = $isPreview ? true : false; switch ($this->resource->type()) { diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php index f8709afd8..1494707e7 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -50,7 +50,8 @@ class Show extends Component $this->isLocked = true; } } - public function serialize() { + public function serialize() + { data_forget($this->env, 'real_value'); if ($this->env->getMorphClass() === 'App\Models\SharedEnvironmentVariable') { data_forget($this->env, 'is_build_time'); @@ -80,11 +81,18 @@ class Show extends Component } else { $this->validate(); } + if (str($this->env->value)->startsWith('{{') && str($this->env->value)->endsWith('}}')) { + $type = str($this->env->value)->after("{{")->before(".")->value; + if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + return; + } + } $this->serialize(); $this->env->save(); $this->dispatch('success', 'Environment variable updated successfully.'); $this->dispatch('refreshEnvs'); - } catch(\Exception $e) { + } catch (\Exception $e) { return handleError($e); } } diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php index 125bf2fb4..e2037991a 100644 --- a/app/Livewire/Project/Shared/ResourceOperations.php +++ b/app/Livewire/Project/Shared/ResourceOperations.php @@ -20,8 +20,8 @@ class ResourceOperations extends Component public function mount() { $parameters = get_route_parameters(); - $this->projectUuid = $parameters['project_uuid']; - $this->environmentName = $parameters['environment_name']; + $this->projectUuid = data_get($parameters, 'project_uuid'); + $this->environmentName = data_get($parameters, 'environment_name'); $this->projects = Project::ownedByCurrentTeam()->get(); $this->servers = currentTeam()->servers; } diff --git a/app/Livewire/Team/Invitations.php b/app/Livewire/Team/Invitations.php index c51723572..436c0778d 100644 --- a/app/Livewire/Team/Invitations.php +++ b/app/Livewire/Team/Invitations.php @@ -12,7 +12,11 @@ class Invitations extends Component public function deleteInvitation(int $invitation_id) { - TeamInvitation::find($invitation_id)->delete(); + $initiation_found = TeamInvitation::find($invitation_id); + if (!$initiation_found) { + return $this->dispatch('error', 'Invitation not found.'); + } + $initiation_found->delete(); $this->refreshInvitations(); $this->dispatch('success', 'Invitation revoked.'); } diff --git a/app/Livewire/Team/Storage/Create.php b/app/Livewire/Team/Storage/Create.php index 82f93320b..a25fc9821 100644 --- a/app/Livewire/Team/Storage/Create.php +++ b/app/Livewire/Team/Storage/Create.php @@ -67,7 +67,8 @@ class Create extends Component $this->storage->save(); return redirect()->route('team.storage.show', $this->storage->uuid); } catch (\Throwable $e) { - return handleError($e, $this); + $this->dispatch('error', 'Failed to create storage.', $e->getMessage()); + // return handleError($e, $this); } } } diff --git a/app/Livewire/Team/Storage/Form.php b/app/Livewire/Team/Storage/Form.php index 8a26a3471..1fd0d470b 100644 --- a/app/Livewire/Team/Storage/Form.php +++ b/app/Livewire/Team/Storage/Form.php @@ -33,9 +33,9 @@ class Form extends Component { try { $this->storage->testConnection(shouldSave: true); - return $this->dispatch('success', 'Connection is working. Tested with "ListObjectsV2" action.'); + return $this->dispatch('success', 'Connection is working.', 'Tested with "ListObjectsV2" action.'); } catch (\Throwable $e) { - return handleError($e, $this); + $this->dispatch('error', 'Failed to create storage.', $e->getMessage()); } } diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index 4ecc6699c..a3611a0c6 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -91,7 +91,7 @@ class EnvironmentVariable extends Model } private function get_real_environment_variables(?string $environment_variable = null, $resource = null): string|null { - if (!$environment_variable) { + if (!$environment_variable || !$resource) { return null; } $environment_variable = trim($environment_variable); @@ -100,6 +100,9 @@ class EnvironmentVariable extends Model $variable = Str::after($environment_variable, "{$type}."); $variable = Str::before($variable, '}}'); $variable = Str::of($variable)->trim()->value; + if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { + return $variable; + } if ($type === 'environment') { $id = $resource->environment->id; } else if ($type === 'project') { diff --git a/app/Models/Project.php b/app/Models/Project.php index d5f1bdd54..b9afc7426 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -64,10 +64,13 @@ class Project extends BaseModel } public function mysqls() { - return $this->hasMany(StandaloneMysql::class, Environment::class); + return $this->hasManyThrough(StandaloneMysql::class, Environment::class); } public function mariadbs() { - return $this->hasMany(StandaloneMariadb::class, Environment::class); + return $this->hasManyThrough(StandaloneMariadb::class, Environment::class); + } + public function resource_count() { + return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count(); } } diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php index 380168005..9b16ebfed 100644 --- a/bootstrap/helpers/constants.php +++ b/bootstrap/helpers/constants.php @@ -34,3 +34,5 @@ const SUPPORTED_OS = [ 'centos fedora rhel ol rocky', 'sles opensuse-leap opensuse-tumbleweed' ]; + +const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment']; diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index 11cc5a3f8..16633168f 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -29,7 +29,7 @@ function generate_github_installation_token(GithubApp $source) 'Accept' => 'application/vnd.github.machine-man-preview+json' ])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens"); if ($token->failed()) { - throw new \Exception("Failed to get access token for " . $source->name . " with error: " . $token->json()['message']); + throw new RuntimeException("Failed to get access token for " . $source->name . " with error: " . $token->json()['message']); } return $token->json()['token']; } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index c3b6f9f01..2a6ce9647 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -937,7 +937,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'service_id' => $resource->id, ])->first(); ['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value); - if ($command->value() === 'FQDN' || $command->value() === 'URL') { + if ($command?->value() === 'FQDN' || $command?->value() === 'URL') { if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($resource->server, $containerName); } else { @@ -1357,7 +1357,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'application_id' => $resource->id, ])->first(); ['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value); - if ($command->value() === 'FQDN' || $command->value() === 'URL') { + if ($command?->value() === 'FQDN' || $command?->value() === 'URL') { if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($server, $containerName); } else { @@ -1670,15 +1670,21 @@ function ip_match($ip, $cidrs, &$match = null) function check_fqdn_usage(ServiceApplication|Application $own_resource) { $domains = collect($own_resource->fqdns)->map(function ($domain) { - return Url::fromString($domain)->getHost(); + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + return str($domain)->replace('http://', '')->replace('https://', ''); }); $apps = Application::all(); foreach ($apps as $app) { $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { - $naked_domain = Url::fromString($domain)->getHost(); + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->replace('http://', '')->replace('https://', '')->value(); if ($domains->contains($naked_domain)) { - if ($app->uuid !== $own_resource->uuid ) { + if ($app->uuid !== $own_resource->uuid) { throw new \RuntimeException("Domain $naked_domain is already in use by another resource."); } } @@ -1688,7 +1694,10 @@ function check_fqdn_usage(ServiceApplication|Application $own_resource) foreach ($apps as $app) { $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); foreach ($list_of_domains as $domain) { - $naked_domain = Url::fromString($domain)->getHost(); + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->replace('http://', '')->replace('https://', '')->value(); if ($domains->contains($naked_domain)) { if ($app->uuid !== $own_resource->uuid) { throw new \RuntimeException("Domain $naked_domain is already in use by another resource."); diff --git a/config/sentry.php b/config/sentry.php index 050405d5e..cb35fa610 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.203', + 'release' => '4.0.0-beta.204', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index c1ca7dc2b..0cbf5f9b7 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ 'Are you sure?', + 'buttonTitle' => 'Open Modal', + 'isErrorButton' => false, + 'disabled' => false, + 'action' => 'delete', +]) +
This destination will be deleted. It is not reversible.
Please think again.
This environment will be deleted. It is not reversible.
Please think again.
This project will be deleted. It is not reversible.
Please think again.