Prefill DKIM selectors so Email DNS Update works.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-18 14:45:09 +03:30
co-authored by Cursor
parent 8630465d34
commit e020c74fb1
@@ -14,28 +14,27 @@ interface EmailDnsModalProps {
onClose: () => void onClose: () => void
} }
const DEFAULT_ED25519_SELECTOR = 'v1-ed25519-20260901'
const DEFAULT_RSA_SELECTOR = 'v1-rsa-20260901'
function pasteIncludesDomainkey(paste: string) { function pasteIncludesDomainkey(paste: string) {
return /[a-z0-9][a-z0-9-]*\._domainkey/i.test(paste) return /[a-z0-9][a-z0-9-]*\._domainkey/i.test(paste)
} }
function keyReady(paste: string, selector: string) { /** Prefer typed selector; otherwise default when the paste has no …._domainkey. */
const trimmed = paste.trim() function resolveSelector(paste: string, selector: string, fallback: string) {
if (!trimmed) return false const typed = selector.trim()
return pasteIncludesDomainkey(trimmed) || Boolean(selector.trim()) if (typed) return typed
} if (pasteIncludesDomainkey(paste)) return ''
return fallback
/** Empty = skip Ed25519; otherwise same rules as RSA. */
function optionalKeyReady(paste: string, selector: string) {
if (!paste.trim()) return true
return keyReady(paste, selector)
} }
export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
const { showToast } = useToast() const { showToast } = useToast()
const [dnsProvider, setDnsProvider] = useState<ParkedDnsProvider>('arvan') const [dnsProvider, setDnsProvider] = useState<ParkedDnsProvider>('arvan')
const [ed25519Selector, setEd25519Selector] = useState('') const [ed25519Selector, setEd25519Selector] = useState(DEFAULT_ED25519_SELECTOR)
const [ed25519Key, setEd25519Key] = useState('') const [ed25519Key, setEd25519Key] = useState('')
const [rsaSelector, setRsaSelector] = useState('') const [rsaSelector, setRsaSelector] = useState(DEFAULT_RSA_SELECTOR)
const [rsaKey, setRsaKey] = useState('') const [rsaKey, setRsaKey] = useState('')
const [submitting, setSubmitting] = useState(false) const [submitting, setSubmitting] = useState(false)
const [error, setError] = useState('') const [error, setError] = useState('')
@@ -43,9 +42,9 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
useEffect(() => { useEffect(() => {
if (!domain) return if (!domain) return
setDnsProvider('arvan') setDnsProvider('arvan')
setEd25519Selector('') setEd25519Selector(DEFAULT_ED25519_SELECTOR)
setEd25519Key('') setEd25519Key('')
setRsaSelector('') setRsaSelector(DEFAULT_RSA_SELECTOR)
setRsaKey('') setRsaKey('')
setError('') setError('')
setSubmitting(false) setSubmitting(false)
@@ -53,23 +52,50 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
if (!domain) return null if (!domain) return null
const target = domain const target = domain
const canSubmit = const canSubmit = Boolean(rsaKey.trim())
keyReady(rsaKey, rsaSelector) &&
optionalKeyReady(ed25519Key, ed25519Selector)
async function submit() { 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) setSubmitting(true)
setError('') setError('')
try { try {
const ed25519Paste = ed25519Key.trim()
const result = await applyMailDns(target.id, { const result = await applyMailDns(target.id, {
dnsProvider, dnsProvider,
rsaKey, rsaKey: rsaPaste,
...(resolvedRsaSelector ? { rsaSelector: resolvedRsaSelector } : {}),
...(ed25519Paste ? { ed25519Key: ed25519Paste } : {}), ...(ed25519Paste ? { ed25519Key: ed25519Paste } : {}),
...(ed25519Paste && ed25519Selector.trim() ...(ed25519Paste && resolvedEd25519Selector
? { ed25519Selector: ed25519Selector.trim() } ? { ed25519Selector: resolvedEd25519Selector }
: {}), : {}),
...(rsaSelector.trim() ? { rsaSelector: rsaSelector.trim() } : {}),
}) })
showToast(result.message, 'success') showToast(result.message, 'success')
onClose() onClose()
@@ -95,14 +121,14 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
<p className={parkedStyles.hint}> <p className={parkedStyles.hint}>
Paste the RSA DKIM <strong>TXT value</strong> (<code>v=DKIM1; …</code>) from Paste the RSA DKIM <strong>TXT value</strong> (<code>v=DKIM1; …</code>) from
Stalwart, or the full zone line. Ed25519 is optional — leave it empty to Stalwart, or the full zone line. Ed25519 is optional — leave it empty to
skip. For value-only pastes, fill the selector (for example{' '} skip. Selectors default to <code>{DEFAULT_RSA_SELECTOR}</code> /{' '}
<code>v1-rsa-20260901</code>). We upsert common mail records on{' '} <code>{DEFAULT_ED25519_SELECTOR}</code> when empty. We upsert common mail
{dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'} (DNS-only / not records on {dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'}{' '}
proxied): <strong>mail</strong> A → 185.214.101.41, MX → mail.meshkee.com (DNS-only / not proxied): <strong>mail</strong> A → 185.214.101.41, MX →
(priority 10), SPF <code>v=spf1 mx -all</code>, DKIM (RSA required, mail.meshkee.com (priority 10), SPF <code>v=spf1 mx -all</code>, DKIM
Ed25519 if provided), autoconfig / autodiscover → mail.meshkee.com. Extra (RSA required, Ed25519 if provided), autoconfig / autodiscover →
MX, <strong>dkim._domainkey</strong>, and <strong>_dmarc</strong> are mail.meshkee.com. Extra MX, <strong>dkim._domainkey</strong>, and{' '}
removed. <strong>_dmarc</strong> are removed.
</p> </p>
{error ? ( {error ? (
<p className={tableStyles.alertError} role="alert"> <p className={tableStyles.alertError} role="alert">
@@ -134,14 +160,14 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
type="text" type="text"
value={ed25519Selector} value={ed25519Selector}
onChange={(e) => setEd25519Selector(e.target.value)} onChange={(e) => setEd25519Selector(e.target.value)}
placeholder="v1-ed25519-20260901" placeholder={DEFAULT_ED25519_SELECTOR}
disabled={submitting} disabled={submitting}
spellCheck={false} spellCheck={false}
dir="ltr" dir="ltr"
autoComplete="off" autoComplete="off"
/> />
<p className={tableStyles.helperText}> <p className={tableStyles.helperText}>
Only needed with an Ed25519 paste that has no <code>…._domainkey</code>. Auto-filled when empty. Change it if Stalwart uses a different selector.
</p> </p>
</div> </div>
<div className={tableStyles.field}> <div className={tableStyles.field}>
@@ -170,15 +196,14 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
type="text" type="text"
value={rsaSelector} value={rsaSelector}
onChange={(e) => setRsaSelector(e.target.value)} onChange={(e) => setRsaSelector(e.target.value)}
placeholder="v1-rsa-20260901" placeholder={DEFAULT_RSA_SELECTOR}
disabled={submitting} disabled={submitting}
spellCheck={false} spellCheck={false}
dir="ltr" dir="ltr"
autoComplete="off" autoComplete="off"
/> />
<p className={tableStyles.helperText}> <p className={tableStyles.helperText}>
Needed when pasting only the TXT value. Optional if the paste includes{' '} Auto-filled when empty. Change it if Stalwart uses a different selector.
<code>…._domainkey</code>.
</p> </p>
</div> </div>
<div className={tableStyles.field}> <div className={tableStyles.field}>