diff --git a/apps/api/src/trust-portal/trust-portal.service.ts b/apps/api/src/trust-portal/trust-portal.service.ts index 430997d967..f8f6cdca98 100644 --- a/apps/api/src/trust-portal/trust-portal.service.ts +++ b/apps/api/src/trust-portal/trust-portal.service.ts @@ -1099,7 +1099,7 @@ export class TrustPortalService { isTxtVerified, isVercelTxtVerified, error: - 'Error verifying DNS records. Please ensure both CNAME and TXT records are correctly configured, or wait a few minutes and try again.', + 'Some DNS records are not configured correctly. Please check the records marked as unverified above and try again.', }; } diff --git a/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/TrustPortalDomain.tsx b/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/TrustPortalDomain.tsx index 0265974c81..628561b2de 100644 --- a/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/TrustPortalDomain.tsx +++ b/apps/app/src/app/(app)/[orgId]/trust/portal-settings/components/TrustPortalDomain.tsx @@ -1,5 +1,6 @@ 'use client'; +import { useDnsStatus } from '@/hooks/use-dns-status'; import { DEFAULT_CNAME_TARGET, useDomain } from '@/hooks/use-domain'; import { Button } from '@trycompai/ui/button'; import { @@ -13,11 +14,12 @@ import { import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@trycompai/ui/form'; import { Input } from '@trycompai/ui/input'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@trycompai/ui/tooltip'; +import { Alert, AlertDescription } from '@trycompai/design-system'; +import { CheckmarkFilled, Copy, Launch, Renew, WarningFilled } from '@trycompai/design-system/icons'; import { zodResolver } from '@hookform/resolvers/zod'; -import { AlertCircle, CheckCircle, ClipboardCopy, ExternalLink, Loader2 } from 'lucide-react'; import { usePermissions } from '@/hooks/use-permissions'; import { useTrustPortalSettings } from '@/hooks/use-trust-portal-settings'; -import { useEffect, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; import { toast } from 'sonner'; import { z } from 'zod'; @@ -47,10 +49,6 @@ export function TrustPortalDomain({ vercelVerification: string | null; orgId: string; }) { - const [isCnameVerified, setIsCnameVerified] = useState(false); - const [isTxtVerified, setIsTxtVerified] = useState(false); - const [isVercelTxtVerified, setIsVercelTxtVerified] = useState(false); - const { data: domainStatus } = useDomain(initialDomain); const verificationInfo = useMemo(() => { @@ -81,14 +79,15 @@ export function TrustPortalDomain({ return target.endsWith('.') ? target : `${target}.`; }, [domainStatus?.data?.cnameTarget]); - useEffect(() => { - const isCnameVerified = localStorage.getItem(`${initialDomain}-isCnameVerified`); - const isTxtVerified = localStorage.getItem(`${initialDomain}-isTxtVerified`); - const isVercelTxtVerified = localStorage.getItem(`${initialDomain}-isVercelTxtVerified`); - setIsCnameVerified(isCnameVerified === 'true'); - setIsTxtVerified(isTxtVerified === 'true'); - setIsVercelTxtVerified(isVercelTxtVerified === 'true'); - }, [initialDomain]); + const { + isCnameVerified, + isTxtVerified, + isVercelTxtVerified, + mutate: recheckDns, + } = useDnsStatus({ + domain: initialDomain, + enabled: !!initialDomain && !isEffectivelyVerified, + }); const { hasPermission } = usePermissions(); const canUpdate = hasPermission('trust', 'update'); @@ -104,14 +103,6 @@ export function TrustPortalDomain({ }); const onSubmit = async (data: z.infer) => { - setIsCnameVerified(false); - setIsTxtVerified(false); - setIsVercelTxtVerified(false); - - localStorage.removeItem(`${initialDomain}-isCnameVerified`); - localStorage.removeItem(`${initialDomain}-isTxtVerified`); - localStorage.removeItem(`${initialDomain}-isVercelTxtVerified`); - setIsUpdatingDomain(true); try { const result = await submitCustomDomain(data.domain); @@ -137,33 +128,10 @@ export function TrustPortalDomain({ setIsCheckingDns(true); try { const data = await checkDns(form.watch('domain')); + // Update SWR cache with the result directly — no duplicate request + recheckDns(data, { revalidate: false }); if (data && typeof data === 'object' && 'error' in data && data.error) { toast.error(data.error as string); - if (data.isCnameVerified) { - setIsCnameVerified(true); - localStorage.setItem(`${initialDomain}-isCnameVerified`, 'true'); - } - if (data.isTxtVerified) { - setIsTxtVerified(true); - localStorage.setItem(`${initialDomain}-isTxtVerified`, 'true'); - } - if (data.isVercelTxtVerified) { - setIsVercelTxtVerified(true); - localStorage.setItem(`${initialDomain}-isVercelTxtVerified`, 'true'); - } - } else if (data && typeof data === 'object') { - if (data.isCnameVerified) { - setIsCnameVerified(true); - localStorage.removeItem(`${initialDomain}-isCnameVerified`); - } - if (data.isTxtVerified) { - setIsTxtVerified(true); - localStorage.removeItem(`${initialDomain}-isTxtVerified`); - } - if (data.isVercelTxtVerified) { - setIsVercelTxtVerified(true); - localStorage.removeItem(`${initialDomain}-isVercelTxtVerified`); - } } } catch { toast.error( @@ -198,7 +166,7 @@ export function TrustPortalDomain({ - + Domain is verified @@ -207,7 +175,7 @@ export function TrustPortalDomain({ - + Domain is not verified yet @@ -235,7 +203,7 @@ export function TrustPortalDomain({ disabled={isCheckingDns} > {isCheckingDns ? ( - + ) : null} Check DNS record @@ -251,35 +219,30 @@ export function TrustPortalDomain({ !isEffectivelyVerified && (
{verificationInfo && ( -
-
- -

- This domain is linked to another Vercel account. To use it with this - project, add a {verificationInfo.type} record at{' '} - {verificationInfo.domain} to verify ownership. You can remove the record - after verification is complete. - - Learn more - - -

-
-
+ + + This domain is linked to another Vercel account. To use it with this + project, add a {verificationInfo.type} record at{' '} + {verificationInfo.domain} to verify ownership. You can remove the record + after verification is complete.{' '} + + Learn more + + + )}
- +
- - - + + + @@ -287,15 +250,15 @@ export function TrustPortalDomain({ @@ -325,15 +288,15 @@ export function TrustPortalDomain({ @@ -370,15 +333,15 @@ export function TrustPortalDomain({ @@ -412,14 +375,18 @@ export function TrustPortalDomain({
VerifiedTypeNameVerifiedTypeName Value
{isCnameVerified ? ( - + ) : ( - + )} CNAME
- {initialDomain} + {initialDomain}
- {cnameTarget} + {cnameTarget}
{isTxtVerified ? ( - + ) : ( - + )} TXT
- @ + @
- + compai-domain-verification={orgId}
{isVercelTxtVerified ? ( - + ) : ( - + )} TXT
- _vercel + _vercel
- + {effectiveVercelTxtValue}
-
-
Type:
-
CNAME
+
+ {isCnameVerified ? ( + + ) : ( + + )} + CNAME
Name:
- {form.watch('domain')} + {form.watch('domain')}
Value:
- {cnameTarget} + {cnameTarget}
-
-
Type:
-
TXT
+
+ {isTxtVerified ? ( + + ) : ( + + )} + TXT
Name:
- @ + @
Value:
- + compai-domain-verification={orgId}
{needsVercelTxt && ( <>
-
-
Type:
-
TXT
+
+ {isVercelTxtVerified ? ( + + ) : ( + + )} + TXT (_vercel)
Name:
- _vercel + _vercel
Value:
- {effectiveVercelTxtValue} + {effectiveVercelTxtValue}
@@ -544,9 +519,9 @@ export function TrustPortalDomain({ } > {isUpdatingDomain ? ( - + ) : null} - {'Save'} + Save diff --git a/apps/app/src/hooks/use-dns-status.ts b/apps/app/src/hooks/use-dns-status.ts new file mode 100644 index 0000000000..060e4e9211 --- /dev/null +++ b/apps/app/src/hooks/use-dns-status.ts @@ -0,0 +1,45 @@ +'use client'; + +import { apiClient } from '@/lib/api-client'; +import useSWR from 'swr'; + +interface DnsCheckResponse { + success: boolean; + isCnameVerified?: boolean; + isTxtVerified?: boolean; + isVercelTxtVerified?: boolean; + error?: string; +} + +/** + * Checks DNS record verification status for a domain. + * Calls the check-dns endpoint once on mount (no polling). + * Disabled when domain is empty or already verified. + */ +export function useDnsStatus({ + domain, + enabled, +}: { + domain: string; + enabled: boolean; +}) { + const { data, isLoading, mutate } = useSWR( + enabled && domain ? ['check-dns', domain] : null, + async () => { + const response = await apiClient.post( + '/v1/trust-portal/settings/check-dns', + { domain }, + ); + return response.data; + }, + { revalidateOnFocus: false, revalidateOnReconnect: false }, + ); + + return { + isCnameVerified: !!data?.isCnameVerified, + isTxtVerified: !!data?.isTxtVerified, + isVercelTxtVerified: !!data?.isVercelTxtVerified, + isLoading, + mutate, + }; +}