CustomDomainActivate.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { useState } from 'react'
  2. import { toast } from 'sonner'
  3. import { Button } from 'ui'
  4. import { Admonition } from 'ui-patterns/admonition'
  5. import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
  6. import { DocsButton } from '@/components/ui/DocsButton'
  7. import Panel from '@/components/ui/Panel'
  8. import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
  9. import { useCheckCNAMERecordMutation } from '@/data/custom-domains/check-cname-mutation'
  10. import { useCustomDomainActivateMutation } from '@/data/custom-domains/custom-domains-activate-mutation'
  11. import { useCustomDomainDeleteMutation } from '@/data/custom-domains/custom-domains-delete-mutation'
  12. import type { CustomDomainResponse } from '@/data/custom-domains/custom-domains-query'
  13. import { DOCS_URL } from '@/lib/constants'
  14. export type CustomDomainActivateProps = {
  15. projectRef?: string
  16. customDomain: CustomDomainResponse
  17. }
  18. export const CustomDomainActivate = ({ projectRef, customDomain }: CustomDomainActivateProps) => {
  19. const [isActivateConfirmModalVisible, setIsActivateConfirmModalVisible] = useState(false)
  20. const { data: settings } = useProjectSettingsV2Query({ projectRef })
  21. const { mutate: checkCNAMERecord, isPending: isCheckingRecord } = useCheckCNAMERecordMutation()
  22. const { mutate: activateCustomDomain, isPending: isActivating } = useCustomDomainActivateMutation(
  23. {
  24. onSuccess: () => {
  25. toast.success(`Successfully activated custom domain`)
  26. setIsActivateConfirmModalVisible(false)
  27. },
  28. }
  29. )
  30. const { mutate: deleteCustomDomain, isPending: isDeleting } = useCustomDomainDeleteMutation({
  31. onSuccess: () => {
  32. toast.success(
  33. '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.'
  34. )
  35. },
  36. })
  37. const endpoint = settings?.app_config?.endpoint
  38. const onActivateCustomDomain = async () => {
  39. if (!projectRef) return console.error('Project ref is required')
  40. checkCNAMERecord(
  41. { domain: customDomain.hostname },
  42. { onSuccess: () => activateCustomDomain({ projectRef }) }
  43. )
  44. }
  45. const onCancelCustomDomain = async () => {
  46. if (!projectRef) return console.error('Project ref is required')
  47. deleteCustomDomain({ projectRef })
  48. }
  49. return (
  50. <>
  51. <div className="flex flex-col items-start">
  52. <Panel.Content>
  53. <div className="flex flex-col gap-2">
  54. <h4 className="text-foreground">Enable your custom domain</h4>
  55. <p className="text-sm text-foreground-light">
  56. Set up is almost complete. Press “Activate” below to enable{' '}
  57. <code className="text-code-inline">{customDomain.hostname}</code> for this project.
  58. </p>
  59. <p className="text-sm text-foreground-light">
  60. We recommend that you schedule a downtime window of 20 - 30 minutes for your
  61. application, as you will need to update any services that need to know about your
  62. custom domain (e.g client side code or OAuth providers).
  63. </p>
  64. </div>
  65. <div className="mt-4">
  66. <Admonition
  67. type="note"
  68. title="Retain your CNAME record for service continuity after activation"
  69. >
  70. <p>
  71. Your custom domain CNAME record for{' '}
  72. <code className="text-code-inline">{customDomain.hostname}</code> should resolve to{' '}
  73. {endpoint ? (
  74. <code className="text-code-inline">{endpoint}</code>
  75. ) : (
  76. "your project's API URL"
  77. )}
  78. . If you're using Cloudflare as your DNS provider, disable the proxy option.
  79. </p>
  80. </Admonition>
  81. </div>
  82. </Panel.Content>
  83. <div className="w-full border-t border-muted" />
  84. <Panel.Content className="w-full">
  85. <div className="flex items-center justify-between">
  86. <DocsButton href={`${DOCS_URL}/guides/platform/custom-domains`} />
  87. <div className="flex items-center space-x-2">
  88. <Button
  89. type="default"
  90. className="self-end"
  91. onClick={onCancelCustomDomain}
  92. loading={isDeleting}
  93. >
  94. Cancel
  95. </Button>
  96. <Button
  97. disabled={isDeleting}
  98. onClick={() => setIsActivateConfirmModalVisible(true)}
  99. className="self-end"
  100. >
  101. Activate
  102. </Button>
  103. </div>
  104. </div>
  105. </Panel.Content>
  106. </div>
  107. <ConfirmationModal
  108. size="small"
  109. loading={isCheckingRecord || isActivating}
  110. visible={isActivateConfirmModalVisible}
  111. title="Activate custom domain"
  112. confirmLabel="Activate"
  113. confirmLabelLoading="Activating"
  114. onCancel={() => setIsActivateConfirmModalVisible(false)}
  115. onConfirm={onActivateCustomDomain}
  116. >
  117. <p className="text-sm">
  118. Activating <code className="text-code-inline break-normal!">{customDomain.hostname}</code>{' '}
  119. will make it visible to users in place of your project’s Briven domain. The Briven
  120. domain will continue to work too.
  121. </p>
  122. </ConfirmationModal>
  123. </>
  124. )
  125. }