diff --git a/app/Http/Middleware/CanAccessTerminal.php b/app/Http/Middleware/CanAccessTerminal.php index dcccd819b..348f389ea 100644 --- a/app/Http/Middleware/CanAccessTerminal.php +++ b/app/Http/Middleware/CanAccessTerminal.php @@ -15,17 +15,15 @@ class CanAccessTerminal */ public function handle(Request $request, Closure $next): Response { + if (! auth()->check()) { + abort(401, 'Authentication required'); + } + + // Only admins/owners can access terminal functionality + if (! auth()->user()->can('canAccessTerminal')) { + abort(403, 'Access to terminal functionality is restricted to team administrators'); + } + return $next($request); - - // if (! auth()->check()) { - // abort(401, 'Authentication required'); - // } - - // // Only admins/owners can access terminal functionality - // if (! auth()->user()->can('canAccessTerminal')) { - // abort(403, 'Access to terminal functionality is restricted to team administrators'); - // } - - // return $next($request); } } diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 3107ef4cb..aa72b7c5f 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -51,9 +51,16 @@ class General extends Component public $parsedServiceDomains = []; + public $domainConflicts = []; + + public $showDomainConflictModal = false; + + public $forceSaveDomains = false; + protected $listeners = [ 'resetDefaultLabels', 'configurationChanged' => '$refresh', + 'confirmDomainUsage', ]; protected function rules(): array @@ -430,7 +437,7 @@ class General extends Component $server = data_get($this->application, 'destination.server'); if ($server) { - $fqdn = generateFqdn(server: $server, random: $this->application->uuid, parserVersion: $this->application->compose_parsing_version); + $fqdn = generateUrl(server: $server, random: $this->application->uuid); $this->application->fqdn = $fqdn; $this->application->save(); $this->resetDefaultLabels(); @@ -485,10 +492,33 @@ class General extends Component } } } - check_domain_usage(resource: $this->application); + + // Check for domain conflicts if not forcing save + if (! $this->forceSaveDomains) { + $result = checkDomainUsage(resource: $this->application); + if ($result['hasConflicts']) { + $this->domainConflicts = $result['conflicts']; + $this->showDomainConflictModal = true; + + return false; + } + } else { + // Reset the force flag after using it + $this->forceSaveDomains = false; + } + $this->application->fqdn = $domains->implode(','); $this->resetDefaultLabels(false); } + + return true; + } + + public function confirmDomainUsage() + { + $this->forceSaveDomains = true; + $this->showDomainConflictModal = false; + $this->submit(); } public function setRedirect() @@ -536,7 +566,9 @@ class General extends Component $this->application->parseHealthcheckFromDockerfile($this->application->dockerfile); } - $this->checkFqdns(); + if (! $this->checkFqdns()) { + return; // Stop if there are conflicts and user hasn't confirmed + } $this->application->save(); if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && ! $this->application->settings->is_container_label_readonly_enabled) { @@ -588,7 +620,20 @@ class General extends Component } } } - check_domain_usage(resource: $this->application); + // Check for domain conflicts if not forcing save + if (! $this->forceSaveDomains) { + $result = checkDomainUsage(resource: $this->application); + if ($result['hasConflicts']) { + $this->domainConflicts = $result['conflicts']; + $this->showDomainConflictModal = true; + + return; + } + } else { + // Reset the force flag after using it + $this->forceSaveDomains = false; + } + $this->application->save(); $this->resetDefaultLabels(); } diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index 9164c1475..ebfd84489 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -25,6 +25,14 @@ class Previews extends Component public int $rate_limit_remaining; + public $domainConflicts = []; + + public $showDomainConflictModal = false; + + public $forceSaveDomains = false; + + public $pendingPreviewId = null; + protected $rules = [ 'application.previews.*.fqdn' => 'string|nullable', ]; @@ -49,6 +57,16 @@ class Previews extends Component } } + public function confirmDomainUsage() + { + $this->forceSaveDomains = true; + $this->showDomainConflictModal = false; + if ($this->pendingPreviewId) { + $this->save_preview($this->pendingPreviewId); + $this->pendingPreviewId = null; + } + } + public function save_preview($preview_id) { try { @@ -63,7 +81,20 @@ class Previews extends Component $this->dispatch('error', 'Validating DNS failed.', "Make sure you have added the DNS records correctly.

$preview->fqdn->{$this->application->destination->server->ip}

Check this documentation for further help."); $success = false; } - check_domain_usage(resource: $this->application, domain: $preview->fqdn); + // Check for domain conflicts if not forcing save + if (! $this->forceSaveDomains) { + $result = checkDomainUsage(resource: $this->application, domain: $preview->fqdn); + if ($result['hasConflicts']) { + $this->domainConflicts = $result['conflicts']; + $this->showDomainConflictModal = true; + $this->pendingPreviewId = $preview_id; + + return; + } + } else { + // Reset the force flag after using it + $this->forceSaveDomains = false; + } } if (! $preview) { diff --git a/app/Livewire/Project/Application/PreviewsCompose.php b/app/Livewire/Project/Application/PreviewsCompose.php index 0317ba7e7..2632509ea 100644 --- a/app/Livewire/Project/Application/PreviewsCompose.php +++ b/app/Livewire/Project/Application/PreviewsCompose.php @@ -60,7 +60,7 @@ class PreviewsCompose extends Component $random = new Cuid2; // Generate a unique domain like main app services do - $generated_fqdn = generateFqdn(server: $server, random: $random, parserVersion: $this->preview->application->compose_parsing_version); + $generated_fqdn = generateUrl(server: $server, random: $random); $preview_fqdn = str_replace('{{random}}', $random, $template); $preview_fqdn = str_replace('{{domain}}', str($generated_fqdn)->after('://'), $preview_fqdn); diff --git a/app/Livewire/Project/CloneMe.php b/app/Livewire/Project/CloneMe.php index 3c8c9843d..be9de139f 100644 --- a/app/Livewire/Project/CloneMe.php +++ b/app/Livewire/Project/CloneMe.php @@ -133,7 +133,7 @@ class CloneMe extends Component $uuid = (string) new Cuid2; $url = $application->fqdn; if ($this->server->proxyType() !== 'NONE' && $applicationSettings->is_container_label_readonly_enabled === true) { - $url = generateFqdn(server: $this->server, random: $uuid, parserVersion: $application->compose_parsing_version); + $url = generateUrl(server: $this->server, random: $uuid); } $newApplication = $application->replicate([ diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php index 7d68ce068..dbb223de2 100644 --- a/app/Livewire/Project/New/DockerImage.php +++ b/app/Livewire/Project/New/DockerImage.php @@ -60,7 +60,7 @@ class DockerImage extends Component 'health_check_enabled' => false, ]); - $fqdn = generateFqdn($destination->server, $application->uuid); + $fqdn = generateUrl(server: $destination->server, random: $application->uuid); $application->update([ 'name' => 'docker-image-'.$application->uuid, 'fqdn' => $fqdn, diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index a7aaa94a4..0f496e6db 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -208,7 +208,7 @@ class GithubPrivateRepository extends Component $application['docker_compose_location'] = $this->docker_compose_location; $application['base_directory'] = $this->base_directory; } - $fqdn = generateFqdn($destination->server, $application->uuid); + $fqdn = generateUrl(server: $destination->server, random: $application->uuid); $application->fqdn = $fqdn; $application->name = generate_application_name($this->selected_repository_owner.'/'.$this->selected_repository_repo, $this->selected_branch_name, $application->uuid); diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index d76f7baaa..5ff8f9137 100644 --- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -194,7 +194,7 @@ class GithubPrivateRepositoryDeployKey extends Component $application->settings->is_static = $this->is_static; $application->settings->save(); - $fqdn = generateFqdn($destination->server, $application->uuid); + $fqdn = generateUrl(server: $destination->server, random: $application->uuid); $application->fqdn = $fqdn; $application->name = generate_random_name($application->uuid); $application->save(); diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 8de998a96..f5978aea1 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -373,7 +373,7 @@ class PublicGitRepository extends Component $application->settings->is_static = $this->isStatic; $application->settings->save(); - $fqdn = generateFqdn($destination->server, $application->uuid); + $fqdn = generateUrl(server: $destination->server, random: $application->uuid); $application->fqdn = $fqdn; $application->save(); if ($this->checkCoolifyConfig) { diff --git a/app/Livewire/Project/New/SimpleDockerfile.php b/app/Livewire/Project/New/SimpleDockerfile.php index ebc9878dc..9cc4fbbe2 100644 --- a/app/Livewire/Project/New/SimpleDockerfile.php +++ b/app/Livewire/Project/New/SimpleDockerfile.php @@ -68,7 +68,7 @@ CMD ["nginx", "-g", "daemon off;"] 'source_type' => GithubApp::class, ]); - $fqdn = generateFqdn($destination->server, $application->uuid); + $fqdn = generateUrl(server: $destination->server, random: $application->uuid); $application->update([ 'name' => 'dockerfile-'.$application->uuid, 'fqdn' => $fqdn, diff --git a/app/Livewire/Project/Service/EditDomain.php b/app/Livewire/Project/Service/EditDomain.php index b7f73159e..5ce170b99 100644 --- a/app/Livewire/Project/Service/EditDomain.php +++ b/app/Livewire/Project/Service/EditDomain.php @@ -12,6 +12,12 @@ class EditDomain extends Component public ServiceApplication $application; + public $domainConflicts = []; + + public $showDomainConflictModal = false; + + public $forceSaveDomains = false; + protected $rules = [ 'application.fqdn' => 'nullable', 'application.required_fqdn' => 'required|boolean', @@ -22,6 +28,13 @@ class EditDomain extends Component $this->application = ServiceApplication::find($this->applicationId); } + public function confirmDomainUsage() + { + $this->forceSaveDomains = true; + $this->showDomainConflictModal = false; + $this->submit(); + } + public function submit() { try { @@ -37,7 +50,20 @@ class EditDomain extends Component if ($warning) { $this->dispatch('warning', __('warning.sslipdomain')); } - check_domain_usage(resource: $this->application); + // Check for domain conflicts if not forcing save + if (! $this->forceSaveDomains) { + $result = checkDomainUsage(resource: $this->application); + if ($result['hasConflicts']) { + $this->domainConflicts = $result['conflicts']; + $this->showDomainConflictModal = true; + + return; + } + } else { + // Reset the force flag after using it + $this->forceSaveDomains = false; + } + $this->validate(); $this->application->save(); updateCompose($this->application); diff --git a/app/Livewire/Project/Service/ServiceApplicationView.php b/app/Livewire/Project/Service/ServiceApplicationView.php index 5e178374b..3ac12cfe9 100644 --- a/app/Livewire/Project/Service/ServiceApplicationView.php +++ b/app/Livewire/Project/Service/ServiceApplicationView.php @@ -23,6 +23,12 @@ class ServiceApplicationView extends Component public $delete_volumes = true; + public $domainConflicts = []; + + public $showDomainConflictModal = false; + + public $forceSaveDomains = false; + protected $rules = [ 'application.human_name' => 'nullable', 'application.description' => 'nullable', @@ -129,6 +135,13 @@ class ServiceApplicationView extends Component } } + public function confirmDomainUsage() + { + $this->forceSaveDomains = true; + $this->showDomainConflictModal = false; + $this->submit(); + } + public function submit() { try { @@ -145,7 +158,20 @@ class ServiceApplicationView extends Component if ($warning) { $this->dispatch('warning', __('warning.sslipdomain')); } - check_domain_usage(resource: $this->application); + // Check for domain conflicts if not forcing save + if (! $this->forceSaveDomains) { + $result = checkDomainUsage(resource: $this->application); + if ($result['hasConflicts']) { + $this->domainConflicts = $result['conflicts']; + $this->showDomainConflictModal = true; + + return; + } + } else { + // Reset the force flag after using it + $this->forceSaveDomains = false; + } + $this->validate(); $this->application->save(); updateCompose($this->application); diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php index 6833492a6..02062e1f7 100644 --- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php +++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php @@ -33,9 +33,6 @@ class ExecuteContainerCommand extends Component public function mount() { - if (! auth()->user()->isAdmin()) { - abort(403); - } $this->parameters = get_route_parameters(); $this->containers = collect(); $this->servers = collect(); diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php index c9b341eed..28a6380d5 100644 --- a/app/Livewire/Project/Shared/ResourceOperations.php +++ b/app/Livewire/Project/Shared/ResourceOperations.php @@ -66,7 +66,7 @@ class ResourceOperations extends Component $url = $this->resource->fqdn; if ($server->proxyType() !== 'NONE' && $applicationSettings->is_container_label_readonly_enabled === true) { - $url = generateFqdn(server: $server, random: $uuid, parserVersion: $this->resource->compose_parsing_version); + $url = generateUrl(server: $server, random: $uuid); } $new_resource = $this->resource->replicate([ diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php index bce343224..d05433082 100644 --- a/app/Livewire/Settings/Index.php +++ b/app/Livewire/Settings/Index.php @@ -35,6 +35,12 @@ class Index extends Component #[Validate('required|string|timezone')] public string $instance_timezone; + public array $domainConflicts = []; + + public bool $showDomainConflictModal = false; + + public bool $forceSaveDomains = false; + public function render() { return view('livewire.settings.index'); @@ -81,6 +87,13 @@ class Index extends Component } } + public function confirmDomainUsage() + { + $this->forceSaveDomains = true; + $this->showDomainConflictModal = false; + $this->submit(); + } + public function submit() { try { @@ -108,7 +121,18 @@ class Index extends Component } } if ($this->fqdn) { - check_domain_usage(domain: $this->fqdn); + if (! $this->forceSaveDomains) { + $result = checkDomainUsage(domain: $this->fqdn); + if ($result['hasConflicts']) { + $this->domainConflicts = $result['conflicts']; + $this->showDomainConflictModal = true; + + return; + } + } else { + // Reset the force flag after using it + $this->forceSaveDomains = false; + } } $this->instantSave(isSave: false); diff --git a/app/Models/ApplicationPreview.php b/app/Models/ApplicationPreview.php index aa31268f1..721b22216 100644 --- a/app/Models/ApplicationPreview.php +++ b/app/Models/ApplicationPreview.php @@ -74,7 +74,7 @@ class ApplicationPreview extends BaseModel public function generate_preview_fqdn() { - if (empty($this->fqdn) && $this->application->fqdn) { + if ($this->application->fqdn) { if (str($this->application->fqdn)->contains(',')) { $url = Url::fromString(str($this->application->fqdn)->explode(',')[0]); $preview_fqdn = getFqdnWithoutPort(str($this->application->fqdn)->explode(',')[0]); diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index 5cc6b739f..6d2396a7d 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -28,7 +28,8 @@ class ServerPolicy */ public function create(User $user): bool { - return $user->isAdmin(); + // return $user->isAdmin(); + return true; } /** @@ -36,7 +37,8 @@ class ServerPolicy */ public function update(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + // return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + return true; } /** @@ -44,7 +46,8 @@ class ServerPolicy */ public function delete(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + // return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + return true; } /** @@ -68,7 +71,8 @@ class ServerPolicy */ public function manageProxy(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + // return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + return true; } /** @@ -76,7 +80,8 @@ class ServerPolicy */ public function manageSentinel(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + // return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + return true; } /** @@ -84,15 +89,8 @@ class ServerPolicy */ public function manageCaCertificate(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams->contains('id', $server->team_id); - } - - /** - * Determine whether the user can view terminal. - */ - public function viewTerminal(User $user, Server $server): bool - { - return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + // return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + return true; } /** @@ -100,6 +98,7 @@ class ServerPolicy */ public function viewSecurity(User $user, Server $server): bool { - return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + // return $user->isAdmin() && $user->teams->contains('id', $server->team_id); + return true; } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 3e76e6976..c017a580e 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -67,8 +67,7 @@ class AuthServiceProvider extends ServiceProvider // Register gate for terminal access Gate::define('canAccessTerminal', function ($user) { - // return $user->isAdmin() || $user->isOwner(); - return true; + return $user->isAdmin() || $user->isOwner(); }); } } diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index ac707f7ab..f61abc806 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -256,12 +256,12 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource) if (str($MINIO_BROWSER_REDIRECT_URL->value ?? '')->isEmpty()) { $MINIO_BROWSER_REDIRECT_URL->update([ - 'value' => generateFqdn(server: $server, random: 'console-'.$uuid, parserVersion: $resource->service->compose_parsing_version, forceHttps: true), + 'value' => generateUrl(server: $server, random: 'console-'.$uuid, forceHttps: true), ]); } if (str($MINIO_SERVER_URL->value ?? '')->isEmpty()) { $MINIO_SERVER_URL->update([ - 'value' => generateFqdn(server: $server, random: 'minio-'.$uuid, parserVersion: $resource->service->compose_parsing_version, forceHttps: true), + 'value' => generateUrl(server: $server, random: 'minio-'.$uuid, forceHttps: true), ]); } $payload = collect([ @@ -279,12 +279,12 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource) if (str($LOGTO_ENDPOINT->value ?? '')->isEmpty()) { $LOGTO_ENDPOINT->update([ - 'value' => generateFqdn(server: $server, random: 'logto-'.$uuid, parserVersion: $resource->service->compose_parsing_version), + 'value' => generateUrl(server: $server, random: 'logto-'.$uuid), ]); } if (str($LOGTO_ADMIN_ENDPOINT->value ?? '')->isEmpty()) { $LOGTO_ADMIN_ENDPOINT->update([ - 'value' => generateFqdn(server: $server, random: 'logto-admin-'.$uuid, parserVersion: $resource->service->compose_parsing_version), + 'value' => generateUrl(server: $server, random: 'logto-admin-'.$uuid), ]); } $payload = collect([ diff --git a/bootstrap/helpers/domains.php b/bootstrap/helpers/domains.php new file mode 100644 index 000000000..b562873b5 --- /dev/null +++ b/bootstrap/helpers/domains.php @@ -0,0 +1,139 @@ +team(); + } + + if ($resource) { + if ($resource->getMorphClass() === Application::class && $resource->build_pack === 'dockercompose') { + $domains = data_get(json_decode($resource->docker_compose_domains, true), '*.domain'); + $domains = collect($domains); + } else { + $domains = collect($resource->fqdns); + } + } elseif ($domain) { + $domains = collect([$domain]); + } else { + return ['conflicts' => [], 'hasConflicts' => false]; + } + + $domains = $domains->map(function ($domain) { + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + + return str($domain); + }); + + // Filter applications by team if we have a current team + $appsQuery = Application::query(); + if ($currentTeam) { + $appsQuery = $appsQuery->whereHas('environment.project', function ($query) use ($currentTeam) { + $query->where('team_id', $currentTeam->id); + }); + } + $apps = $appsQuery->get(); + foreach ($apps as $app) { + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + foreach ($list_of_domains as $domain) { + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->value(); + if ($domains->contains($naked_domain)) { + if (data_get($resource, 'uuid')) { + if ($resource->uuid !== $app->uuid) { + $conflicts[] = [ + 'domain' => $naked_domain, + 'resource_name' => $app->name, + 'resource_link' => $app->link(), + 'resource_type' => 'application', + 'message' => "Domain $naked_domain is already in use by application '{$app->name}'", + ]; + } + } elseif ($domain) { + $conflicts[] = [ + 'domain' => $naked_domain, + 'resource_name' => $app->name, + 'resource_link' => $app->link(), + 'resource_type' => 'application', + 'message' => "Domain $naked_domain is already in use by application '{$app->name}'", + ]; + } + } + } + } + + // Filter service applications by team if we have a current team + $serviceAppsQuery = ServiceApplication::query(); + if ($currentTeam) { + $serviceAppsQuery = $serviceAppsQuery->whereHas('service.environment.project', function ($query) use ($currentTeam) { + $query->where('team_id', $currentTeam->id); + }); + } + $apps = $serviceAppsQuery->get(); + foreach ($apps as $app) { + $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); + foreach ($list_of_domains as $domain) { + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->value(); + if ($domains->contains($naked_domain)) { + if (data_get($resource, 'uuid')) { + if ($resource->uuid !== $app->uuid) { + $conflicts[] = [ + 'domain' => $naked_domain, + 'resource_name' => $app->service->name, + 'resource_link' => $app->service->link(), + 'resource_type' => 'service', + 'message' => "Domain $naked_domain is already in use by service '{$app->service->name}'", + ]; + } + } elseif ($domain) { + $conflicts[] = [ + 'domain' => $naked_domain, + 'resource_name' => $app->service->name, + 'resource_link' => $app->service->link(), + 'resource_type' => 'service', + 'message' => "Domain $naked_domain is already in use by service '{$app->service->name}'", + ]; + } + } + } + } + + if ($resource) { + $settings = instanceSettings(); + if (data_get($settings, 'fqdn')) { + $domain = data_get($settings, 'fqdn'); + if (str($domain)->endsWith('/')) { + $domain = str($domain)->beforeLast('/'); + } + $naked_domain = str($domain)->value(); + if ($domains->contains($naked_domain)) { + $conflicts[] = [ + 'domain' => $naked_domain, + 'resource_name' => 'Coolify Instance', + 'resource_link' => '#', + 'resource_type' => 'instance', + 'message' => "Domain $naked_domain is already in use by this Coolify instance", + ]; + } + } + } + + return [ + 'conflicts' => $conflicts, + 'hasConflicts' => count($conflicts) > 0, + ]; +} diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 7a9b5df80..4743a811b 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1157,79 +1157,6 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId = } } } -function check_domain_usage(ServiceApplication|Application|null $resource = null, ?string $domain = null) -{ - if ($resource) { - if ($resource->getMorphClass() === \App\Models\Application::class && $resource->build_pack === 'dockercompose') { - $domains = data_get(json_decode($resource->docker_compose_domains, true), '*.domain'); - $domains = collect($domains); - } else { - $domains = collect($resource->fqdns); - } - } elseif ($domain) { - $domains = collect($domain); - } else { - throw new \RuntimeException('No resource or FQDN provided.'); - } - $domains = $domains->map(function ($domain) { - if (str($domain)->endsWith('/')) { - $domain = str($domain)->beforeLast('/'); - } - - return str($domain); - }); - $apps = Application::all(); - foreach ($apps as $app) { - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); - foreach ($list_of_domains as $domain) { - if (str($domain)->endsWith('/')) { - $domain = str($domain)->beforeLast('/'); - } - $naked_domain = str($domain)->value(); - if ($domains->contains($naked_domain)) { - if (data_get($resource, 'uuid')) { - if ($resource->uuid !== $app->uuid) { - throw new \RuntimeException("Domain $naked_domain is already in use by another resource:

Link: {$app->name}"); - } - } elseif ($domain) { - throw new \RuntimeException("Domain $naked_domain is already in use by another resource:

Link: {$app->name}"); - } - } - } - } - $apps = ServiceApplication::all(); - foreach ($apps as $app) { - $list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== ''); - foreach ($list_of_domains as $domain) { - if (str($domain)->endsWith('/')) { - $domain = str($domain)->beforeLast('/'); - } - $naked_domain = str($domain)->value(); - if ($domains->contains($naked_domain)) { - if (data_get($resource, 'uuid')) { - if ($resource->uuid !== $app->uuid) { - throw new \RuntimeException("Domain $naked_domain is already in use by another resource:

Link: {$app->service->name}"); - } - } elseif ($domain) { - throw new \RuntimeException("Domain $naked_domain is already in use by another resource:

Link: {$app->service->name}"); - } - } - } - } - if ($resource) { - $settings = instanceSettings(); - if (data_get($settings, 'fqdn')) { - $domain = data_get($settings, 'fqdn'); - if (str($domain)->endsWith('/')) { - $domain = str($domain)->beforeLast('/'); - } - $naked_domain = str($domain)->value(); - if ($domains->contains($naked_domain)) { - throw new \RuntimeException("Domain $naked_domain is already in use by this Coolify instance."); - } - } - } -} function parseCommandsByLineForSudo(Collection $commands, Server $server): array { diff --git a/config/constants.php b/config/constants.php index 770d9ba3f..44b51b978 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.424', + 'version' => '4.0.0-beta.425', 'helper_version' => '1.0.10', 'realtime_version' => '1.0.10', 'self_hosted' => env('SELF_HOSTED', true), diff --git a/resources/views/components/domain-conflict-modal.blade.php b/resources/views/components/domain-conflict-modal.blade.php new file mode 100644 index 000000000..218a7ef16 --- /dev/null +++ b/resources/views/components/domain-conflict-modal.blade.php @@ -0,0 +1,91 @@ +@props([ + 'conflicts' => [], + 'showModal' => false, + 'confirmAction' => 'confirmDomainUsage', +]) + +@if ($showModal && count($conflicts) > 0) +
+ +
+@endif diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php index b833fc7bb..315385593 100644 --- a/resources/views/livewire/project/application/general.blade.php +++ b/resources/views/livewire/project/application/general.blade.php @@ -462,6 +462,12 @@ + + + @script