feat(domains): implement domain conflict detection and user confirmation modal across application components

This commit is contained in:
Andras Bacsai
2025-08-28 10:52:41 +02:00
parent 643343785a
commit 80499a03d8
14 changed files with 390 additions and 26 deletions

View File

@@ -5,6 +5,14 @@ use App\Models\ServiceApplication;
function checkDomainUsage(ServiceApplication|Application|null $resource = null, ?string $domain = null)
{
$conflicts = [];
// Get the current team for filtering
$currentTeam = null;
if ($resource) {
$currentTeam = $resource->team();
}
if ($resource) {
if ($resource->getMorphClass() === Application::class && $resource->build_pack === 'dockercompose') {
$domains = data_get(json_decode($resource->docker_compose_domains, true), '*.domain');
@@ -15,8 +23,9 @@ function checkDomainUsage(ServiceApplication|Application|null $resource = null,
} elseif ($domain) {
$domains = collect([$domain]);
} else {
throw new \RuntimeException('No resource or FQDN provided.');
return ['conflicts' => [], 'hasConflicts' => false];
}
$domains = $domains->map(function ($domain) {
if (str($domain)->endsWith('/')) {
$domain = str($domain)->beforeLast('/');
@@ -24,7 +33,15 @@ function checkDomainUsage(ServiceApplication|Application|null $resource = null,
return str($domain);
});
$apps = Application::all();
// 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) {
@@ -35,15 +52,35 @@ function checkDomainUsage(ServiceApplication|Application|null $resource = null,
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: <br><br>Link: <a class='underline' target='_blank' href='{$app->link()}'>{$app->name}</a>");
$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) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource: <br><br>Link: <a class='underline' target='_blank' href='{$app->link()}'>{$app->name}</a>");
$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}'",
];
}
}
}
}
$apps = ServiceApplication::all();
// 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) {
@@ -54,14 +91,27 @@ function checkDomainUsage(ServiceApplication|Application|null $resource = null,
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: <br><br>Link: <a class='underline' target='_blank' href='{$app->service->link()}'>{$app->service->name}</a>");
$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) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource: <br><br>Link: <a class='underline' target='_blank' href='{$app->service->link()}'>{$app->service->name}</a>");
$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')) {
@@ -71,8 +121,19 @@ function checkDomainUsage(ServiceApplication|Application|null $resource = null,
}
$naked_domain = str($domain)->value();
if ($domains->contains($naked_domain)) {
throw new \RuntimeException("Domain $naked_domain is already in use by this Coolify instance.");
$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,
];
}