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)
+
Warning: Domain Conflict Detected
+{{ $slot ?? 'The following domain(s) are already in use by other resources. Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.' }} +
+What will happen if you continue?
+ @if (isset($consequences)) + {{ $consequences }} + @else +