|
|
|
@@ -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<ParkedDnsProvider>('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) {
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<p className={parkedStyles.hint}>
|
|
|
|
|
Paste the two DKIM lines from Stalwart (Directory → Domains → ⋮ → View DNS
|
|
|
|
|
zone file). We upsert mail records on{' '}
|
|
|
|
|
Paste each DKIM <strong>TXT value</strong> (<code>v=DKIM1; …</code>) from
|
|
|
|
|
Stalwart, or the full zone line. For value-only pastes, fill the selector
|
|
|
|
|
(for example <code>v1-ed25519-20260901</code>). We upsert mail records on{' '}
|
|
|
|
|
{dnsProvider === 'cloudflare' ? 'Cloudflare' : 'Arvan'} (DNS-only / not
|
|
|
|
|
proxied): <strong>mail</strong> 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) {
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className={tableStyles.field}>
|
|
|
|
|
<label htmlFor="mail-dkim-ed25519-selector">Ed25519 selector</label>
|
|
|
|
|
<input
|
|
|
|
|
id="mail-dkim-ed25519-selector"
|
|
|
|
|
className={styles.selectorInput}
|
|
|
|
|
type="text"
|
|
|
|
|
value={ed25519Selector}
|
|
|
|
|
onChange={(e) => setEd25519Selector(e.target.value)}
|
|
|
|
|
placeholder="v1-ed25519-20260901"
|
|
|
|
|
disabled={submitting}
|
|
|
|
|
spellCheck={false}
|
|
|
|
|
dir="ltr"
|
|
|
|
|
autoComplete="off"
|
|
|
|
|
/>
|
|
|
|
|
<p className={tableStyles.helperText}>
|
|
|
|
|
Needed when pasting only the TXT value. Optional if the paste includes{' '}
|
|
|
|
|
<code>…._domainkey</code>.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className={tableStyles.field}>
|
|
|
|
|
<label htmlFor="mail-dkim-ed25519">Ed25519 domain key</label>
|
|
|
|
|
<textarea
|
|
|
|
@@ -99,14 +139,33 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
|
|
|
|
|
className={styles.textarea}
|
|
|
|
|
value={ed25519Key}
|
|
|
|
|
onChange={(e) => setEd25519Key(e.target.value)}
|
|
|
|
|
placeholder="v1-ed25519-…. _domainkey IN TXT "v=DKIM1; k=ed25519; …""
|
|
|
|
|
placeholder='v=DKIM1; k=ed25519; h=sha256; p=…'
|
|
|
|
|
disabled={submitting}
|
|
|
|
|
spellCheck={false}
|
|
|
|
|
dir="ltr"
|
|
|
|
|
rows={5}
|
|
|
|
|
rows={4}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div style={{ height: 12 }} />
|
|
|
|
|
<div className={tableStyles.field}>
|
|
|
|
|
<label htmlFor="mail-dkim-rsa-selector">RSA selector</label>
|
|
|
|
|
<input
|
|
|
|
|
id="mail-dkim-rsa-selector"
|
|
|
|
|
className={styles.selectorInput}
|
|
|
|
|
type="text"
|
|
|
|
|
value={rsaSelector}
|
|
|
|
|
onChange={(e) => setRsaSelector(e.target.value)}
|
|
|
|
|
placeholder="v1-rsa-20260901"
|
|
|
|
|
disabled={submitting}
|
|
|
|
|
spellCheck={false}
|
|
|
|
|
dir="ltr"
|
|
|
|
|
autoComplete="off"
|
|
|
|
|
/>
|
|
|
|
|
<p className={tableStyles.helperText}>
|
|
|
|
|
Needed when pasting only the TXT value. Optional if the paste includes{' '}
|
|
|
|
|
<code>…._domainkey</code>.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className={tableStyles.field}>
|
|
|
|
|
<label htmlFor="mail-dkim-rsa">RSA domain key</label>
|
|
|
|
|
<textarea
|
|
|
|
@@ -114,11 +173,11 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
|
|
|
|
|
className={styles.textarea}
|
|
|
|
|
value={rsaKey}
|
|
|
|
|
onChange={(e) => setRsaKey(e.target.value)}
|
|
|
|
|
placeholder="v1-rsa-…. _domainkey IN TXT "v=DKIM1; k=rsa; …""
|
|
|
|
|
placeholder='v=DKIM1; k=rsa; h=sha256; p=…'
|
|
|
|
|
disabled={submitting}
|
|
|
|
|
spellCheck={false}
|
|
|
|
|
dir="ltr"
|
|
|
|
|
rows={7}
|
|
|
|
|
rows={6}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
@@ -136,7 +195,7 @@ export function EmailDnsModal({ domain, onClose }: EmailDnsModalProps) {
|
|
|
|
|
type="button"
|
|
|
|
|
className={`${tableStyles.btn} ${tableStyles.btnPrimary}`}
|
|
|
|
|
onClick={() => void submit()}
|
|
|
|
|
disabled={submitting || !ed25519Key.trim() || !rsaKey.trim()}
|
|
|
|
|
disabled={submitting || !canSubmit}
|
|
|
|
|
>
|
|
|
|
|
Update DNS
|
|
|
|
|
</button>
|
|
|
|
|