From 7964563b620868279f5fb6baab0d7499c41f51ed Mon Sep 17 00:00:00 2001 From: Dries Augustyns Date: Tue, 17 Feb 2026 16:33:34 +0100 Subject: [PATCH] feat: Add advanced DNS configuration --- apps/api/src/controllers/Config.ts | 4 + apps/web/src/components/DomainsSettings.tsx | 428 ++++++++++++++------ apps/web/src/lib/hooks/useConfig.ts | 3 + 3 files changed, 310 insertions(+), 125 deletions(-) diff --git a/apps/api/src/controllers/Config.ts b/apps/api/src/controllers/Config.ts index a121a9c..89d458c 100644 --- a/apps/api/src/controllers/Config.ts +++ b/apps/api/src/controllers/Config.ts @@ -3,6 +3,7 @@ import type {Request, Response} from 'express'; import { API_URI, + AWS_SES_REGION, DASHBOARD_URI, GITHUB_OAUTH_ENABLED, GOOGLE_OAUTH_ENABLED, @@ -59,6 +60,9 @@ export class Config { : null, }, }, + aws: { + sesRegion: AWS_SES_REGION, + }, }); } } diff --git a/apps/web/src/components/DomainsSettings.tsx b/apps/web/src/components/DomainsSettings.tsx index f24fe42..f0b67a3 100644 --- a/apps/web/src/components/DomainsSettings.tsx +++ b/apps/web/src/components/DomainsSettings.tsx @@ -3,8 +3,6 @@ import {useForm} from 'react-hook-form'; import {zodResolver} from '@hookform/resolvers/zod'; import {DomainSchemas} from '@plunk/shared'; import { - Alert, - AlertDescription, Badge, Button, Card, @@ -22,7 +20,8 @@ import { Input, } from '@plunk/ui'; import {AnimatePresence, motion} from 'framer-motion'; -import {Check, CheckCircle2, Copy, Loader2, RefreshCw, Trash2, XCircle} from 'lucide-react'; +import {Check, CheckCircle2, ChevronDown, Copy, Loader2, RefreshCw, Trash2, XCircle} from 'lucide-react'; +import {useConfig} from '../lib/hooks/useConfig'; import {useAddDomain, useCheckDomainVerification, useDomains, useRemoveDomain} from '../lib/hooks/useDomains'; interface DomainsSettingsProps { @@ -34,6 +33,7 @@ export function DomainsSettings({projectId}: DomainsSettingsProps) { const {addDomain} = useAddDomain(); const {checkVerification} = useCheckDomainVerification(); const {removeDomain} = useRemoveDomain(); + const {data: config} = useConfig(); const [successMessage, setSuccessMessage] = useState(null); const [errorMessage, setErrorMessage] = useState(null); @@ -47,6 +47,7 @@ export function DomainsSettings({projectId}: DomainsSettingsProps) { const [cooldownSeconds, setCooldownSeconds] = useState<{[key: string]: number}>({}); const [showRemoveDialog, setShowRemoveDialog] = useState(false); const [domainToRemove, setDomainToRemove] = useState<{id: string; name: string} | null>(null); + const [expandedDomains, setExpandedDomains] = useState<{[key: string]: boolean}>({}); const form = useForm<{domain: string}>({ resolver: zodResolver(DomainSchemas.create.omit({projectId: true})), @@ -86,6 +87,28 @@ export function DomainsSettings({projectId}: DomainsSettingsProps) { return () => clearInterval(interval); }, [lastVerificationCheck]); + // Auto-expand unverified domains on initial load + useEffect(() => { + if (domains && domains.length > 0) { + const unverifiedDomains = domains + .filter(d => !d.verified) + .reduce( + (acc, d) => { + acc[d.id] = true; + return acc; + }, + {} as {[key: string]: boolean}, + ); + + if (Object.keys(unverifiedDomains).length > 0) { + setExpandedDomains(prev => ({ + ...prev, + ...unverifiedDomains, + })); + } + } + }, [domains]); + const showMessage = (type: 'success' | 'error', message: string) => { if (type === 'success') { setSuccessMessage(message); @@ -113,6 +136,11 @@ export function DomainsSettings({projectId}: DomainsSettingsProps) { }, })); setSelectedDomain(newDomain.id); + // Auto-expand newly added domain + setExpandedDomains(prev => ({ + ...prev, + [newDomain.id]: true, + })); } await mutateDomains(); @@ -322,20 +350,42 @@ export function DomainsSettings({projectId}: DomainsSettingsProps) { - {!status.verified && Array.isArray(status.tokens) && (status.tokens as string[]).length > 0 && ( -
- - -
-
-

DNS Configuration Required

-

- Add the following DNS records to verify your domain. DNS changes can take up to 48 - hours to propagate. + {Array.isArray(status.tokens) && (status.tokens as string[]).length > 0 && ( +

+ + + {expandedDomains[domain.id] && ( +
+ {/* Required DKIM Records for Sending */} +
+
+

Required for Sending

+ + REQUIRED + +
+ {!status.verified && ( +

+ Add these DKIM records to verify your domain and send emails. DNS changes can take up + to 48 hours to propagate.

-
+ )} - {/* DNS Records Table */}
@@ -401,120 +451,248 @@ export function DomainsSettings({projectId}: DomainsSettingsProps) { ))} - - {/* MX Record */} - - - - - - - {/* TXT Record (SPF) */} - - - - -
- MX - -
- - plunk.{domain.domain} - - -
-
-
- - 10 feedback-smtp.eu-north-1.amazonses.com - - -
-
- TXT - -
- - plunk.{domain.domain} - - -
-
-
- - "v=spf1 include:amazonses.com ~all" - - -
-
- -
-
- - - -
-

- Click the copy icon to copy record values. After adding all records to your DNS - provider, use the refresh button above to verify your domain. -

-
- - + + {/* Optional: Custom MAIL FROM Domain */} + {config?.aws?.sesRegion && ( +
+
+

+ Custom MAIL FROM Domain (Optional) +

+ + OPTIONAL + +
+

+ Set up a custom MAIL FROM domain (plunk.{domain.domain}) to improve deliverability and + handle bounces/complaints. +

+ +
+ + + + + + + + + + {/* MX Record (Bounce/Complaint Handling) */} + + + + + + + {/* TXT Record (SPF) */} + + + + + + +
+ Type + + Name + + Value +
+ MX + +
+ + plunk.{domain.domain} + + +
+
+
+ + 10 feedback-smtp.{config.aws.sesRegion}.amazonses.com + + +
+
+ TXT + +
+ + plunk.{domain.domain} + + +
+
+
+ + "v=spf1 include:amazonses.com ~all" + + +
+
+
+
+ )} + + {/* Optional: Inbound Email */} + {config?.aws?.sesRegion && ( +
+
+

Inbound Email (Optional)

+ + OPTIONAL + +
+

+ Configure this MX record to receive emails at your domain. +

+ +
+ + + + + + + + + + {/* Inbound MX Record */} + + + + + + +
+ Type + + Name + + Value +
+ MX + +
+ + {domain.domain} + + +
+
+
+ + 10 inbound-smtp.{config.aws.sesRegion}.amazonaws.com + + +
+
+
+
+ )} + +
+
+ + + +
+

+ Click the copy icon to copy record values. After adding all records to your DNS + provider, use the refresh button above to verify your domain. +

+
+
+ )}
)}
diff --git a/apps/web/src/lib/hooks/useConfig.ts b/apps/web/src/lib/hooks/useConfig.ts index 3a08246..c4198df 100644 --- a/apps/web/src/lib/hooks/useConfig.ts +++ b/apps/web/src/lib/hooks/useConfig.ts @@ -19,6 +19,9 @@ export interface ConfigResponse { ports: {secure: number; submission: number} | null; }; }; + aws: { + sesRegion: string; + }; } /**