From 18ef5072a39779aa33a8ab9f7ccb86b2e01419db Mon Sep 17 00:00:00 2001 From: bluzername Date: Mon, 14 Sep 2026 07:22:02 +0300 Subject: [PATCH] Label an IPv6 public address as AAAA, not A, in the DNS dialogs Both BindDomainDialog and SandboxDomainCard hard-code the DNS record type to A, but the address they display come from detectPublicIp, which just take the first address in an ingress probe without checking if it is IPv4 or IPv6. When the only probed address is IPv6, the dialog show record type A next to a value that is not valid for an A record, so an IPv6-only operator get a copyable instruction that cannot work. Added dnsRecordType(ip) in publicIp.ts, it just check for a colon since every IPv6 textual form has one and no IPv4 dotted-quad does. Both dialogs now use it instead of the hard-coded 'A'. Test: publicIp.test.ts checks dnsRecordType on an IPv4 address, a compressed IPv6 address and a full-form IPv6 address. Without the function the test fail with "dnsRecordType is not a function", and after the fix all three pass. --- .../ingress/components/BindDomainDialog.tsx | 6 +++++- .../ingress/components/SandboxDomainCard.tsx | 6 +++++- .../src/features/ingress/lib/publicIp.test.ts | 18 ++++++++++++++++++ .../src/features/ingress/lib/publicIp.ts | 12 ++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 packages/console/src/features/ingress/lib/publicIp.test.ts diff --git a/packages/console/src/features/ingress/components/BindDomainDialog.tsx b/packages/console/src/features/ingress/components/BindDomainDialog.tsx index 894bcc98..3acc67aa 100644 --- a/packages/console/src/features/ingress/components/BindDomainDialog.tsx +++ b/packages/console/src/features/ingress/components/BindDomainDialog.tsx @@ -23,6 +23,7 @@ import { Input } from '@/components/ui/input'; import { Spinner } from '@/components/ui/spinner'; import { m } from '@/paraglide/messages'; import { useSetIngress } from '../hooks/useIngress'; +import { dnsRecordType } from '../lib/publicIp'; import { DnsRecordGuide } from './DnsRecordGuide'; /** @@ -91,7 +92,10 @@ export function BindDomainDialog({ { + it('labels an IPv4 address as an A record', () => { + expect(dnsRecordType('203.0.113.10')).toBe('A'); + }); + + it('labels an IPv6 address as an AAAA record, not A', () => { + expect(dnsRecordType('2001:db8::10')).toBe('AAAA'); + }); + + it('labels a full-form IPv6 address as AAAA too', () => { + expect(dnsRecordType('2001:0db8:0000:0000:0000:0000:0000:0010')).toBe( + 'AAAA', + ); + }); +}); diff --git a/packages/console/src/features/ingress/lib/publicIp.ts b/packages/console/src/features/ingress/lib/publicIp.ts index f2246258..182a735f 100644 --- a/packages/console/src/features/ingress/lib/publicIp.ts +++ b/packages/console/src/features/ingress/lib/publicIp.ts @@ -18,3 +18,15 @@ export function detectPublicIp(statuses: IngressDomainStatus[]): string | null { ); return (here ?? ready)?.probe.dnsAddresses[0] ?? null; } + +/** + * detectPublicIp picks one address without keeping track of its family, so + * the DNS dialogs cannot just hard-code "A" as the record type: an + * IPv6-only probe would then tell the operator to create an A record for a + * value Node itself classifies as invalid for that record type. A bare + * colon check is enough here, every IPv6 textual form has one and no IPv4 + * dotted-quad ever does. + */ +export function dnsRecordType(ip: string): 'A' | 'AAAA' { + return ip.includes(':') ? 'AAAA' : 'A'; +}