fix(api): persist Docker Compose domain ports as overrides
Normalize Compose domains on create and update, retaining explicit ports in `domain_port_overrides` while storing port-free domain values. Preserve empty Compose FQDNs and cover both API flows with feature tests.
This commit is contained in:
parent
208f7720bc
commit
47c61feb84
3 changed files with 97 additions and 3 deletions
|
|
@ -27,6 +27,7 @@
|
|||
use App\Support\ValidationPatterns;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
|
@ -1455,7 +1456,9 @@ private function create_application(Request $request, $type)
|
|||
$request->offsetUnset('docker_compose_domains');
|
||||
}
|
||||
if ($dockerComposeDomainsJson->count() > 0) {
|
||||
[$dockerComposeDomainsJson, $domainPortOverrides] = $this->normalizeDockerComposeDomainPorts($dockerComposeDomainsJson);
|
||||
$application->docker_compose_domains = json_encode($dockerComposeDomainsJson);
|
||||
$application->domain_port_overrides = $domainPortOverrides;
|
||||
}
|
||||
$repository_url_parsed = Url::fromString($request->git_repository);
|
||||
$git_host = $repository_url_parsed->getHost();
|
||||
|
|
@ -1719,7 +1722,9 @@ private function create_application(Request $request, $type)
|
|||
$request->offsetUnset('docker_compose_domains');
|
||||
}
|
||||
if ($dockerComposeDomainsJson->count() > 0) {
|
||||
[$dockerComposeDomainsJson, $domainPortOverrides] = $this->normalizeDockerComposeDomainPorts($dockerComposeDomainsJson);
|
||||
$application->docker_compose_domains = json_encode($dockerComposeDomainsJson);
|
||||
$application->domain_port_overrides = $domainPortOverrides;
|
||||
}
|
||||
$application->fqdn = $fqdn;
|
||||
$application->git_repository = str($gitRepository)->trim()->toString();
|
||||
|
|
@ -1950,7 +1955,9 @@ private function create_application(Request $request, $type)
|
|||
$request->offsetUnset('docker_compose_domains');
|
||||
}
|
||||
if ($dockerComposeDomainsJson->count() > 0) {
|
||||
[$dockerComposeDomainsJson, $domainPortOverrides] = $this->normalizeDockerComposeDomainPorts($dockerComposeDomainsJson);
|
||||
$application->docker_compose_domains = json_encode($dockerComposeDomainsJson);
|
||||
$application->domain_port_overrides = $domainPortOverrides;
|
||||
}
|
||||
$application->fqdn = $fqdn;
|
||||
$application->private_key_id = $privateKey->id;
|
||||
|
|
@ -3369,7 +3376,12 @@ public function update_by_uuid(Request $request)
|
|||
}
|
||||
|
||||
if ($dockerComposeDomainsJson->count() > 0) {
|
||||
[$dockerComposeDomainsJson, $domainPortOverrides] = $this->normalizeDockerComposeDomainPorts(
|
||||
$dockerComposeDomainsJson,
|
||||
$application->domain_port_overrides,
|
||||
);
|
||||
data_set($data, 'docker_compose_domains', json_encode($dockerComposeDomainsJson));
|
||||
data_set($data, 'domain_port_overrides', $domainPortOverrides);
|
||||
}
|
||||
$requestHasNoindexDomains = $request->has('noindex_domains');
|
||||
data_forget($data, 'noindex_domains');
|
||||
|
|
@ -6116,4 +6128,28 @@ public function remove_destination(Request $request): JsonResponse
|
|||
|
||||
return response()->json(['message' => 'Destination detached.']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<string, array{domain: ?string, redirect?: string}> $domains
|
||||
* @param array<string, int|string>|null $existingOverrides
|
||||
* @return array{Collection<string, array{domain: ?string, redirect?: string}>, ?array<string, int>}
|
||||
*/
|
||||
private function normalizeDockerComposeDomainPorts(Collection $domains, ?array $existingOverrides = null): array
|
||||
{
|
||||
$allDomains = $domains
|
||||
->pluck('domain')
|
||||
->filter()
|
||||
->implode(',');
|
||||
$normalized = DomainPortOverrides::normalize($allDomains, $existingOverrides);
|
||||
|
||||
$domains = $domains->map(function (array $entry): array {
|
||||
$entry['domain'] = collect(ValidationPatterns::applicationDomainList($entry['domain'] ?? null))
|
||||
->map(fn (string $domain): string => DomainPortOverrides::withoutPort($domain))
|
||||
->implode(',');
|
||||
|
||||
return $entry;
|
||||
});
|
||||
|
||||
return [$domains, $normalized['overrides']];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Enums\BuildPackTypes;
|
||||
use App\Services\ConfigurationGenerator;
|
||||
use App\Services\DeploymentConfiguration\ApplicationConfigurationSnapshot;
|
||||
use App\Services\DeploymentConfiguration\ConfigurationDiff;
|
||||
|
|
@ -291,9 +292,11 @@ protected static function booted()
|
|||
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'];
|
||||
if ($application->build_pack !== BuildPackTypes::DOCKERCOMPOSE->value || filled($application->fqdn)) {
|
||||
$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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,6 +165,61 @@ function recommendedApplicationSettingsPayload(): array
|
|||
->and($labels)->not->toContain('loadbalancer.server.port=3000');
|
||||
});
|
||||
|
||||
test('compose domain ports are stored as overrides when updating through the API', function () {
|
||||
$this->application->update([
|
||||
'build_pack' => 'dockercompose',
|
||||
'docker_compose_raw' => "services:\n api:\n image: nginx\n frontend:\n image: nginx\n",
|
||||
'docker_compose_domains' => json_encode([
|
||||
'api' => ['domain' => 'https://api.example.com'],
|
||||
'frontend' => ['domain' => 'https://app.example.com'],
|
||||
]),
|
||||
]);
|
||||
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'docker_compose_domains' => [
|
||||
['name' => 'api', 'domain' => 'https://api.example.com'],
|
||||
['name' => 'frontend', 'domain' => 'https://app.example.com:80'],
|
||||
],
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
$application = $this->application->fresh();
|
||||
$domains = json_decode($application->docker_compose_domains, true);
|
||||
|
||||
expect(data_get($domains, 'frontend.domain'))->toBe('https://app.example.com')
|
||||
->and($application->domain_port_overrides)->toBe([
|
||||
'https://app.example.com' => 80,
|
||||
]);
|
||||
});
|
||||
|
||||
test('compose domain ports are stored as overrides when creating through the API', function () {
|
||||
Queue::fake();
|
||||
|
||||
$response = $this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->postJson('/api/v1/applications/public', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_uuid' => $this->environment->uuid,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'git_repository' => 'https://gitlab.com/coolify/compose-domain-port-test',
|
||||
'git_branch' => 'main',
|
||||
'build_pack' => 'dockercompose',
|
||||
'autogenerate_domain' => false,
|
||||
'docker_compose_domains' => [
|
||||
['name' => 'frontend', 'domain' => 'https://app.example.com:80'],
|
||||
],
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
|
||||
$domains = json_decode($application->docker_compose_domains, true);
|
||||
|
||||
expect(data_get($domains, 'frontend.domain'))->toBe('https://app.example.com')
|
||||
->and($application->domain_port_overrides)->toBe([
|
||||
'https://app.example.com' => 80,
|
||||
]);
|
||||
});
|
||||
|
||||
test('http basic auth updates regenerate managed labels', function () {
|
||||
$this->application->settings->update(['is_container_label_readonly_enabled' => true]);
|
||||
$this->application->update([
|
||||
|
|
|
|||
Loading…
Reference in a new issue