diff --git a/package.json b/package.json index 851cb44908..83b7b7981c 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,8 @@ "typescript": "^5.8.2", "typescript-eslint": "^8.30.1", "vite": "^6.2.3", - "vitest": "^3.0.0" + "vitest": "^3.0.0", + "tldts": "^7.0.7" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92351d55c4..b14eb716f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -177,6 +177,9 @@ importers: svelte-sequential-preprocessor: specifier: ^2.0.2 version: 2.0.2 + tldts: + specifier: ^7.0.7 + version: 7.0.7 tslib: specifier: ^2.8.1 version: 2.8.1 @@ -3239,10 +3242,17 @@ packages: tldts-core@6.1.85: resolution: {integrity: sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==} + tldts-core@7.0.7: + resolution: {integrity: sha512-ECqb8imSroX1UmUuhRBNPkkmtZ8mHEenieim80UVxG0M5wXVjY2Fp2tYXCPvk+nLy1geOhFpeD5YQhM/gF63Jg==} + tldts@6.1.85: resolution: {integrity: sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==} hasBin: true + tldts@7.0.7: + resolution: {integrity: sha512-ETNXj36ql5BXDa4VVJk3wgqansg8TI1Yqo217twSAPjyDnh/b2T+XzrI0ftn6EnzVPbXpMTZHOWj5s3a8/uGPA==} + hasBin: true + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -6834,10 +6844,16 @@ snapshots: tldts-core@6.1.85: {} + tldts-core@7.0.7: {} + tldts@6.1.85: dependencies: tldts-core: 6.1.85 + tldts@7.0.7: + dependencies: + tldts-core: 7.0.7 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 diff --git a/src/lib/helpers/tlds.ts b/src/lib/helpers/tlds.ts new file mode 100644 index 0000000000..23adffb4cb --- /dev/null +++ b/src/lib/helpers/tlds.ts @@ -0,0 +1,21 @@ +import { parse } from 'tldts'; + +/** + * Returns the apex/root domain from a full domain string. + */ +export function getApexDomain(domain: string): string | null { + return parse(domain).domain; +} + +/** + * Checks whether the given domain is a subdomain. + */ +export function isASubdomain(domain: string | null): boolean { + if (!domain) return false; + + const { domain: apex, subdomain } = parse(domain); + if (!apex) return false; + if (subdomain === 'www') return false; + + return !!subdomain; +} diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/+page.svelte index dd26bd21fd..e7a55a9566 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/+page.svelte @@ -20,6 +20,7 @@ import { isValueOfStringEnum } from '$lib/helpers/types.js'; import { isCloud } from '$lib/system'; import { project } from '$routes/(console)/project-[region]-[project]/store'; + import { getApexDomain } from '$lib/helpers/tlds'; const routeBase = `${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains`; @@ -47,14 +48,12 @@ }); async function addDomain() { - let domain = data.domains?.domains.find((d) => d.domain === domainName); + const apexDomain = getApexDomain(domainName); + let domain = data.domains?.domains.find((d) => d.domain === apexDomain); - if (!domain && isCloud) { + if (apexDomain && !domain && isCloud) { try { - domain = await sdk.forConsole.domains.create( - $project.teamId, - domainName.toLocaleLowerCase() - ); + domain = await sdk.forConsole.domains.create($project.teamId, apexDomain); } catch (error) { addNotification({ type: 'error', diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/verify-[domain]/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/verify-[domain]/+page.svelte index f8b03eb861..be84fec9fa 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/verify-[domain]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/domains/add-domain/verify-[domain]/+page.svelte @@ -20,6 +20,7 @@ import Wizard from '$lib/layout/wizard.svelte'; import { base } from '$app/paths'; import { writable } from 'svelte/store'; + import { isASubdomain } from '$lib/helpers/tlds'; import { consoleVariables } from '$routes/(console)/store'; import NameserverTable from '$lib/components/domains/nameserverTable.svelte'; import RecordTable from '$lib/components/domains/recordTable.svelte'; @@ -27,7 +28,7 @@ let { data } = $props(); const ruleId = page.url.searchParams.get('rule'); - let isSubDomain = $derived(page.params.domain?.split('.')?.length >= 3); + const isSubDomain = $derived.by(() => isASubdomain(page.params.domain)); let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>('nameserver'); $effect(() => { diff --git a/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/+page.svelte b/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/+page.svelte index 09ff919995..781c68bb2a 100644 --- a/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/+page.svelte @@ -11,6 +11,7 @@ import { onMount } from 'svelte'; import { isCloud } from '$lib/system'; import { project } from '$routes/(console)/project-[region]-[project]/store'; + import { getApexDomain } from '$lib/helpers/tlds'; const routeBase = `${base}/project-${page.params.region}-${page.params.project}/settings/domains`; @@ -27,14 +28,12 @@ }); async function addDomain() { - let domain = data.domains?.domains.find((d) => d.domain === domainName); + const apexDomain = getApexDomain(domainName); + let domain = data.domains?.domains.find((d) => d.domain === apexDomain); - if (!domain && isCloud) { + if (apexDomain && !domain && isCloud) { try { - domain = await sdk.forConsole.domains.create( - $project.teamId, - domainName.toLocaleLowerCase() - ); + domain = await sdk.forConsole.domains.create($project.teamId, apexDomain); } catch (error) { addNotification({ type: 'error', @@ -48,7 +47,7 @@ try { const rule = await sdk .forProject(page.params.region, page.params.project) - .proxy.createAPIRule(domainName); + .proxy.createAPIRule(domainName.toLocaleLowerCase()); if (rule?.status === 'verified') { await goto(routeBase); await invalidate(Dependencies.DOMAINS); diff --git a/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/verify-[domain]/+page.svelte b/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/verify-[domain]/+page.svelte index ac027c30b8..9866c94f08 100644 --- a/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/verify-[domain]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/settings/domains/add-domain/verify-[domain]/+page.svelte @@ -20,6 +20,7 @@ import Wizard from '$lib/layout/wizard.svelte'; import { base } from '$app/paths'; import { writable } from 'svelte/store'; + import { isASubdomain } from '$lib/helpers/tlds'; import { consoleVariables } from '$routes/(console)/store'; import NameserverTable from '$lib/components/domains/nameserverTable.svelte'; import RecordTable from '$lib/components/domains/recordTable.svelte'; @@ -27,7 +28,7 @@ let { data } = $props(); const ruleId = page.url.searchParams.get('rule'); - let isSubDomain = $derived(page.params.domain?.split('.')?.length >= 3); + const isSubDomain = $derived.by(() => isASubdomain(page.params.domain)); let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>('nameserver'); $effect(() => { diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/+page.svelte index 3b053e49fe..c57b7c2b68 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/+page.svelte @@ -25,6 +25,7 @@ import { ConnectRepoModal } from '$lib/components/git/index.js'; import { project } from '$routes/(console)/project-[region]-[project]/store'; import { isCloud } from '$lib/system'; + import { getApexDomain } from '$lib/helpers/tlds'; const routeBase = `${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/domains`; @@ -52,14 +53,12 @@ }); async function addDomain() { - let domain = data.domains?.domains.find((d) => d.domain === domainName); + const apexDomain = getApexDomain(domainName); + let domain = data.domains?.domains.find((d) => d.domain === apexDomain); - if (!domain && isCloud) { + if (apexDomain && !domain && isCloud) { try { - domain = await sdk.forConsole.domains.create( - $project.teamId, - domainName.toLocaleLowerCase() - ); + domain = await sdk.forConsole.domains.create($project.teamId, apexDomain); } catch (error) { addNotification({ type: 'error', diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/verify-[domain]/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/verify-[domain]/+page.svelte index cc27aa40bb..108d34030f 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/verify-[domain]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/domains/add-domain/verify-[domain]/+page.svelte @@ -20,6 +20,7 @@ import Wizard from '$lib/layout/wizard.svelte'; import { base } from '$app/paths'; import { writable } from 'svelte/store'; + import { isASubdomain } from '$lib/helpers/tlds'; import { consoleVariables } from '$routes/(console)/store'; import NameserverTable from '$lib/components/domains/nameserverTable.svelte'; import RecordTable from '$lib/components/domains/recordTable.svelte'; @@ -27,9 +28,10 @@ let { data } = $props(); const ruleId = page.url.searchParams.get('rule'); - let isSubDomain = $derived(page.params.domain?.split('.')?.length >= 3); + const isSubDomain = $derived.by(() => isASubdomain(page.params.domain)); let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>('nameserver'); + $effect(() => { if ($consoleVariables._APP_DOMAIN_TARGET_CNAME && isSubDomain) { selectedTab = 'cname'; 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 f18c1b664b..389cbc2f7f 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 @@ -11,6 +11,7 @@ import { consoleVariables } from '$routes/(console)/store'; 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'; @@ -22,9 +23,7 @@ selectedDomain: Models.ProxyRule; } = $props(); - let isSubDomain = $derived.by(() => - selectedDomain?.domain?.length ? selectedDomain?.domain?.split('.')?.length >= 3 : false - ); + const isSubDomain = $derived.by(() => isASubdomain(selectedDomain?.domain)); let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>('nameserver'); $effect(() => {