From f276f9573f2cadc1f2b0d8ed8535425f149e8cf6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:12:27 +0200 Subject: [PATCH] fix(previews): allow re-checking DNS over a stored completed result Starting a DNS check for a preview domain used to leave the stored completed status in place. That blocked the new 'checking' state from being saved. persistDnsStatuses now takes the status keys whose checks are being started and lets those keys overwrite what is stored. Add tests for re-checking DNS on application domains and preview domains that already have a completed result. --- .../Project/Application/PreviewDomains.php | 15 +++-- tests/Feature/ApplicationDomainsTest.php | 56 +++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/app/Livewire/Project/Application/PreviewDomains.php b/app/Livewire/Project/Application/PreviewDomains.php index 5f454d91ba..ee635f16b6 100644 --- a/app/Livewire/Project/Application/PreviewDomains.php +++ b/app/Livewire/Project/Application/PreviewDomains.php @@ -339,17 +339,18 @@ class PreviewDomains extends Component } $row = $this->domainRows[$index]; + $statusKey = $this->statusKey($row['url'], $row['service']); $checkId = new_public_id(); $this->domainRows[$index]['dns_status'] = 'checking'; $this->domainRows[$index]['dns_message'] = 'Checking DNS...'; $this->domainRows[$index]['check_id'] = $checkId; - $this->persistDnsStatuses(); + $this->persistDnsStatuses([$statusKey]); try { $server = $this->preview->application->destination?->server; CheckDomainDnsJob::dispatch( $this->preview, - $this->statusKey($row['url'], $row['service']), + $statusKey, $row['url'], $server, $server ? serverDnsTargetIp($server) ?? $server->ip : null, @@ -494,7 +495,11 @@ class PreviewDomains extends Component return true; } - private function persistDnsStatuses(): void + /** + * @param array $startingCheckKeys Status keys whose check is being started by this call. + * They are allowed to replace a stored completed result. + */ + private function persistDnsStatuses(array $startingCheckKeys = []): void { $statuses = []; foreach ($this->domainRows as $row) { @@ -505,13 +510,13 @@ class PreviewDomains extends Component ]; } - DB::transaction(function () use (&$statuses): void { + DB::transaction(function () use (&$statuses, $startingCheckKeys): void { $preview = ApplicationPreview::query()->lockForUpdate()->findOrFail($this->preview->id); $storedStatuses = $preview->domain_dns_statuses ?? []; foreach ($statuses as $key => $status) { $storedStatus = $storedStatuses[$key] ?? null; - if (! is_array($storedStatus)) { + if (! is_array($storedStatus) || in_array($key, $startingCheckKeys, true)) { continue; } diff --git a/tests/Feature/ApplicationDomainsTest.php b/tests/Feature/ApplicationDomainsTest.php index 183073366b..4bc05e018a 100644 --- a/tests/Feature/ApplicationDomainsTest.php +++ b/tests/Feature/ApplicationDomainsTest.php @@ -3598,3 +3598,59 @@ it('does not overwrite a completed dns result with stale checking state', functi 'check_id' => 'completed-check', ]); }); + +it('replaces a completed dns result when the domain is checked again', function () { + $settings = InstanceSettings::get(); + $settings->is_dns_validation_enabled = true; + $settings->save(); + + $url = 'https://this-domain-should-not-resolve-for-coolify-tests.invalid'; + $checkedAt = now()->subDays(12)->toIso8601String(); + $this->application->update([ + 'fqdn' => $url, + 'domain_dns_statuses' => [ + $url => [ + 'status' => 'ok', + 'message' => 'DNS looks correct.', + 'expected_ip' => '203.0.113.10', + 'checked_at' => $checkedAt, + ], + ], + ]); + + Livewire::test(Domains::class, ['application' => $this->application->fresh()]) + ->call('checkDomainDns', 0) + ->call('pollDnsChecks') + ->assertSet('domainRows.0.dns_status', 'failed'); + + expect($this->application->fresh()->domain_dns_statuses[$url]) + ->status->toBe('failed') + ->checked_at->not->toBe($checkedAt); +}); + +it('restarts a preview dns check when the domain already has a completed result', function () { + Queue::fake(); + + $url = 'https://preview-recheck.example.com'; + $statusKey = hash('sha256', $url.'|'); + $preview = ApplicationPreview::create([ + 'application_id' => $this->application->id, + 'pull_request_id' => 54, + 'pull_request_html_url' => 'https://github.com/coollabsio/coolify/pull/54', + 'fqdn' => $url, + 'domain_dns_statuses' => [ + $statusKey => [ + 'status' => 'failed', + 'message' => 'Required DNS record type A pointing to 203.0.113.10', + ], + ], + ]); + + Livewire::test(PreviewDomains::class, ['preview' => $preview]) + ->call('checkDomainDns', 0) + ->assertSet('domainRows.0.dns_status', 'checking'); + + expect($preview->fresh()->domain_dns_statuses[$statusKey]['status'])->toBe('checking'); + + Queue::assertPushed(CheckDomainDnsJob::class); +});