From 6ef9ad75490844d1d663f6f2f53b1484eddd7b5b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 20 Aug 2026 03:35:43 +0000 Subject: [PATCH] fix(sites): block GitHub repo create when site limit reached Check the plan site limit before calling vcs.createRepository in the template wizard, and disable Create site on the list page when limited, so users are not left with empty GitHub repositories after a failed deploy. Fixes #2175 Co-authored-by: chiragaggarwal5k --- .../sites/+page.svelte | 40 ++++++++++++++----- .../template-[template]/+page.svelte | 18 ++++++++- .../templates/template-[template]/+page.ts | 13 +++--- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/sites/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/+page.svelte index e9ffd7c38d..e0c891f00c 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/+page.svelte @@ -11,7 +11,7 @@ import { isServiceLimited } from '$lib/stores/billing'; import { organization } from '$lib/stores/organization'; import { canWriteSites } from '$lib/stores/roles.js'; - import { Icon, Layout } from '@appwrite.io/pink-svelte'; + import { Icon, Layout, Tooltip } from '@appwrite.io/pink-svelte'; import { Button } from '$lib/elements/forms'; import { app } from '$lib/stores/app'; import CreateSiteModal from './createSiteModal.svelte'; @@ -27,11 +27,17 @@ import { Dependencies } from '$lib/constants'; import { realtime } from '$lib/stores/sdk'; import { page } from '$app/state'; + import { + BODY_TOOLTIP_MAX_WIDTH, + BODY_TOOLTIP_WRAPPER_STYLE_PRELINE + } from '$lib/helpers/tooltipContent'; export let data; let show = false; + $: isLimited = isServiceLimited('sites', $organization, data.siteList?.total); + $: $registerCommands([ { label: 'Create site', @@ -39,8 +45,7 @@ show = true; }, keys: ['c'], - disabled: - isServiceLimited('sites', $organization, data.siteList?.total) || !$canWriteSites, + disabled: isLimited || !$canWriteSites, icon: IconPlus, group: 'sites' } @@ -70,10 +75,25 @@ hideColumns hideView={!data.siteList.total} /> {#if $canWriteSites} - + +
+ +
+ +
+ You have reached the maximum number of sites for your plan. +
+
+
{/if} @@ -93,12 +113,14 @@ {:else} (show = true)}> + on:click={() => { + if (!isLimited) show = true; + }}> {/if} diff --git a/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.svelte b/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.svelte index 006003d846..ac1afb4fe3 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.svelte @@ -46,9 +46,15 @@ import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store'; import { getTemplateSourceUrl } from '$lib/helpers/templateSource'; import { validateVariables } from '$lib/helpers/variables'; + import { isServiceLimited } from '$lib/stores/billing'; + import { organization } from '$lib/stores/organization'; + import { isCloud } from '$lib/system'; export let data; + $: sitesLimited = + isCloud && isServiceLimited('sites', $organization, data.siteList?.total ?? 0); + let showExitModal = false; let isCreatingRepository = false; let hasInstallations = !!data?.installations?.total; @@ -81,6 +87,15 @@ }); async function createRepository() { + if (sitesLimited) { + addNotification({ + type: 'error', + message: + 'The maximum number of sites allowed for the selected plan has been reached. Upgrade to increase the limit.' + }); + return; + } + try { isCreatingRepository = true; const repo = await sdk @@ -294,7 +309,8 @@ on:click={createRepository} forceShowLoader submissionLoader={isCreatingRepository} - disabled={!repositoryName || + disabled={sitesLimited || + !repositoryName || !$installation?.$id || isCreatingRepository}> Create diff --git a/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.ts b/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.ts index 5a9948ba65..f5059d0d7d 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.ts +++ b/src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.ts @@ -1,13 +1,15 @@ import { sdk } from '$lib/stores/sdk'; -import { ID } from '@appwrite.io/console'; +import { ID, Query } from '@appwrite.io/console'; import { buildVerboseDomain } from '../../store.js'; export const load = async ({ parent, params }) => { const { installations, frameworks, project, organization, regionalConsoleVariables } = await parent(); - const template = await sdk - .forProject(params.region, params.project) - .sites.getTemplate({ templateId: params.template }); + const projectSdk = sdk.forProject(params.region, params.project); + const [template, siteList] = await Promise.all([ + projectSdk.sites.getTemplate({ templateId: params.template }), + projectSdk.sites.list({ queries: [Query.limit(1)] }) + ]); const domain = await buildVerboseDomain( regionalConsoleVariables._APP_DOMAIN_SITES, template.name, @@ -20,6 +22,7 @@ export const load = async ({ parent, params }) => { installations, frameworks, template, - domain + domain, + siteList }; };