feat(domains): surface autodiscovery SRV records (RFC 6186)
ci / changes (push) Successful in 4s
ci / tc_booking (push) Has been skipped
ci / tc_operator (push) Has been skipped
ci / tc_website (push) Has been skipped
ci / tc_platform_api (push) Successful in 20s
ci / tc_portal (push) Failing after 27s
ci / build_booking (push) Has been skipped
ci / build_operator (push) Has been skipped
ci / build_portal (push) Has been skipped
ci / test_platform_api (push) Successful in 33s
ci / build_platform_api (push) Successful in 15s
ci / deploy (push) Failing after 3m5s
ci / changes (push) Successful in 4s
ci / tc_booking (push) Has been skipped
ci / tc_operator (push) Has been skipped
ci / tc_website (push) Has been skipped
ci / tc_platform_api (push) Successful in 20s
ci / tc_portal (push) Failing after 27s
ci / build_booking (push) Has been skipped
ci / build_operator (push) Has been skipped
ci / build_portal (push) Has been skipped
ci / test_platform_api (push) Successful in 33s
ci / build_platform_api (push) Successful in 15s
ci / deploy (push) Failing after 3m5s
Mail clients could never autoconfigure: Stalwart's zone file contains the _imaps/_submissions/_pop3s SRV records but classify() dropped everything except mx/spf/dkim/dmarc, so customers never saw them and every client needed manual server entry. New 'autodiscovery' record kind: classified from the zone (only the services actually reachable in prod — the _jmap/_caldavs SRVs target :443 which Traefik owns, deferred to the webmail story), verified via resolveSrv (missing=bad, wrong target=warn), shown as an OPTIONAL slot on the portal Domains page that never gates the domain status or the records-to-fix nag. Also fixed on the live server via management JMAP (x:SystemSettings): hostname was the machine name node1.dezky.eu from the v0.16 auto-bootstrap — MX/SRV targets and the SMTP banner now say mail.dezky.eu, and the LE x:Certificate is set as defaultCertificateId.
This commit is contained in:
@@ -14,7 +14,7 @@ const toast = useToast()
|
||||
const { domains, refresh, recheck, remove } = useDomains()
|
||||
|
||||
type Tone = 'ok' | 'warn' | 'bad'
|
||||
type RecordKey = 'mx' | 'spf' | 'dkim' | 'dmarc'
|
||||
type RecordKey = 'mx' | 'spf' | 'dkim' | 'dmarc' | 'autodiscovery'
|
||||
|
||||
// Static explanatory copy per record + status. Record VALUES are no longer here
|
||||
// — those come from the server (the real MX host, DKIM public key, etc.). We
|
||||
@@ -64,9 +64,23 @@ const DNS_FIX: Record<RecordKey, {
|
||||
pending: { headline: 'Not checked yet', body: 'Add the record below, then re-check.' },
|
||||
},
|
||||
},
|
||||
autodiscovery: {
|
||||
label: 'SRV · autodiscovery',
|
||||
purpose: 'Lets mail apps set themselves up from just the email address — no manual server fields.',
|
||||
states: {
|
||||
ok: { headline: 'Autodiscovery active', body: 'Mail clients can configure IMAP/SMTP automatically.' },
|
||||
warn: { headline: 'SRV points elsewhere', body: 'An SRV record exists but targets the wrong host or port — clients may try a dead endpoint. Update it to the values below.' },
|
||||
bad: { headline: 'No SRV records', body: 'Optional but recommended: without these, users type the mail server manually. Add the records below.' },
|
||||
pending: { headline: 'Not checked yet', body: 'Add the records below, then re-check.' },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const RECORD_KEYS: RecordKey[] = ['mx', 'spf', 'dkim', 'dmarc']
|
||||
const RECORD_KEYS: RecordKey[] = ['mx', 'spf', 'dkim', 'dmarc', 'autodiscovery']
|
||||
|
||||
// Required kinds drive the "X records to fix" nag; autodiscovery is optional
|
||||
// (clients fall back to manual server entry) so it never counts as an issue.
|
||||
const REQUIRED_KEYS: RecordKey[] = ['mx', 'spf', 'dkim', 'dmarc']
|
||||
|
||||
const expanded = reactive<Record<string, RecordKey | null>>({})
|
||||
const copied = ref<string | null>(null)
|
||||
@@ -89,7 +103,7 @@ function copyValue(text: string) {
|
||||
}
|
||||
|
||||
function issuesFor(d: DomainView): RecordKey[] {
|
||||
return RECORD_KEYS.filter((k) => d.checks[k] !== 'ok')
|
||||
return REQUIRED_KEYS.filter((k) => d.checks[k] !== 'ok')
|
||||
}
|
||||
function recordsOfKind(d: DomainView, k: RecordKey): DomainRecordView[] {
|
||||
return d.records.filter((r) => r.kind === k)
|
||||
|
||||
Reference in New Issue
Block a user