feat(domains): support per-domain internal port overrides (#11594)

This commit is contained in:
Andras Bacsai
2026-09-02 19:39:19 +02:00
committed by GitHub
parent 7a7564e6b3
commit e2e91fbb85
70 changed files with 4025 additions and 297 deletions
+48
View File
@@ -7,6 +7,8 @@ use App\Services\ConfigurationGenerator;
use App\Services\DeploymentConfiguration\ApplicationConfigurationSnapshot;
use App\Services\DeploymentConfiguration\ConfigurationDiff;
use App\Services\DeploymentConfiguration\ConfigurationDiffer;
use App\Support\DomainPortOverrides;
use App\Support\DomainUrlParts;
use App\Traits\ClearsGlobalSearchCache;
use App\Traits\HasConfiguration;
use App\Traits\HasMetrics;
@@ -135,6 +137,7 @@ class Application extends BaseModel
'description',
'fqdn',
'noindex_domains',
'domain_port_overrides',
'git_repository',
'git_branch',
'git_commit_sha',
@@ -246,6 +249,7 @@ class Application extends BaseModel
'docker_compose_raw',
'custom_labels',
'domain_dns_statuses',
'domain_port_overrides',
];
protected function casts(): array
@@ -258,6 +262,7 @@ class Application extends BaseModel
'manual_webhook_secret_gitea' => 'encrypted',
'noindex_domains' => 'array',
'domain_dns_statuses' => 'array',
'domain_port_overrides' => 'array',
'restart_count' => 'integer',
'max_restart_count' => 'integer',
'restart_limit_reached' => 'boolean',
@@ -286,6 +291,9 @@ class Application extends BaseModel
if ($application->fqdn === '') {
$application->fqdn = null;
}
$normalized = DomainPortOverrides::normalize($application->fqdn, $application->domain_port_overrides);
$application->fqdn = $normalized['fqdn'];
$application->domain_port_overrides = $normalized['overrides'];
$payload['fqdn'] = $application->fqdn;
$application->syncNoindexDomains();
}
@@ -974,6 +982,46 @@ class Application extends BaseModel
return $this->settings->is_static ? [80] : $this->ports_exposes_array;
}
/**
* Ports the container is expected to listen on: Ports Exposes plus ports already used by application domains.
*
* @return list<int>
*/
public function availableInternalPorts(): array
{
$ports = collect($this->settings?->is_static ? [80] : $this->ports_exposes_array)
->filter(fn (mixed $port): bool => is_numeric($port) && (int) $port > 0)
->map(fn (mixed $port): int => (int) $port);
foreach ($this->domain_port_overrides ?? [] as $port) {
if (is_numeric($port) && (int) $port > 0) {
$ports->push((int) $port);
}
}
foreach (explode(',', (string) $this->fqdn) as $url) {
$url = trim($url);
if ($url === '') {
continue;
}
$legacyPort = DomainUrlParts::split($url)['port'] ?? '';
if ($legacyPort !== '' && is_numeric($legacyPort) && (int) $legacyPort > 0) {
$ports->push((int) $legacyPort);
}
}
return $ports->unique()->sort()->values()->all();
}
public function portRequiresConfirmation(?int $port): bool
{
if ($port === null || $port <= 0) {
return false;
}
return ! in_array($port, $this->availableInternalPorts(), true);
}
public function detectPortFromEnvironment(?bool $isPreview = false): ?int
{
$envVars = $isPreview