Refactor code for domain usage and validation

This commit is contained in:
Andras Bacsai 2024-04-08 12:15:44 +02:00
parent ddfded048c
commit 8bf0561009
5 changed files with 130 additions and 70 deletions

View File

@ -163,18 +163,16 @@ public function loadComposeFile($isInit = false)
} }
public function generateDomain(string $serviceName) public function generateDomain(string $serviceName)
{ {
$domain = $this->parsedServiceDomains[$serviceName]['domain'] ?? null; $uuid = new Cuid2(7);
if (!$domain) { $domain = generateFqdn($this->application->destination->server, $uuid);
$uuid = new Cuid2(7); $this->parsedServiceDomains[$serviceName]['domain'] = $domain;
$domain = generateFqdn($this->application->destination->server, $uuid); $this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
$this->parsedServiceDomains[$serviceName]['domain'] = $domain; $this->application->save();
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains); $this->dispatch('success', 'Domain generated.');
$this->application->save();
}
return $domain; return $domain;
} }
public function updatedApplicationBaseDirectory() { public function updatedApplicationBaseDirectory()
raY('asdf'); {
if ($this->application->build_pack === 'dockercompose') { if ($this->application->build_pack === 'dockercompose') {
$this->loadComposeFile(); $this->loadComposeFile();
} }
@ -206,30 +204,47 @@ public function getWildcardDomain()
$fqdn = generateFqdn($server, $this->application->uuid); $fqdn = generateFqdn($server, $this->application->uuid);
$this->application->fqdn = $fqdn; $this->application->fqdn = $fqdn;
$this->application->save(); $this->application->save();
$this->updatedApplicationFqdn(); $this->dispatch('success', 'Wildcard domain generated.');
} }
} }
public function resetDefaultLabels($showToaster = true) public function resetDefaultLabels()
{ {
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n"); $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
$this->ports_exposes = $this->application->ports_exposes; $this->ports_exposes = $this->application->ports_exposes;
$this->submit($showToaster);
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
} }
public function updatedApplicationFqdn() public function checkFqdns($showToaster = true)
{ {
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim(); if (data_get($this->application, 'fqdn')) {
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim(); $domains = str($this->application->fqdn)->trim()->explode(',');
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { if ($this->application->additional_servers->count() === 0) {
return str($domain)->trim()->lower(); foreach ($domains as $domain) {
}); if (!validate_dns_entry($domain, $this->application->destination->server)) {
$this->application->fqdn = $this->application->fqdn->unique()->implode(','); $showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
$this->application->save(); }
$this->resetDefaultLabels(false); }
}
check_domain_usage(resource: $this->application);
$this->application->fqdn = $domains->implode(',');
}
} }
public function submit($showToaster = true) public function submit($showToaster = true)
{ {
try { try {
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
return str($domain)->trim()->lower();
});
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
$this->checkFqdns();
$this->application->save();
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n"); $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
$this->application->custom_labels = base64_encode($this->customLabels); $this->application->custom_labels = base64_encode($this->customLabels);
@ -241,25 +256,14 @@ public function submit($showToaster = true)
} }
$this->validate(); $this->validate();
if ($this->ports_exposes !== $this->application->ports_exposes) { if ($this->ports_exposes !== $this->application->ports_exposes) {
$this->resetDefaultLabels(false); $this->resetDefaultLabels();
} }
if (data_get($this->application, 'build_pack') === 'dockerimage') { if (data_get($this->application, 'build_pack') === 'dockerimage') {
$this->validate([ $this->validate([
'application.docker_registry_image_name' => 'required', 'application.docker_registry_image_name' => 'required',
]); ]);
} }
if (data_get($this->application, 'fqdn')) {
$domains = str($this->application->fqdn)->trim()->explode(',');
if ($this->application->additional_servers->count() === 0) {
foreach ($domains as $domain) {
if (!validate_dns_entry($domain, $this->application->destination->server)) {
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
}
}
}
check_fqdn_usage($this->application);
$this->application->fqdn = $domains->implode(',');
}
if (data_get($this->application, 'custom_docker_run_options')) { if (data_get($this->application, 'custom_docker_run_options')) {
$this->application->custom_docker_run_options = str($this->application->custom_docker_run_options)->trim(); $this->application->custom_docker_run_options = str($this->application->custom_docker_run_options)->trim();
} }
@ -277,6 +281,15 @@ public function submit($showToaster = true)
} }
if ($this->application->build_pack === 'dockercompose') { if ($this->application->build_pack === 'dockercompose') {
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains); $this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
foreach ($this->parsedServiceDomains as $serviceName => $service) {
$domain = data_get($service, 'domain');
if ($domain) {
if (!validate_dns_entry($domain, $this->application->destination->server)) {
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
}
check_domain_usage(resource: $this->application);
}
}
if ($this->application->settings->is_raw_compose_deployment_enabled) { if ($this->application->settings->is_raw_compose_deployment_enabled) {
$this->application->parseRawCompose(); $this->application->parseRawCompose();
} else { } else {

View File

@ -65,7 +65,7 @@ public function mount()
public function submit() public function submit()
{ {
try { try {
check_fqdn_usage($this->application); check_domain_usage(resource: $this->application);
$this->validate(); $this->validate();
$this->application->save(); $this->application->save();
updateCompose($this->application); updateCompose($this->application);

View File

@ -59,23 +59,37 @@ public function instantSave()
public function submit() public function submit()
{ {
$this->resetErrorBag(); try {
if ($this->settings->public_port_min > $this->settings->public_port_max) { $error_show = false;
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.'); $this->server = Server::findOrFail(0);
return; $this->resetErrorBag();
if ($this->settings->public_port_min > $this->settings->public_port_max) {
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
return;
}
$this->validate();
if ($this->settings->is_dns_validation_enabled) {
if (!validate_dns_entry($this->settings->fqdn, $this->server)) {
$this->dispatch('error', "Validating DNS ({$this->settings->fqdn}) failed.<br><br>Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
$error_show = true;
}
}
check_domain_usage(domain: $this->settings->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->save();
$this->server->setupDynamicProxyConfiguration();
if (!$error_show) {
$this->dispatch('success', 'Instance settings updated successfully!');
}
} catch (\Exception $e) {
return handleError($e, $this);
} }
$this->validate();
$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->save();
$this->server = Server::findOrFail(0);
$this->server->setupDynamicProxyConfiguration();
$this->dispatch('success', 'Instance settings updated successfully!');
} }
} }

View File

@ -1857,13 +1857,26 @@ function ip_match($ip, $cidrs, &$match = null)
} }
return false; return false;
} }
function check_fqdn_usage(ServiceApplication|Application $own_resource) function check_domain_usage(ServiceApplication|Application|null $resource = null, ?string $domain = null)
{ {
$domains = collect($own_resource->fqdns)->map(function ($domain) { if ($resource) {
if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose') {
$domains = data_get(json_decode($resource->docker_compose_domains, true), "*.domain");
ray($domains);
$domains = collect($domains);
} else {
$domains = collect($resource->fqdns);
}
} else if ($domain) {
$domains = collect($domain);
} else {
throw new \RuntimeException("No resource or FQDN provided.");
}
$domains = $domains->map(function ($domain) {
if (str($domain)->endsWith('/')) { if (str($domain)->endsWith('/')) {
$domain = str($domain)->beforeLast('/'); $domain = str($domain)->beforeLast('/');
} }
return str($domain)->replace('http://', '')->replace('https://', ''); return str($domain);
}); });
$apps = Application::all(); $apps = Application::all();
foreach ($apps as $app) { foreach ($apps as $app) {
@ -1872,10 +1885,15 @@ function check_fqdn_usage(ServiceApplication|Application $own_resource)
if (str($domain)->endsWith('/')) { if (str($domain)->endsWith('/')) {
$domain = str($domain)->beforeLast('/'); $domain = str($domain)->beforeLast('/');
} }
$naked_domain = str($domain)->replace('http://', '')->replace('https://', '')->value(); $naked_domain = str($domain)->value();
if ($domains->contains($naked_domain)) { if ($domains->contains($naked_domain)) {
if ($app->uuid !== $own_resource->uuid) { if (data_get($resource, 'uuid')) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource:<br> {$app->name}."); ray($resource->uuid, $app->uuid);
if ($resource->uuid !== $app->uuid) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource called: <br><br>{$app->name}.");
}
} else if ($domain) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource called: <br><br>{$app->name}.");
} }
} }
} }
@ -1887,12 +1905,29 @@ function check_fqdn_usage(ServiceApplication|Application $own_resource)
if (str($domain)->endsWith('/')) { if (str($domain)->endsWith('/')) {
$domain = str($domain)->beforeLast('/'); $domain = str($domain)->beforeLast('/');
} }
$naked_domain = str($domain)->replace('http://', '')->replace('https://', '')->value(); $naked_domain = str($domain)->value();
if ($domains->contains($naked_domain)) { if ($domains->contains($naked_domain)) {
if ($app->uuid !== $own_resource->uuid) { if (data_get($resource, 'uuid')) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource."); if ($resource->uuid !== $app->uuid) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource called: <br><br>{$app->name}.");
}
} else if ($domain) {
throw new \RuntimeException("Domain $naked_domain is already in use by another resource called: <br><br>{$app->name}.");
} }
} }
} }
} }
if ($resource) {
$settings = InstanceSettings::get();
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.");
}
}
}
} }

View File

@ -59,10 +59,8 @@
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. " helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "
label="Domains for {{ str($serviceName)->headline() }}" label="Domains for {{ str($serviceName)->headline() }}"
id="parsedServiceDomains.{{ $serviceName }}.domain"></x-forms.input> id="parsedServiceDomains.{{ $serviceName }}.domain"></x-forms.input>
@if (!data_get($parsedServiceDomains, "$serviceName.domain")) <x-forms.button wire:click="generateDomain('{{ $serviceName }}')">Generate
<x-forms.button wire:click="generateDomain('{{ $serviceName }}')">Generate Domain</x-forms.button>
Domain</x-forms.button>
@endif
</div> </div>
@endif @endif
@endforeach @endforeach
@ -205,10 +203,10 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
</div> </div>
@if ($this->application->is_github_based() && !$this->application->is_public_repository()) @if ($this->application->is_github_based() && !$this->application->is_public_repository())
<div class="pb-4"> <div class="pb-4">
<x-forms.textarea helper="Gitignore-style rules to filter Git based webhook deployments." <x-forms.textarea helper="Gitignore-style rules to filter Git based webhook deployments."
placeholder="src/pages/**" id="application.watch_paths" label="Watch Paths" /> placeholder="src/pages/**" id="application.watch_paths" label="Watch Paths" />
</div> </div>
@endif @endif
<x-forms.input <x-forms.input
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>" helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"