fix(service): avoid storing inherited ports as overrides

This commit is contained in:
Andras Bacsai 2026-09-10 12:08:06 +02:00
parent 0134401f7c
commit 36befcfecd
4 changed files with 27 additions and 7 deletions

View file

@ -5,3 +5,7 @@ ## Alpine x-transition + tw-animate-css exit animations flash at the end
- Cause: `animate-out` keyframes default to `animation-fill-mode: none`. The element snaps back to its natural state when the keyframe ends. Alpine hides the element (display: none) only after its own timer (read from `transition-duration`), which starts ~2 rAF later than the animation. The gap shows the element at full opacity.
- Rule: every `x-transition:leave` that uses tw-animate-css `animate-out` MUST also include `fill-mode-forwards`.
- Rule: when a user reports UI flicker, check ALL layers of the animation stack (state reset timing, spinner flash, keyframe fill mode, focus restore) before you report the fix as complete. My first fix covered state reset and spinner only; the fill-mode snap was the visible one.
## Displayed defaults must not become stored overrides
- When an edit form shows an inherited or computed default, trace an unchanged save and a related-field edit through persistence.
- Preserve the inherited state when the displayed value still equals the computed default; store an override only when the user selects a different value.

1
.gitignore vendored
View file

@ -40,6 +40,7 @@ CHANGELOG.md
/.workspaces
/.superpowers/
/docs/superpowers/plans/
/.ai/todo.md
tests/Browser/Screenshots
tests/v4/Browser/Screenshots
ref

View file

@ -1180,11 +1180,9 @@ public function startEdit(int $index): void
$this->editingIndex = $index;
$this->editingDomain = $this->domainRows[$index]['url'];
$this->editingDomainParts = DomainUrlParts::split($this->editingDomain);
$app = $this->findServiceApp((int) $this->domainRows[$index]['service_application_id']);
$canonical = DomainPortOverrides::withoutPort($this->editingDomain);
$savedPort = ($app?->domain_port_overrides ?? [])[$canonical] ?? null;
if (filled($savedPort)) {
$this->editingDomainParts['port'] = (string) $savedPort;
$internalPort = $this->domainRows[$index]['internal_port'] ?? null;
if (filled($internalPort)) {
$this->editingDomainParts['port'] = (string) $internalPort;
}
$this->editingDomainPartsChanged = false;
$this->editingServiceApplicationId = (int) $this->domainRows[$index]['service_application_id'];
@ -1219,8 +1217,16 @@ public function updateDomain(): void
return;
}
if ($this->editingDomainPartsChanged || filled($this->editingDomainParts['host'] ?? null)) {
$this->editingDomain = DomainUrlParts::compose(...$this->editingDomainParts);
$editingDomainParts = $this->editingDomainParts;
$editingRow = $this->domainRows[$this->editingIndex];
if (
! ($editingRow['has_port_override'] ?? false)
&& (string) ($editingDomainParts['port'] ?? '') === (string) ($editingRow['internal_port'] ?? '')
) {
$editingDomainParts['port'] = '';
}
if ($this->editingDomainPartsChanged || filled($editingDomainParts['host'] ?? null)) {
$this->editingDomain = DomainUrlParts::compose(...$editingDomainParts);
}
$this->validateOnly('editingDomain');

View file

@ -907,9 +907,18 @@
Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])])
->assertSet('domainRows.0.internal_port', 3000)
->assertSet('domainRows.0.has_port_override', false)
->set('dnsValidationEnabled', false)
->call('startEdit', 0)
->assertSet('editingDomainParts.port', '3000')
->call('updateDomain')
->assertHasNoErrors()
->assertSee('Internal port 3000')
->assertSee('Inherited from the Coolify service port', false)
->assertDontSee('No internal port');
expect($this->apiApp->fresh())
->fqdn->toBe('https://api.example.com')
->domain_port_overrides->toBeNull();
});
it('shows a custom internal port badge for a service domain override', function () {