Handle NS record flow and auto-verification of sub-domains

This commit is contained in:
Hemachandar
2026-01-13 22:23:30 +05:30
parent da326f8fe2
commit e9bf6017ea
13 changed files with 250 additions and 202 deletions
@@ -3,8 +3,15 @@
import { Badge, Layout, Typography, Table, InteractiveText } from '@appwrite.io/pink-svelte';
export let domain: string;
export let verified = undefined;
let {
domain,
verified = undefined,
ruleStatus = undefined
}: {
domain: string;
verified?: boolean;
ruleStatus?: 'created' | 'verifying' | 'unverified' | 'verified';
} = $props();
const nameserverList = $regionalConsoleVariables?._APP_DOMAINS_NAMESERVERS
? $regionalConsoleVariables?._APP_DOMAINS_NAMESERVERS?.split(',')
@@ -16,10 +23,20 @@
<Typography.Text variant="l-500" color="--fgcolor-neutral-primary">
{domain}
</Typography.Text>
{#if verified === true}
<Badge variant="secondary" type="success" size="xs" content="Verified" />
{:else if verified === false}
<Badge variant="secondary" type="warning" size="xs" content="Verification failed" />
{#if verified !== undefined}
{#if ruleStatus === 'created'}
<Badge variant="secondary" type="error" size="xs" content="Verification failed" />
{:else if ruleStatus === 'verifying'}
<Badge variant="secondary" size="xs" content="Generating certificate" />
{:else if ruleStatus === 'unverified'}
<Badge
variant="secondary"
type="error"
size="xs"
content="Certificate generation failed" />
{:else if verified === true}
<Badge variant="secondary" type="success" size="xs" content="Verified" />
{/if}
{/if}
</Layout.Stack>
<Typography.Text variant="m-400">
@@ -48,16 +48,24 @@
async function addDomain() {
const apexDomain = getApexDomain(domainName);
let domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
const domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
if (apexDomain && !domain && isCloud) {
try {
domain = await sdk.forConsole.domains.create({
await sdk.forConsole.domains.create({
teamId: $project.teamId,
domain: apexDomain
});
} catch (error) {
// Apex domain creation error needs to be silent.
// apex might already be added on organization level, skip.
const alreadyAdded = error?.type === 'domain_already_exists';
if (!alreadyAdded) {
addNotification({
type: 'error',
message: error.message
});
return;
}
}
}
@@ -89,12 +97,18 @@
functionId: page.params.function
});
}
if (rule?.status === 'verified') {
await invalidate(Dependencies.FUNCTION_DOMAINS);
const verified = rule?.status !== 'created';
if (verified) {
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await goto(routeBase);
await invalidate(Dependencies.FUNCTION_DOMAINS);
} else {
await goto(`${routeBase}/add-domain/verify-${domainName}?rule=${rule.$id}`);
await invalidate(Dependencies.FUNCTION_DOMAINS);
}
} catch (error) {
addNotification({
@@ -11,7 +11,6 @@
} from '@appwrite.io/pink-svelte';
import { Button, Form } from '$lib/elements/forms';
import { sdk } from '$lib/stores/sdk';
import { organization } from '$lib/stores/organization';
import { addNotification } from '$lib/stores/notifications';
import { goto, invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
@@ -23,6 +22,7 @@
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';
import { getApexDomain } from '$lib/helpers/tlds.js';
let { data } = $props();
@@ -54,41 +54,30 @@
}
async function verify() {
const isNewDomain =
data.domainsList.domains.find((rule) => rule.domain === data.proxyRule.domain) ===
undefined;
try {
if (selectedTab !== 'nameserver') {
const ruleData = await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId });
verified = ruleData.status === 'verified';
const apexDomain = getApexDomain(data.proxyRule.domain);
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
if (ruleData.status === 'created') {
throw new Error(
'Domain verification failed. Please check your domain settings or try again later'
);
}
} else if (isNewDomain && isCloud) {
const domainData = await sdk.forConsole.domains.create({
teamId: $organization.$id,
domain: data.proxyRule.domain
});
verified = domainData.nameservers.toLowerCase() === 'appwrite';
}
if (verified) {
addNotification({
type: 'success',
message: 'Domain added successfully'
});
} else {
addNotification({
type: 'info',
message: 'Verification in progress'
if (isCloud && domain) {
await sdk.forConsole.domains.updateNameservers({
domainId: domain.$id
});
}
} catch (error) {
// Ignore error
}
try {
await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId });
verified = true;
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await goto(routeBase);
await invalidate(Dependencies.DOMAINS);
await invalidate(Dependencies.FUNCTION_DOMAINS);
@@ -172,7 +161,10 @@
<Divider />
</div>
{#if selectedTab === 'nameserver'}
<NameserverTable domain={data.proxyRule.domain} {verified} />
<NameserverTable
{verified}
domain={data.proxyRule.domain}
ruleStatus={data.proxyRule.status} />
{:else}
<RecordTable
{verified}
@@ -13,13 +13,16 @@
import { Divider, Tabs } from '@appwrite.io/pink-svelte';
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
import RecordTable from '$lib/components/domains/recordTable.svelte';
import { getApexDomain } from '$lib/helpers/tlds';
let {
show = $bindable(false),
selectedProxyRule
selectedProxyRule,
domainsList
}: {
show: boolean;
selectedProxyRule: Models.ProxyRule;
domainsList?: Models.DomainsList;
} = $props();
const showCNAMETab = $derived(
@@ -40,43 +43,43 @@
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 retryProxyRule() {
error = null;
try {
error = null;
const proxyRule = await sdk
const apexDomain = getApexDomain(selectedProxyRule.domain);
const domain = domainsList?.domains.find((d) => d.domain === apexDomain);
if (isCloud && domain) {
await sdk.forConsole.domains.updateNameservers({
domainId: domain.$id
});
}
} catch (error) {
// Ignore error
}
try {
await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId: selectedProxyRule.$id });
verified = proxyRule.status === 'verified';
verified = true;
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await invalidate(Dependencies.FUNCTION_DOMAINS);
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
if (proxyRule.status === 'created') {
throw new Error(
'Domain verification failed. Please check your domain settings or try again later'
);
}
if (verified) {
addNotification({
type: 'success',
message: `${selectedProxyRule.domain} has been verified`
});
} else {
addNotification({
type: 'info',
message: 'Verification in progress'
});
}
show = false;
trackEvent(Submit.DomainUpdateVerification);
} catch (e) {
verified = false;
error =
e.message ??
'Domain verification failed. Please check your domain settings or try again later';
@@ -130,7 +133,10 @@
<Divider />
</div>
{#if selectedTab === 'nameserver'}
<NameserverTable domain={selectedProxyRule.domain} {verified} />
<NameserverTable
{verified}
domain={selectedProxyRule.domain}
ruleStatus={selectedProxyRule.status} />
{:else}
<RecordTable
{verified}
@@ -222,7 +222,7 @@
{/if}
{#if showRetry}
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} />
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} domainsList={organizationDomains} />
{/if}
{#if showLogs}
@@ -30,16 +30,24 @@
async function addDomain() {
const apexDomain = getApexDomain(domainName);
let domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
const domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
if (apexDomain && !domain && isCloud) {
try {
domain = await sdk.forConsole.domains.create({
await sdk.forConsole.domains.create({
teamId: $project.teamId,
domain: apexDomain
});
} catch (error) {
// Apex domain creation error needs to be silent.
// apex might already be added on organization level, skip.
const alreadyAdded = error?.type === 'domain_already_exists';
if (!alreadyAdded) {
addNotification({
type: 'error',
message: error.message
});
return;
}
}
}
@@ -47,12 +55,18 @@
const rule = await sdk
.forProject(page.params.region, page.params.project)
.proxy.createAPIRule({ domain: domainName.toLocaleLowerCase() });
if (rule?.status === 'verified') {
await invalidate(Dependencies.DOMAINS);
const verified = rule?.status !== 'created';
if (verified) {
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await goto(routeBase);
await invalidate(Dependencies.DOMAINS);
} else {
await goto(`${routeBase}/add-domain/verify-${domainName}?rule=${rule.$id}`);
await invalidate(Dependencies.DOMAINS);
}
} catch (error) {
addNotification({
@@ -11,7 +11,6 @@
} from '@appwrite.io/pink-svelte';
import { Button, Form } from '$lib/elements/forms';
import { sdk } from '$lib/stores/sdk';
import { organization } from '$lib/stores/organization';
import { addNotification } from '$lib/stores/notifications';
import { goto, invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
@@ -23,6 +22,7 @@
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';
import { getApexDomain } from '$lib/helpers/tlds.js';
let { data } = $props();
@@ -55,41 +55,29 @@
}
async function verify() {
const isNewDomain =
data.domainsList.domains.find((rule) => rule.domain === data.proxyRule.domain) ===
undefined;
try {
if (selectedTab !== 'nameserver') {
const ruleData = await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId });
verified = ruleData.status === 'verified';
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
if (ruleData.status === 'created') {
throw new Error(
'Domain verification failed. Please check your domain settings or try again later'
);
}
} else if (isNewDomain && isCloud) {
const domainData = await sdk.forConsole.domains.create({
teamId: $organization.$id,
domain: data.proxyRule.domain
});
verified = domainData.nameservers.toLowerCase() === 'appwrite';
}
if (verified) {
addNotification({
type: 'success',
message: 'Domain added successfully'
});
} else {
addNotification({
type: 'info',
message: 'Verification in progress'
const apexDomain = getApexDomain(data.proxyRule.domain);
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
if (isCloud && domain) {
await sdk.forConsole.domains.updateNameservers({
domainId: domain.$id
});
}
} catch (error) {
// Ignore error
}
try {
await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId });
verified = true;
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await goto(routeBase);
await invalidate(Dependencies.DOMAINS);
} catch (error) {
@@ -13,13 +13,16 @@
import { Divider, Tabs } from '@appwrite.io/pink-svelte';
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
import RecordTable from '$lib/components/domains/recordTable.svelte';
import { getApexDomain } from '$lib/helpers/tlds';
let {
show = $bindable(),
selectedProxyRule
selectedProxyRule,
domainsList
}: {
show: boolean;
selectedProxyRule: Models.ProxyRule;
domainsList?: Models.DomainsList;
} = $props();
const showCNAMETab = $derived(
@@ -40,43 +43,43 @@
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() {
error = null;
try {
error = null;
const proxyRule = await sdk
const apexDomain = getApexDomain(selectedProxyRule.domain);
const domain = domainsList?.domains.find((d) => d.domain === apexDomain);
if (isCloud && domain) {
await sdk.forConsole.domains.updateNameservers({
domainId: domain.$id
});
}
} catch (error) {
// Ignore error
}
try {
await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId: selectedProxyRule.$id });
verified = proxyRule.status === 'verified';
verified = true;
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await invalidate(Dependencies.DOMAINS);
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
if (proxyRule.status === 'created') {
throw new Error(
'Domain verification failed. Please check your domain settings or try again later'
);
}
if (verified) {
addNotification({
type: 'success',
message: `${selectedProxyRule.domain} has been verified`
});
} else {
addNotification({
type: 'info',
message: 'Verification in progress'
});
}
show = false;
trackEvent(Submit.DomainUpdateVerification);
} catch (e) {
verified = false;
error =
e.message ??
'Domain verification failed. Please check your domain settings or try again later';
@@ -130,7 +133,10 @@
<Divider />
</div>
{#if selectedTab === 'nameserver'}
<NameserverTable domain={selectedProxyRule.domain} {verified} />
<NameserverTable
{verified}
domain={selectedProxyRule.domain}
ruleStatus={selectedProxyRule.status} />
{:else}
<RecordTable
{verified}
@@ -227,7 +227,7 @@
{/if}
{#if showRetry}
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} />
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} domainsList={organizationDomains} />
{/if}
{#if showLogs}
@@ -58,16 +58,24 @@
async function addDomain() {
const apexDomain = getApexDomain(domainName);
const isSiteDomain = domainName.endsWith($regionalConsoleVariables._APP_DOMAIN_SITES);
let domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
if (isCloud && apexDomain && !domain && !isSiteDomain) {
try {
domain = await sdk.forConsole.domains.create({
await sdk.forConsole.domains.create({
teamId: $project.teamId,
domain: apexDomain
});
} catch (error) {
// Apex domain creation error needs to be silent.
// apex might already be added on organization level, skip.
const alreadyAdded = error?.type === 'domain_already_exists';
if (!alreadyAdded) {
addNotification({
type: 'error',
message: error.message
});
return;
}
}
}
@@ -99,12 +107,18 @@
siteId: page.params.site
});
}
if (rule?.status === 'verified') {
await invalidate(Dependencies.SITES_DOMAINS);
const verified = rule?.status !== 'created';
if (verified) {
addNotification({
type: 'success',
message: 'Domain added successfully'
});
await goto(routeBase);
await invalidate(Dependencies.SITES_DOMAINS);
} else {
await goto(`${routeBase}/add-domain/verify-${domainName}?rule=${rule.$id}`);
await invalidate(Dependencies.SITES_DOMAINS);
}
} catch (error) {
addNotification({
@@ -11,7 +11,6 @@
} from '@appwrite.io/pink-svelte';
import { Button, Form } from '$lib/elements/forms';
import { sdk } from '$lib/stores/sdk';
import { organization } from '$lib/stores/organization';
import { addNotification } from '$lib/stores/notifications';
import { goto, invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
@@ -23,6 +22,7 @@
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';
import { getApexDomain } from '$lib/helpers/tlds.js';
let { data } = $props();
@@ -54,41 +54,29 @@
}
async function verify() {
const isNewDomain =
data.domainsList.domains.findIndex((rule) => rule.domain === data.proxyRule.domain) ===
-1;
try {
if (selectedTab !== 'nameserver') {
const ruleData = await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId });
verified = ruleData.status === 'verified';
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
if (ruleData.status === 'created') {
throw new Error(
'Domain verification failed. Please check your domain settings or try again later'
);
}
} else if (isNewDomain && isCloud) {
const domainData = await sdk.forConsole.domains.create({
teamId: $organization.$id,
domain: data.proxyRule.domain
});
verified = domainData.nameservers.toLowerCase() === 'appwrite';
}
if (verified) {
addNotification({
type: 'success',
message: 'Domain added successfully'
});
} else {
addNotification({
type: 'info',
message: 'Verification in progress'
const apexDomain = getApexDomain(data.proxyRule.domain);
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
if (isCloud && domain) {
await sdk.forConsole.domains.updateNameservers({
domainId: domain.$id
});
}
} catch (error) {
// Ignore error
}
try {
await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId });
verified = true;
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await goto(routeBase);
await invalidate(Dependencies.DOMAINS);
await invalidate(Dependencies.SITES_DOMAINS);
@@ -172,7 +160,10 @@
<Divider />
</div>
{#if selectedTab === 'nameserver'}
<NameserverTable domain={data.proxyRule.domain} {verified} />
<NameserverTable
{verified}
domain={data.proxyRule.domain}
ruleStatus={data.proxyRule.status} />
{:else}
<RecordTable
{verified}
@@ -13,13 +13,16 @@
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';
import { getApexDomain } from '$lib/helpers/tlds';
let {
show = $bindable(false),
selectedProxyRule
selectedProxyRule,
domainsList
}: {
show: boolean;
selectedProxyRule: Models.ProxyRule;
domainsList?: Models.DomainsList;
} = $props();
const showCNAMETab = $derived(
@@ -40,43 +43,43 @@
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() {
error = null;
try {
error = null;
const proxyRule = await sdk
const apexDomain = getApexDomain(selectedProxyRule.domain);
const domain = domainsList?.domains.find((d) => d.domain === apexDomain);
if (isCloud && domain) {
await sdk.forConsole.domains.updateNameservers({
domainId: domain.$id
});
}
} catch (error) {
// Ignore error
}
try {
await sdk
.forProject(page.params.region, page.params.project)
.proxy.updateRuleVerification({ ruleId: selectedProxyRule.$id });
verified = proxyRule.status === 'verified';
verified = true;
addNotification({
type: 'success',
message: 'Domain verified successfully'
});
await invalidate(Dependencies.SITES_DOMAINS);
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
if (proxyRule.status === 'created') {
throw new Error(
'Domain verification failed. Please check your domain settings or try again later'
);
}
if (verified) {
addNotification({
type: 'success',
message: `${selectedProxyRule.domain} has been verified`
});
} else {
addNotification({
type: 'info',
message: 'Verification in progress'
});
}
show = false;
trackEvent(Submit.DomainUpdateVerification);
} catch (e) {
verified = false;
error =
e.message ??
'Domain verification failed. Please check your domain settings or try again later';
@@ -130,7 +133,10 @@
<Divider />
</div>
{#if selectedTab === 'nameserver'}
<NameserverTable domain={selectedProxyRule.domain} {verified} />
<NameserverTable
{verified}
domain={selectedProxyRule.domain}
ruleStatus={selectedProxyRule.status} />
{:else}
<RecordTable
{verified}
@@ -222,7 +222,7 @@
{/if}
{#if showRetry}
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} />
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} domainsList={organizationDomains} />
{/if}
{#if showLogs}