diff --git a/apps/super-admin/src/components/EmailDnsModal.module.css b/apps/super-admin/src/components/EmailDnsModal.module.css index a912426..c32ec26 100644 --- a/apps/super-admin/src/components/EmailDnsModal.module.css +++ b/apps/super-admin/src/components/EmailDnsModal.module.css @@ -12,6 +12,30 @@ color: var(--text-primary); } +.selectorInput { + width: 100%; + min-height: var(--field-height); + padding: var(--field-padding-y) var(--field-padding-x); + font-size: var(--field-font-size); + font-family: var(--font-ui); + line-height: 1.4; + color: var(--text-primary); + background-color: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(148, 163, 184, 0.35); + border-radius: var(--radius-sm); + outline: none; + box-sizing: border-box; +} + +.selectorInput:focus { + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(var(--primary-rgb) / 0.12); +} + +.selectorInput:disabled { + opacity: 0.65; +} + .textarea { width: 100%; min-height: 110px; diff --git a/apps/super-admin/src/components/EmailDnsModal.tsx b/apps/super-admin/src/components/EmailDnsModal.tsx index e68d334..1161118 100644 --- a/apps/super-admin/src/components/EmailDnsModal.tsx +++ b/apps/super-admin/src/components/EmailDnsModal.tsx @@ -14,10 +14,22 @@ interface EmailDnsModalProps { onClose: () => void } +function pasteIncludesDomainkey(paste: string) { + return /[a-z0-9][a-z0-9-]*\._domainkey/i.test(paste) +} + +function keyReady(paste: string, selector: string) { + const trimmed = paste.trim() + if (!trimmed) return false + return pasteIncludesDomainkey(trimmed) || Boolean(selector.trim()) +} + export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { const { showToast } = useToast() const [dnsProvider, setDnsProvider] = useState('arvan') + const [ed25519Selector, setEd25519Selector] = useState('') const [ed25519Key, setEd25519Key] = useState('') + const [rsaSelector, setRsaSelector] = useState('') const [rsaKey, setRsaKey] = useState('') const [submitting, setSubmitting] = useState(false) const [error, setError] = useState('') @@ -25,7 +37,9 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { useEffect(() => { if (!domain) return setDnsProvider('arvan') + setEd25519Selector('') setEd25519Key('') + setRsaSelector('') setRsaKey('') setError('') setSubmitting(false) @@ -33,6 +47,8 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { if (!domain) return null const target = domain + const canSubmit = + keyReady(ed25519Key, ed25519Selector) && keyReady(rsaKey, rsaSelector) async function submit() { setSubmitting(true) @@ -42,6 +58,10 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { dnsProvider, ed25519Key, rsaKey, + ...(ed25519Selector.trim() + ? { ed25519Selector: ed25519Selector.trim() } + : {}), + ...(rsaSelector.trim() ? { rsaSelector: rsaSelector.trim() } : {}), }) showToast(result.message, 'success') onClose() @@ -65,8 +85,9 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { }} >

- Paste the two DKIM lines from Stalwart (Directory → Domains → ⋮ → View DNS - zone file). We upsert mail records on{' '} + Paste each DKIM TXT value (v=DKIM1; …) from + Stalwart, or the full zone line. For value-only pastes, fill the selector + (for example v1-ed25519-20260901). We upsert mail records on{' '} {dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'} (DNS-only / not proxied): mail A → 185.214.101.41, MX → mail.meshkee.com, SPF, both DKIM keys, autoconfig / autodiscover. Extra MX,{' '} @@ -92,6 +113,25 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) { /> +

+ + setEd25519Selector(e.target.value)} + placeholder="v1-ed25519-20260901" + disabled={submitting} + spellCheck={false} + dir="ltr" + autoComplete="off" + /> +

+ Needed when pasting only the TXT value. Optional if the paste includes{' '} + …._domainkey. +

+