import { useEffect, useState } from 'react' import { Modal } from './Modal' import { DnsProviderSwitch } from './DnsProviderSwitch' import { ApiError } from '../lib/api' import { applyMailDns } from '../services/domainService' import type { DomainListItem, ParkedDnsProvider } from '../types/domain' import { useToast } from '../context/ToastContext' import tableStyles from '../pages/BusinessesPage.module.css' import parkedStyles from './ParkedDomainsModal.module.css' import styles from './EmailDnsModal.module.css' interface EmailDnsModalProps { domain: DomainListItem | null onClose: () => void } const DEFAULT_ED25519_SELECTOR = 'v1-ed25519-20260901' const DEFAULT_RSA_SELECTOR = 'v1-rsa-20260901' function pasteIncludesDomainkey(paste: string) { return /[a-z0-9][a-z0-9-]*\._domainkey/i.test(paste) } /** Prefer typed selector; otherwise default when the paste has no …._domainkey. */ function resolveSelector(paste: string, selector: string, fallback: string) { const typed = selector.trim() if (typed) return typed if (pasteIncludesDomainkey(paste)) return '' return fallback } export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { const { showToast } = useToast() const [dnsProvider, setDnsProvider] = useState('arvan') const [ed25519Selector, setEd25519Selector] = useState(DEFAULT_ED25519_SELECTOR) const [ed25519Key, setEd25519Key] = useState('') const [rsaSelector, setRsaSelector] = useState(DEFAULT_RSA_SELECTOR) const [rsaKey, setRsaKey] = useState('') const [submitting, setSubmitting] = useState(false) const [error, setError] = useState('') useEffect(() => { if (!domain) return setDnsProvider('arvan') setEd25519Selector(DEFAULT_ED25519_SELECTOR) setEd25519Key('') setRsaSelector(DEFAULT_RSA_SELECTOR) setRsaKey('') setError('') setSubmitting(false) }, [domain]) if (!domain) return null const target = domain const canSubmit = Boolean(rsaKey.trim()) async function submit() { if (!rsaKey.trim()) { const message = 'Paste the RSA DKIM TXT value from Stalwart.' setError(message) showToast(message, 'error') return } const ed25519Paste = ed25519Key.trim() const rsaPaste = rsaKey.trim() const resolvedRsaSelector = resolveSelector( rsaPaste, rsaSelector, DEFAULT_RSA_SELECTOR, ) const resolvedEd25519Selector = ed25519Paste ? resolveSelector( ed25519Paste, ed25519Selector, DEFAULT_ED25519_SELECTOR, ) : '' // Show resolved defaults in the fields if they were cleared. if (!rsaSelector.trim() && resolvedRsaSelector) { setRsaSelector(resolvedRsaSelector) } if (ed25519Paste && !ed25519Selector.trim() && resolvedEd25519Selector) { setEd25519Selector(resolvedEd25519Selector) } setSubmitting(true) setError('') try { const result = await applyMailDns(target.id, { dnsProvider, rsaKey: rsaPaste, ...(resolvedRsaSelector ? { rsaSelector: resolvedRsaSelector } : {}), ...(ed25519Paste ? { ed25519Key: ed25519Paste } : {}), ...(ed25519Paste && resolvedEd25519Selector ? { ed25519Selector: resolvedEd25519Selector } : {}), }) showToast(result.message, 'success') onClose() } catch (err) { const message = err instanceof ApiError ? err.message : 'Unable to update mail DNS.' setError(message) showToast(message, 'error') } finally { setSubmitting(false) } } return ( { if (!submitting) onClose() }} >

Paste the RSA DKIM TXT value (v=DKIM1; …) from Stalwart, or the full zone line. Ed25519 is optional — leave it empty to skip. Selectors default to {DEFAULT_RSA_SELECTOR} /{' '} {DEFAULT_ED25519_SELECTOR} when empty. We upsert common mail records on {dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'}{' '} (DNS-only / not proxied): mail A → 185.214.101.41, MX → mail.meshkee.com (priority 10), SPF v=spf1 mx -all, DKIM (RSA required, Ed25519 if provided), autoconfig / autodiscover → mail.meshkee.com. Extra MX, dkim._domainkey, and{' '} _dmarc are removed.

{error ? (

{error}

) : null} {submitting ? (

Updating {dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'} mail DNS…

) : null}
DNS host
setEd25519Selector(e.target.value)} placeholder={DEFAULT_ED25519_SELECTOR} disabled={submitting} spellCheck={false} dir="ltr" autoComplete="off" />

Auto-filled when empty. Change it if Stalwart uses a different selector.