mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #2974 from appwrite/feat-custom-domain-limits-enforcement
feat: enforce custom domain plan limits in UI
This commit is contained in:
@@ -24,7 +24,6 @@
|
||||
"deep-equal": "^2.2.3",
|
||||
"echarts": "^5.6.0",
|
||||
"flatted": "^3.4.2",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@67539a6",
|
||||
"ignore": "^6.0.2",
|
||||
"nanoid": "^5.1.7",
|
||||
"nanotar": "^0.3.0",
|
||||
@@ -41,7 +40,7 @@
|
||||
"@melt-ui/svelte": "^0.86.6",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/kit": "^2.55.0",
|
||||
"@sveltejs/kit": "^2.57.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.1.1",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
@@ -435,7 +434,7 @@
|
||||
|
||||
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.10", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew=="],
|
||||
|
||||
"@sveltejs/kit": ["@sveltejs/kit@2.55.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.4", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-MdFRjevVxmAknf2NbaUkDF16jSIzXMWd4Nfah0Qp8TtQVoSp3bV4jKt8mX7z7qTUTWvgSaxtR0EG5WJf53gcuA=="],
|
||||
"@sveltejs/kit": ["@sveltejs/kit@2.57.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.4", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-VRdSbB96cI1EnRh09CqmnQqP/YJvET5buj8S6k7CxaJqBJD4bw4fRKDjcarAj/eX9k2eHifQfDH8NtOh+ZxxPw=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ=="],
|
||||
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@
|
||||
"@melt-ui/svelte": "^0.86.6",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/kit": "^2.55.0",
|
||||
"@sveltejs/kit": "^2.57.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.1.1",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
|
||||
@@ -86,6 +86,18 @@
|
||||
{formatNum(currentPlan.executions)} executions
|
||||
</span>
|
||||
</li>
|
||||
{#if currentPlan.domains > 0}
|
||||
<li class="list-item u-gap-4 u-cross-center">
|
||||
<span class="icon-arrow-down u-color-text-danger" aria-hidden="true"></span>
|
||||
<span class="text">
|
||||
Limited to {currentPlan.domains} custom {pluralize(
|
||||
currentPlan.domains,
|
||||
'domain'
|
||||
)}
|
||||
per project
|
||||
</span>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
{:else}
|
||||
<ul class="un-order-list">
|
||||
@@ -105,12 +117,22 @@
|
||||
<li>
|
||||
Limited to {formatNum(currentPlan.executions)} executions
|
||||
</li>
|
||||
{#if currentPlan.domains > 0}
|
||||
<li>
|
||||
Limited to {currentPlan.domains} custom {pluralize(
|
||||
currentPlan.domains,
|
||||
'domain'
|
||||
)} per project
|
||||
</li>
|
||||
{:else}
|
||||
<li>Unlimited custom domains</li>
|
||||
{/if}
|
||||
</ul>
|
||||
{/if}
|
||||
{:else if planHasGroup(selectedTab, BillingPlanGroup.Pro)}
|
||||
<Typography.Text>Everything in the Free plan, plus:</Typography.Text>
|
||||
<ul class="un-order-list">
|
||||
<li>Unlimited databases, buckets, functions</li>
|
||||
<li>Unlimited databases, buckets, functions, and custom domains</li>
|
||||
<li>Unlimited seats</li>
|
||||
<li>{currentPlan.bandwidth}GB bandwidth</li>
|
||||
<li>{currentPlan.storage}GB storage</li>
|
||||
|
||||
@@ -200,6 +200,7 @@ export type PlanServices =
|
||||
| 'bandwidthAddon'
|
||||
| 'buckets'
|
||||
| 'databases'
|
||||
| 'domains'
|
||||
| 'executions'
|
||||
| 'executionsAddon'
|
||||
| 'fileSize'
|
||||
@@ -316,6 +317,7 @@ export function checkForProjectLimitation(plan: string, id: PlanServices) {
|
||||
|
||||
switch (id) {
|
||||
case 'databases':
|
||||
case 'domains':
|
||||
case 'functions':
|
||||
case 'buckets':
|
||||
case 'members': // Only applies to Free plan now
|
||||
|
||||
@@ -34,11 +34,11 @@
|
||||
import { View } from '$lib/helpers/load';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
export let data;
|
||||
let { data } = $props();
|
||||
|
||||
let showDelete = false;
|
||||
let showRetry = false;
|
||||
let selectedDomain: Models.Domain = null;
|
||||
let showDelete = $state(false);
|
||||
let showRetry = $state(false);
|
||||
let selectedDomain = $state<Models.Domain | null>(null);
|
||||
|
||||
const isDomainVerified = (domain: Models.Domain) => {
|
||||
return domain.nameservers.toLowerCase() === 'appwrite';
|
||||
|
||||
+51
-17
@@ -6,16 +6,26 @@
|
||||
import Container from '$lib/layout/container.svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Card, Empty, Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Card, Empty, Icon, Layout, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import DeleteDomainModal from './deleteDomainModal.svelte';
|
||||
import RetryDomainModal from './retryDomainModal.svelte';
|
||||
import SearchQuery from '$lib/components/searchQuery.svelte';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import {
|
||||
BODY_TOOLTIP_MAX_WIDTH,
|
||||
BODY_TOOLTIP_WRAPPER_STYLE_PRELINE
|
||||
} from '$lib/helpers/tooltipContent';
|
||||
import { isServiceLimited } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import Table from './table.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
const isDomainLimitReached = $derived(
|
||||
isServiceLimited('domains', $organization, data.proxyRules.total)
|
||||
);
|
||||
|
||||
let showDelete = $state(false);
|
||||
let showRetry = $state(false);
|
||||
let selectedProxyRule: Models.ProxyRule = null;
|
||||
@@ -24,16 +34,28 @@
|
||||
<Container>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<SearchQuery placeholder="Search domain" />
|
||||
<Button
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
|
||||
on:click={() => {
|
||||
trackEvent(Click.DomainCreateClick, {
|
||||
source: 'functions_domain_overview'
|
||||
});
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" />
|
||||
Add domain
|
||||
</Button>
|
||||
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
|
||||
<div>
|
||||
<Button
|
||||
disabled={isDomainLimitReached}
|
||||
href={isDomainLimitReached
|
||||
? undefined
|
||||
: `${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
|
||||
on:click={() => {
|
||||
trackEvent(Click.DomainCreateClick, {
|
||||
source: 'functions_domain_overview'
|
||||
});
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" />
|
||||
Add domain
|
||||
</Button>
|
||||
</div>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
|
||||
You have reached the maximum number of custom domains for your plan.
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
{#if data.proxyRules.total}
|
||||
<Table proxyRules={data.proxyRules} organizationDomains={data.organizationDomains} />
|
||||
@@ -70,12 +92,24 @@
|
||||
size="s"
|
||||
ariaLabel="add domain">Documentation</Button>
|
||||
|
||||
<Button
|
||||
secondary
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
|
||||
size="s">
|
||||
Add domain
|
||||
</Button>
|
||||
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
|
||||
<div>
|
||||
<Button
|
||||
secondary
|
||||
disabled={isDomainLimitReached}
|
||||
href={isDomainLimitReached
|
||||
? undefined
|
||||
: `${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains/add-domain`}
|
||||
size="s">
|
||||
Add domain
|
||||
</Button>
|
||||
</div>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
|
||||
You have reached the maximum number of custom domains for your plan.
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
|
||||
@@ -5,13 +5,23 @@
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import Container from '$lib/layout/container.svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Card, Empty, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { Card, Empty, Icon, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import {
|
||||
BODY_TOOLTIP_MAX_WIDTH,
|
||||
BODY_TOOLTIP_WRAPPER_STYLE_PRELINE
|
||||
} from '$lib/helpers/tooltipContent';
|
||||
import { isServiceLimited } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import Table from './table.svelte';
|
||||
import { ResponsiveContainerHeader } from '$lib/layout';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
const isDomainLimitReached = $derived(
|
||||
isServiceLimited('domains', $organization, data.rules.total)
|
||||
);
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
@@ -20,16 +30,28 @@
|
||||
hideView
|
||||
searchPlaceholder="Search by domain"
|
||||
analyticsSource="settings_domain_overview">
|
||||
<Button
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
|
||||
on:click={() => {
|
||||
trackEvent(Click.DomainCreateClick, {
|
||||
source: 'settings_domain_overview'
|
||||
});
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" />
|
||||
Add domain
|
||||
</Button>
|
||||
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
|
||||
<div>
|
||||
<Button
|
||||
disabled={isDomainLimitReached}
|
||||
href={isDomainLimitReached
|
||||
? undefined
|
||||
: `${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
|
||||
on:click={() => {
|
||||
trackEvent(Click.DomainCreateClick, {
|
||||
source: 'settings_domain_overview'
|
||||
});
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" />
|
||||
Add domain
|
||||
</Button>
|
||||
</div>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
|
||||
You have reached the maximum number of custom domains for your plan.
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</ResponsiveContainerHeader>
|
||||
{#if data.rules.total}
|
||||
<Table domains={data.rules} organizationDomains={data.organizationDomains} />
|
||||
@@ -64,12 +86,24 @@
|
||||
size="s"
|
||||
ariaLabel="add domain">Documentation</Button>
|
||||
|
||||
<Button
|
||||
secondary
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
|
||||
size="s">
|
||||
Add domain
|
||||
</Button>
|
||||
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
|
||||
<div>
|
||||
<Button
|
||||
secondary
|
||||
disabled={isDomainLimitReached}
|
||||
href={isDomainLimitReached
|
||||
? undefined
|
||||
: `${base}/project-${page.params.region}-${page.params.project}/settings/domains/add-domain`}
|
||||
size="s">
|
||||
Add domain
|
||||
</Button>
|
||||
</div>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
|
||||
You have reached the maximum number of custom domains for your plan.
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
|
||||
+52
-18
@@ -5,28 +5,50 @@
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import Container from '$lib/layout/container.svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Card, Empty, Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Card, Empty, Icon, Layout, Tooltip } from '@appwrite.io/pink-svelte';
|
||||
import SearchQuery from '$lib/components/searchQuery.svelte';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import {
|
||||
BODY_TOOLTIP_MAX_WIDTH,
|
||||
BODY_TOOLTIP_WRAPPER_STYLE_PRELINE
|
||||
} from '$lib/helpers/tooltipContent';
|
||||
import { isServiceLimited } from '$lib/stores/billing';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import Table from './table.svelte';
|
||||
|
||||
export let data;
|
||||
let { data } = $props();
|
||||
|
||||
const isDomainLimitReached = $derived(
|
||||
isServiceLimited('domains', $organization, data.proxyRules.total)
|
||||
);
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<SearchQuery placeholder="Search domain" />
|
||||
<Button
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/domains/add-domain`}
|
||||
on:click={() => {
|
||||
trackEvent(Click.DomainCreateClick, {
|
||||
source: 'sites_domain_overview'
|
||||
});
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" />
|
||||
Add domain
|
||||
</Button>
|
||||
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
|
||||
<div>
|
||||
<Button
|
||||
disabled={isDomainLimitReached}
|
||||
href={isDomainLimitReached
|
||||
? undefined
|
||||
: `${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/domains/add-domain`}
|
||||
on:click={() => {
|
||||
trackEvent(Click.DomainCreateClick, {
|
||||
source: 'sites_domain_overview'
|
||||
});
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" />
|
||||
Add domain
|
||||
</Button>
|
||||
</div>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
|
||||
You have reached the maximum number of custom domains for your plan.
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if data.proxyRules.total}
|
||||
@@ -62,12 +84,24 @@
|
||||
size="s"
|
||||
ariaLabel="add domain">Documentation</Button>
|
||||
|
||||
<Button
|
||||
secondary
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/domains/add-domain`}
|
||||
size="s">
|
||||
Add domain
|
||||
</Button>
|
||||
<Tooltip disabled={!isDomainLimitReached} maxWidth={BODY_TOOLTIP_MAX_WIDTH}>
|
||||
<div>
|
||||
<Button
|
||||
secondary
|
||||
disabled={isDomainLimitReached}
|
||||
href={isDomainLimitReached
|
||||
? undefined
|
||||
: `${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/domains/add-domain`}
|
||||
size="s">
|
||||
Add domain
|
||||
</Button>
|
||||
</div>
|
||||
<svelte:fragment slot="tooltip">
|
||||
<div style={BODY_TOOLTIP_WRAPPER_STYLE_PRELINE}>
|
||||
You have reached the maximum number of custom domains for your plan.
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
</svelte:fragment>
|
||||
</Empty>
|
||||
</Card.Base>
|
||||
|
||||
Reference in New Issue
Block a user