diff --git a/src/lib/components/domains/nameserverTable.svelte b/src/lib/components/domains/nameserverTable.svelte index a08de42080..2c0eea3fb7 100644 --- a/src/lib/components/domains/nameserverTable.svelte +++ b/src/lib/components/domains/nameserverTable.svelte @@ -19,7 +19,7 @@ {#if verified === true} {:else if verified === false} - + {/if} diff --git a/src/lib/components/domains/recordTable.svelte b/src/lib/components/domains/recordTable.svelte index 14683b7218..0380a1e07a 100644 --- a/src/lib/components/domains/recordTable.svelte +++ b/src/lib/components/domains/recordTable.svelte @@ -9,20 +9,56 @@ Alert } from '@appwrite.io/pink-svelte'; import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store'; + import { getSubdomain } from '$lib/helpers/tlds'; + import { isCloud } from '$lib/system'; - export let domain: string; - export let verified = undefined; - export let variant: 'cname' | 'a' | 'aaaa'; - export let service: 'sites' | 'general' = 'general'; + let { + domain, + verified = undefined, + variant, + service = 'general', + ruleStatus = undefined, + onNavigateToNameservers = () => {}, + onNavigateToA = () => {}, + onNavigateToAAAA = () => {} + }: { + domain: string; + verified?: boolean; + variant: 'cname' | 'a' | 'aaaa'; + service?: 'sites' | 'functions' | 'general'; + ruleStatus?: 'created' | 'verifying' | 'unverified' | 'verified'; + onNavigateToNameservers?: () => void; + onNavigateToA?: () => void; + onNavigateToAAAA?: () => void; + } = $props(); - let subdomain = domain?.split('.')?.slice(0, -2)?.join('.'); + const subdomain = $derived(getSubdomain(domain)); + const caaText = $derived( + $regionalConsoleVariables._APP_DOMAIN_TARGET_CAA?.includes(' ') + ? $regionalConsoleVariables._APP_DOMAIN_TARGET_CAA + : `0 issue "${$regionalConsoleVariables._APP_DOMAIN_TARGET_CAA}"` + ); + const aTabVisible = $derived( + !isCloud && + Boolean($regionalConsoleVariables._APP_DOMAIN_TARGET_A) && + $regionalConsoleVariables._APP_DOMAIN_TARGET_A !== '127.0.0.1' + ); + const aaaaTabVisible = $derived( + !isCloud && + Boolean($regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA) && + $regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA !== '::1' + ); function setTarget() { switch (variant) { case 'cname': - return service === 'general' - ? $regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME - : $regionalConsoleVariables._APP_DOMAIN_SITES; + if (service === 'sites') { + return $regionalConsoleVariables._APP_DOMAIN_SITES; + } else if (service === 'functions') { + return $regionalConsoleVariables._APP_DOMAIN_FUNCTIONS; + } else { + return $regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME; + } case 'a': return $regionalConsoleVariables._APP_DOMAIN_TARGET_A; case 'aaaa': @@ -37,42 +73,90 @@ {domain} - {#if verified === true} - - {:else if verified === false} - + {#if verified !== undefined} + {#if ruleStatus === 'created'} + + {:else if ruleStatus === 'verifying'} + + {:else if ruleStatus === 'unverified'} + + {:else if verified === true} + + {/if} {/if} - Add the following record on your DNS provider. Note that DNS changes may take time to - propagate fully. + Add the following {$regionalConsoleVariables._APP_DOMAIN_TARGET_CAA + ? 'records' + : 'record'} on your DNS provider. Note that DNS changes may take up to 48 hours to propagate + fully. - + - Type - Name - Value + Type + Name + Value - {variant.toUpperCase()} - {subdomain || '@'} - + {variant.toUpperCase()} + {subdomain || '@'} + + {#if $regionalConsoleVariables._APP_DOMAIN_TARGET_CAA} + + + + CAA + + + + @ + + + + + {/if} - {#if variant === 'cname'} - - If your domain uses CAA records, ensure certainly.com is authorized — otherwise, SSL - setup may fail. A list of all domain providers and their DNS setting is available here. - + {#if variant === 'cname' && !subdomain} + {#if isCloud} + + Since is an apex domain, CNAME + record is only supported by certain providers. If yours doesn't, please verify using + nameservers instead. + + {:else if aTabVisible || aaaaTabVisible} + + Since is an apex domain, CNAME + record is only supported by certain providers. If yours doesn't, please verify using + {#if aTabVisible} + A record + {#if aaaaTabVisible} + or AAAA record{/if} + {:else if aaaaTabVisible} + AAAA record + {/if} instead. + + {/if} {:else} A list of all domain providers and their DNS setting is available {#if dailyCredits !== null} - + {dailyCredits} daily and {monthlyCredits} monthly credits diff --git a/src/lib/helpers/tlds.ts b/src/lib/helpers/tlds.ts index 6de3c38887..3e9fc1fd69 100644 --- a/src/lib/helpers/tlds.ts +++ b/src/lib/helpers/tlds.ts @@ -18,3 +18,12 @@ export function isASubdomain(domain: string | null): boolean { return !!subdomain; } + +/** + * Returns the subdomain part from a full domain string. + */ +export function getSubdomain(domain: string): string { + if (!domain) return ''; + + return parse(domain, { allowPrivateDomains: true }).subdomain || ''; +} diff --git a/src/lib/studio/domains/add/view.svelte b/src/lib/studio/domains/add/view.svelte index 9cb1a603fb..a68b6341b9 100644 --- a/src/lib/studio/domains/add/view.svelte +++ b/src/lib/studio/domains/add/view.svelte @@ -24,7 +24,7 @@ }: { show: boolean; siteId: string; - onDomainAdded: (rule: string, domain: Models.Domain, verified: boolean) => void; + onDomainAdded: (rule: Models.ProxyRule, domain: Models.Domain, verified: boolean) => void; } = $props(); let domainName = $state(''); @@ -84,9 +84,15 @@ siteId }); - const verified = rule?.status === 'verified'; - onDomainAdded(rule.$id, domain, verified); - if (verified) await invalidate(Dependencies.SITES_DOMAINS); + const verified = rule?.status !== 'created'; + if (verified) { + await invalidate(Dependencies.SITES_DOMAINS); + addNotification({ + type: 'success', + message: 'Domain verified successfully' + }); + } + onDomainAdded(rule, domain, verified); show = false; } catch (error) { diff --git a/src/lib/studio/domains/manage/table.svelte b/src/lib/studio/domains/manage/table.svelte index aa9ebf5a1d..e4cd0f237e 100644 --- a/src/lib/studio/domains/manage/table.svelte +++ b/src/lib/studio/domains/manage/table.svelte @@ -108,27 +108,46 @@ {/each} {:else if proxyRules && proxyRules.total > 0} - {#each proxyRules.rules as rule} + {#each proxyRules.rules as proxyRule} + {@const isRetryable = + proxyRule.status === 'created' || proxyRule.status === 'unverified'} - + - {rule.domain} + {proxyRule.domain} - {#if rule.status === 'verifying'} - - {:else if rule.status !== 'verified'} + {#if proxyRule.status !== 'verified'} + type={proxyRule.status === 'verifying' ? undefined : 'error'} + content={proxyRule.status === 'created' + ? 'Verification failed' + : proxyRule.status === 'verifying' + ? 'Generating certificate' + : 'Certificate generation failed'} + size="xs" /> + {/if} + {#if isRetryable} + { + e.preventDefault(); + selectedProxyRule = proxyRule; + showRetry = true; + }}> + Retry + {/if} @@ -145,7 +164,7 @@ - {@render domainActions(rule, toggle)} + {@render domainActions(proxyRule, toggle, isRetryable)} @@ -169,12 +188,12 @@ {/if} -{#snippet domainActions(rule, toggle)} +{#snippet domainActions(rule, toggle, isRetryable)} Open domain - {#if rule.status !== 'verified' && rule.status !== 'verifying'} + {#if isRetryable} { diff --git a/src/lib/studio/domains/verify/view.svelte b/src/lib/studio/domains/verify/view.svelte index 466527141c..dcd0771f50 100644 --- a/src/lib/studio/domains/verify/view.svelte +++ b/src/lib/studio/domains/verify/view.svelte @@ -18,7 +18,6 @@ import { page } from '$app/state'; import Wizard from '$lib/layout/wizard.svelte'; import { writable } from 'svelte/store'; - import { isASubdomain } from '$lib/helpers/tlds'; import RecordTable from '$lib/components/domains/recordTable.svelte'; import NameserverTable from '$lib/components/domains/nameserverTable.svelte'; import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store'; @@ -32,43 +31,53 @@ show = $bindable(false) }: { show: boolean; - rule: string; + rule?: Models.ProxyRule; domain: Models.Domain; onChangeDomain: () => void; onVerified?: () => void; } = $props(); - const isSubDomain = $derived.by(() => isASubdomain(domain?.domain)); - - let verified = $state(false); + const showCNAMETab = $derived( + Boolean($regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME) && + $regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME !== 'localhost' + ); + const showATab = $derived( + !isCloud && + Boolean($regionalConsoleVariables._APP_DOMAIN_TARGET_A) && + $regionalConsoleVariables._APP_DOMAIN_TARGET_A !== '127.0.0.1' + ); + const showAAAATab = $derived( + !isCloud && + Boolean($regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA) && + $regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA !== '::1' + ); + const showNSTab = isCloud; + + let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab()); + let verified: boolean | undefined = $state(undefined); let isSubmitting = $state(writable(false)); - let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>('nameserver'); async function verify() { try { - if (selectedTab !== 'nameserver') { - const ruleData = await sdk - .forProject(page.params.region, page.params.project) - .proxy.updateRuleVerification({ ruleId: rule }); - - verified = ruleData.status === 'verified'; - } else if (isCloud) { - const domainData = await sdk.forConsole.domains.get({ - domainId: domain.$id - }); - - verified = domainData.nameservers.toLowerCase() === 'appwrite'; + if (isCloud) { + try { + await sdk.forConsole.domains.updateNameservers({ + domainId: domain.$id + }); + } catch (error) { + // Ignore error + } } - if (!verified) { - throw new Error( - 'Domain verification failed. Please check your domain settings or try again later' - ); - } + await sdk + .forProject(page.params.region, page.params.project) + .proxy.updateRuleVerification({ ruleId: rule.$id }); + + verified = true; addNotification({ type: 'success', - message: 'Domain added successfully' + message: 'Domain verified successfully' }); show = false; @@ -89,24 +98,16 @@ if (rule) { await sdk .forProject(page.params.region, page.params.project) - .proxy.deleteRule({ ruleId: rule }); + .proxy.deleteRule({ ruleId: rule.$id }); } show = false; onChangeDomain(); } - $effect(() => { - if ($regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME && isSubDomain) { - selectedTab = 'cname'; - } else if (!isCloud && $regionalConsoleVariables._APP_DOMAIN_TARGET_A) { - selectedTab = 'a'; - } else if (!isCloud && $regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA) { - selectedTab = 'aaaa'; - } else { - selectedTab = 'nameserver'; - } - }); + function getDefaultTab() { + return showCNAMETab ? 'cname' : showATab ? 'a' : showAAAATab ? 'aaaa' : 'nameserver'; + } {#if show} @@ -123,7 +124,7 @@ - {domain?.domain} + {rule?.domain} @@ -134,7 +135,7 @@
- {#if isSubDomain && !!$regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME && $regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME !== 'localhost'} + {#if showCNAMETab} (selectedTab = 'cname')} @@ -142,7 +143,7 @@ CNAME {/if} - {#if isCloud} + {#if showNSTab} (selectedTab = 'nameserver')} @@ -150,7 +151,7 @@ Nameservers {/if} - {#if !isCloud && !!$regionalConsoleVariables._APP_DOMAIN_TARGET_A && $regionalConsoleVariables._APP_DOMAIN_TARGET_A !== '127.0.0.1'} + {#if showATab} (selectedTab = 'a')} @@ -158,7 +159,7 @@ A {/if} - {#if !isCloud && !!$regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA && $regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA !== '::1'} + {#if showAAAATab} (selectedTab = 'aaaa')} @@ -171,13 +172,17 @@
{#if selectedTab === 'nameserver'} - + {:else} + domain={rule?.domain} + ruleStatus={rule?.status} + onNavigateToNameservers={() => (selectedTab = 'nameserver')} + onNavigateToA={() => (selectedTab = 'a')} + onNavigateToAAAA={() => (selectedTab = 'aaaa')} /> {/if} diff --git a/src/lib/studio/studio.svelte b/src/lib/studio/studio.svelte index c59b0bfe1f..1d9279af0f 100644 --- a/src/lib/studio/studio.svelte +++ b/src/lib/studio/studio.svelte @@ -33,7 +33,7 @@ userId: string; } = $props(); - const siteId = `project-${projectId}`; + const siteId = $derived(`project-${projectId}`); const isStage = sdk.forConsole.client.config.endpoint.includes('stage'); let showAddDomainsWizard = $state(false); let showManageDomainsSheet = $state(false); @@ -42,7 +42,7 @@ ); let showVerifyDomainsWizard = $state(false); - let ruleIdForVerification = $state(null); + let ruleForVerification = $state(null); let domainForVerification = $state(null); onMount(() => { @@ -94,20 +94,29 @@ bind:show={showAddDomainsWizard} onDomainAdded={(rule, domain, verified) => { invalidateSiteInfo(); - if (!verified) { - ruleIdForVerification = rule; + if (verified) { + ruleForVerification = null; + domainForVerification = null; + showManageDomainsSheet = true; + } else { + ruleForVerification = rule; domainForVerification = domain; showVerifyDomainsWizard = true; } }} /> { + invalidateSiteInfo(); + ruleForVerification = null; + domainForVerification = null; + showManageDomainsSheet = true; + }} bind:show={showVerifyDomainsWizard} onChangeDomain={() => { - ruleIdForVerification = null; + ruleForVerification = null; domainForVerification = null; showAddDomainsWizard = true; }} /> diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/retryDomainModal.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/retryDomainModal.svelte index 949d8079c9..17933613bb 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/retryDomainModal.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/retryDomainModal.svelte @@ -10,7 +10,6 @@ import { Divider, Tabs } from '@appwrite.io/pink-svelte'; import { isCloud } from '$lib/system'; import { page } from '$app/state'; - import { isASubdomain } from '$lib/helpers/tlds'; import NameserverTable from '$lib/components/domains/nameserverTable.svelte'; import RecordTable from '$lib/components/domains/recordTable.svelte'; import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store'; @@ -23,41 +22,50 @@ selectedProxyRule: Models.ProxyRule; } = $props(); - const isSubDomain = $derived.by(() => isASubdomain(selectedProxyRule?.domain)); - - let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>('nameserver'); - - $effect(() => { - if ($regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME && isSubDomain) { - selectedTab = 'cname'; - } else if (!isCloud && $regionalConsoleVariables._APP_DOMAIN_TARGET_A) { - selectedTab = 'a'; - } else if (!isCloud && $regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA) { - selectedTab = 'aaaa'; - } else { - selectedTab = 'nameserver'; - } - }); + const showCNAMETab = $derived( + Boolean($regionalConsoleVariables._APP_DOMAIN_SITES) && + $regionalConsoleVariables._APP_DOMAIN_SITES !== 'localhost' + ); + const showATab = $derived( + !isCloud && + Boolean($regionalConsoleVariables._APP_DOMAIN_TARGET_A) && + $regionalConsoleVariables._APP_DOMAIN_TARGET_A !== '127.0.0.1' + ); + const showAAAATab = $derived( + !isCloud && + Boolean($regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA) && + $regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA !== '::1' + ); + const showNSTab = isCloud; + let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab()); let error = $state(null); - let verified = $state(false); + let verified: boolean | undefined = $state(undefined); + + function getDefaultTab() { + return showCNAMETab ? 'cname' : showATab ? 'a' : showAAAATab ? 'aaaa' : 'nameserver'; + } async function retryDomain() { try { - const domain = await sdk + error = null; + const proxyRule = await sdk .forProject(page.params.region, page.params.project) .proxy.updateRuleVerification({ ruleId: selectedProxyRule.$id }); - show = false; - verified = domain.status === 'verified'; + verified = proxyRule.status !== 'created'; await invalidate(Dependencies.SITES_DOMAINS); - addNotification({ - type: 'success', - message: `${selectedProxyRule.domain} has been verified` - }); + if (verified) { + addNotification({ + type: 'success', + message: `Domain verified successfully` + }); + } + show = false; trackEvent(Submit.DomainUpdateVerification); } catch (e) { + verified = false; error = e.message ?? 'Domain verification failed. Please check your domain settings or try again later'; @@ -75,7 +83,7 @@
- {#if isSubDomain && !!$regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME && $regionalConsoleVariables._APP_DOMAIN_TARGET_CNAME !== 'localhost'} + {#if showCNAMETab} (selectedTab = 'cname')} @@ -83,7 +91,7 @@ CNAME {/if} - {#if isCloud} + {#if showNSTab} (selectedTab = 'nameserver')} @@ -91,7 +99,7 @@ Nameservers {/if} - {#if !isCloud && !!$regionalConsoleVariables._APP_DOMAIN_TARGET_A && $regionalConsoleVariables._APP_DOMAIN_TARGET_A !== '127.0.0.1'} + {#if showATab} (selectedTab = 'a')} @@ -99,7 +107,7 @@ A {/if} - {#if !isCloud && !!$regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA && $regionalConsoleVariables._APP_DOMAIN_TARGET_AAAA !== '::1'} + {#if showAAAATab} (selectedTab = 'aaaa')} @@ -117,7 +125,11 @@ {verified} service="sites" variant={selectedTab} - domain={selectedProxyRule.domain} /> + domain={selectedProxyRule.domain} + ruleStatus={selectedProxyRule.status} + onNavigateToNameservers={() => (selectedTab = 'nameserver')} + onNavigateToA={() => (selectedTab = 'a')} + onNavigateToAAAA={() => (selectedTab = 'aaaa')} /> {/if}