| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- import { useParams } from 'common'
- import { AlertCircle, RefreshCw } from 'lucide-react'
- import { useEffect } from 'react'
- import { toast } from 'sonner'
- import { Alert, AlertDescription, AlertTitle, Button, WarningIcon } from 'ui'
- import { Admonition } from 'ui-patterns/admonition'
- import { DNSRecord } from './DNSRecord'
- import { DNSTableHeaders } from './DNSTableHeaders'
- import { DocsButton } from '@/components/ui/DocsButton'
- import { InlineLink } from '@/components/ui/InlineLink'
- import Panel from '@/components/ui/Panel'
- import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
- import { useCustomDomainDeleteMutation } from '@/data/custom-domains/custom-domains-delete-mutation'
- import { useCustomDomainsQuery } from '@/data/custom-domains/custom-domains-query'
- import { useCustomDomainReverifyQuery } from '@/data/custom-domains/custom-domains-reverify-query'
- import { DOCS_URL } from '@/lib/constants'
- export const CustomDomainVerify = () => {
- const { ref: projectRef } = useParams()
- const { data: settings } = useProjectSettingsV2Query({ projectRef })
- const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
- const customDomain = customDomainData?.customDomain
- const isSSLCertificateDeploying =
- customDomain?.ssl.status !== undefined && customDomain.ssl.txt_name === undefined
- const { mutate: deleteCustomDomain, isPending: isDeleting } = useCustomDomainDeleteMutation({
- onSuccess: () => {
- toast.success(
- 'Custom domain setup cancelled successfully. It may take a few seconds before your custom domain is fully removed, so you may need to refresh your browser.'
- )
- },
- })
- const {
- data: reverifyData,
- refetch: refetchReverify,
- isFetching: isReverifyLoading,
- isError: isReverifyError,
- error: reverifyError,
- } = useCustomDomainReverifyQuery(
- { projectRef },
- {
- // Poll every 10 seconds if the SSL certificate is being deployed
- refetchInterval: isSSLCertificateDeploying && !isDeleting ? 10000 : false,
- }
- )
- useEffect(() => {
- if (isReverifyError) {
- toast.error(reverifyError?.message)
- }
- }, [isReverifyError, reverifyError])
- const isNotVerifiedYet = reverifyData?.status === '2_initiated'
- const hasCAAErrors = customDomain?.ssl.validation_errors?.reduce(
- (acc, error) => acc || error.message.includes('caa_error'),
- false
- )
- const isValidating = (customDomain?.ssl.txt_name ?? '') === ''
- const onReverifyCustomDomain = () => {
- if (!projectRef) return console.error('Project ref is required')
- refetchReverify()
- }
- const onCancelCustomDomain = async () => {
- if (!projectRef) return console.error('Project ref is required')
- deleteCustomDomain({ projectRef })
- }
- return (
- <>
- <Panel.Content className="space-y-6">
- <div>
- <h4 className="text-foreground mb-2">
- Configure TXT verification for your custom domain{' '}
- <code className="text-code-inline">{customDomain?.hostname}</code>
- </h4>
- <p className="text-sm text-foreground-light">
- Set the following TXT record(s) in your DNS provider, then click verify to confirm your
- control over the domain. Records which have been successfully verified will be removed
- from this list below.
- </p>
- {!isValidating && (
- <div className="mt-4 mb-2">
- <Admonition
- type="note"
- title={
- isNotVerifiedYet
- ? 'Unable to verify records from DNS provider yet.'
- : 'Please note that it may take up to 24 hours for the DNS records to propagate.'
- }
- >
- <p>
- You may also visit{' '}
- <InlineLink href={`https://whatsmydns.net/#TXT/${customDomain?.ssl.txt_name}`}>
- here
- </InlineLink>{' '}
- to check if your DNS has been propagated successfully before clicking verify.
- </p>
- {isNotVerifiedYet && (
- <p className="mt-1">
- Some registrars will require you to remove the domain name when creating DNS
- records. As an example, to create a record for{' '}
- <code className="text-code-inline">foo.app.example.com</code>, you would need to
- create an entry for <code className="text-code-inline">foo.app</code>.
- </p>
- )}
- </Admonition>
- </div>
- )}
- </div>
- {hasCAAErrors && (
- <Alert>
- <WarningIcon />
- <AlertTitle>Certificate Authority Authentication (CAA) error</AlertTitle>
- <AlertDescription>
- Please add a CAA record allowing "digicert.com" to issue certificates for{' '}
- <code className="text-code-inline">{customDomain?.hostname}</code>. For example:{' '}
- <code className="text-code-inline">0 issue "digicert.com"</code>
- </AlertDescription>
- </Alert>
- )}
- {customDomain?.ssl.status === 'validation_timed_out' ? (
- <Alert>
- <WarningIcon />
- <AlertTitle>Validation timed out</AlertTitle>
- <AlertDescription>
- Please click "Verify" again to retry the validation of the records
- </AlertDescription>
- </Alert>
- ) : (
- <div className="space-y-2">
- <DNSTableHeaders display={customDomain?.ssl.txt_name ?? ''} />
- {customDomain?.verification_errors?.includes(
- 'custom hostname does not CNAME to this zone.'
- ) && (
- <DNSRecord
- type="CNAME"
- name={customDomain?.hostname}
- value={settings?.app_config?.endpoint ?? 'Loading...'}
- />
- )}
- {!isValidating && customDomain?.ssl.status === 'pending_validation' && (
- <DNSRecord
- type="TXT"
- name={customDomain?.ssl.txt_name ?? 'Loading...'}
- value={customDomain?.ssl.txt_value ?? 'Loading...'}
- />
- )}
- {customDomain?.ssl.status === 'pending_deployment' && (
- <div className="flex items-center justify-center space-x-2 py-8">
- <AlertCircle size={16} strokeWidth={1.5} />
- <p className="text-sm text-foreground-light">
- SSL certificate is being deployed. Please wait a few minutes and try again.
- </p>
- </div>
- )}
- </div>
- )}
- </Panel.Content>
- <div className="border-t border-muted" />
- <Panel.Content>
- <div className="flex items-center justify-between">
- <DocsButton href={`${DOCS_URL}/guides/platform/custom-domains`} />
- <div className="flex items-center space-x-2">
- <Button
- type="default"
- onClick={onCancelCustomDomain}
- loading={isDeleting}
- className="self-end"
- >
- Cancel
- </Button>
- <Button
- icon={<RefreshCw />}
- onClick={onReverifyCustomDomain}
- loading={!isValidating && isReverifyLoading}
- disabled={isDeleting || isReverifyLoading || isValidating}
- className="self-end"
- >
- Verify
- </Button>
- </div>
- </div>
- </Panel.Content>
- </>
- )
- }
|