mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-14 14:17:18 +00:00
feat(domains): add per-domain noindex support
Flagged domains are served with X-Robots-Tag: noindex, nofollow via Traefik and Caddy routing labels, so an auto-generated technical domain can be excluded from indexing while the production domain on the same resource stays indexable.
This commit is contained in:
1 parent
3da9942bf3
commit
4afe131905
20 files changed
+617
-23
No files matched your search
@@ -5,6 +5,7 @@ use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Support\ValidationPatterns;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Url\Url;
|
||||
@@ -387,7 +388,21 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
|
||||
return $payload;
|
||||
}
|
||||
|
||||
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null, bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null)
|
||||
/**
|
||||
* @param Collection<int, string>|null $noindex_domains
|
||||
*/
|
||||
function isNoindexDomain(string $domain, ?Collection $noindex_domains): bool
|
||||
{
|
||||
if (blank($noindex_domains)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $noindex_domains
|
||||
->map(fn (string $noindex_domain) => ValidationPatterns::normalizeApplicationDomainUrl($noindex_domain))
|
||||
->contains(ValidationPatterns::normalizeApplicationDomainUrl($domain));
|
||||
}
|
||||
|
||||
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null, bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null, ?Collection $noindex_domains = null)
|
||||
{
|
||||
$labels = collect([]);
|
||||
if ($serviceLabels) {
|
||||
@@ -419,7 +434,14 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
|
||||
$port = $predefinedPort;
|
||||
}
|
||||
$labels->push("caddy_{$loop}={$schema}://{$host}");
|
||||
$labels->push("caddy_{$loop}.header=-Server");
|
||||
if (isNoindexDomain($domain, $noindex_domains)) {
|
||||
// Caddy's header directive takes either inline arguments or a block,
|
||||
// never both, so -Server has to move into the block alongside it.
|
||||
$labels->push("caddy_{$loop}.header.0_-Server=");
|
||||
$labels->push("caddy_{$loop}.header.1_X-Robots-Tag=\"noindex, nofollow\"");
|
||||
} else {
|
||||
$labels->push("caddy_{$loop}.header=-Server");
|
||||
}
|
||||
$labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
|
||||
|
||||
if ($port) {
|
||||
@@ -445,7 +467,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
|
||||
return $labels->sort();
|
||||
}
|
||||
|
||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both', bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null)
|
||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both', bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null, ?Collection $noindex_domains = null)
|
||||
{
|
||||
$labels = collect([]);
|
||||
$labels->push('traefik.enable=true');
|
||||
@@ -518,6 +540,12 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels->push("caddy_{$loop}.handle_path.{$loop}_redir-ghost-{$uuid}.rewrite.replacement=/$1");
|
||||
}
|
||||
|
||||
$noindex_name = "{$loop}-{$uuid}-noindex";
|
||||
$is_noindex = isNoindexDomain($domain, $noindex_domains);
|
||||
if ($is_noindex) {
|
||||
$labels->push("traefik.http.middlewares.{$noindex_name}.headers.customresponseheaders.X-Robots-Tag=noindex, nofollow");
|
||||
}
|
||||
|
||||
$to_www_name = "{$loop}-{$uuid}-to-www";
|
||||
$to_non_www_name = "{$loop}-{$uuid}-to-non-www";
|
||||
$redirect_to_non_www = [
|
||||
@@ -562,6 +590,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
$middlewares->push($http_basic_auth_label);
|
||||
}
|
||||
if ($is_noindex) {
|
||||
$middlewares->push($noindex_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
@@ -588,6 +619,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
$middlewares->push($http_basic_auth_label);
|
||||
}
|
||||
if ($is_noindex) {
|
||||
$middlewares->push($noindex_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
@@ -606,8 +640,15 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port");
|
||||
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
|
||||
}
|
||||
$middlewares = collect([]);
|
||||
if ($is_noindex) {
|
||||
$middlewares->push($noindex_name);
|
||||
}
|
||||
if ($is_force_https_enabled) {
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https");
|
||||
$middlewares->push('redirect-to-https');
|
||||
}
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares->join(',')}");
|
||||
}
|
||||
} else {
|
||||
// Set labels for http
|
||||
@@ -640,6 +681,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
$middlewares->push($http_basic_auth_label);
|
||||
}
|
||||
if ($is_noindex) {
|
||||
$middlewares->push($noindex_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
@@ -666,6 +710,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
if ($is_http_basic_auth_enabled) {
|
||||
$middlewares->push($http_basic_auth_label);
|
||||
}
|
||||
if ($is_noindex) {
|
||||
$middlewares->push($noindex_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
@@ -698,6 +745,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
if ($pull_request_id === 0) {
|
||||
if ($application->fqdn) {
|
||||
$domains = str(data_get($application, 'fqdn'))->explode(',');
|
||||
$noindexDomains = $application->noindexDomains();
|
||||
$shouldGenerateLabelsExactly = $application->destination->server->settings->generate_exact_labels;
|
||||
if ($shouldGenerateLabelsExactly) {
|
||||
switch ($application->destination->server->proxyType()) {
|
||||
@@ -713,6 +761,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
break;
|
||||
case ProxyTypes::CADDY->value:
|
||||
@@ -728,6 +777,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
break;
|
||||
}
|
||||
@@ -743,6 +793,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
$labels = $labels->merge(fqdnLabelsForCaddy(
|
||||
network: $application->destination->network,
|
||||
@@ -756,6 +807,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -765,6 +817,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
} else {
|
||||
$domains = collect([]);
|
||||
}
|
||||
// Previews are ephemeral: every domain is noindex.
|
||||
$noindexDomains = $domains;
|
||||
$shouldGenerateLabelsExactly = $application->destination->server->settings->generate_exact_labels;
|
||||
if ($shouldGenerateLabelsExactly) {
|
||||
switch ($application->destination->server->proxyType()) {
|
||||
@@ -779,6 +833,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
break;
|
||||
case ProxyTypes::CADDY->value:
|
||||
@@ -793,6 +848,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
break;
|
||||
}
|
||||
@@ -807,6 +863,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
$labels = $labels->merge(fqdnLabelsForCaddy(
|
||||
network: $application->destination->network,
|
||||
@@ -819,6 +876,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
|
||||
http_basic_auth_username: $application->http_basic_auth_username,
|
||||
http_basic_auth_password: $application->http_basic_auth_password,
|
||||
noindex_domains: $noindexDomains,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user