diff --git a/bun.lock b/bun.lock index c85d19e7c4..ee0f40a142 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "@appwrite/console", "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@7789ae4", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@2642dc5", "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bfe7ce3", "@appwrite.io/pink-legacy": "^1.0.3", @@ -125,7 +125,7 @@ "@analytics/type-utils": ["@analytics/type-utils@0.6.4", "", {}, "sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw=="], - "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@7789ae4", { "dependencies": { "json-bigint": "1.0.0" } }], + "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@2642dc5", { "dependencies": { "json-bigint": "1.0.0" } }], "@appwrite.io/pink-icons": ["@appwrite.io/pink-icons@0.25.0", "", {}, "sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q=="], diff --git a/package.json b/package.json index c1b9df8208..de34cd4cc3 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@7789ae4", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@2642dc5", "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bfe7ce3", "@appwrite.io/pink-legacy": "^1.0.3", diff --git a/src/routes/(console)/account/payments/addressModal.svelte b/src/routes/(console)/account/payments/addressModal.svelte index 57ea22c101..af186cb103 100644 --- a/src/routes/(console)/account/payments/addressModal.svelte +++ b/src/routes/(console)/account/payments/addressModal.svelte @@ -10,7 +10,7 @@ import type { Models } from '@appwrite.io/console'; export let show = false; - export let locale: Models.Locale; + export let locale: Models.CloudLocale; export let organization: string = null; export let countryList: Models.CountryList; diff --git a/src/routes/(console)/account/payments/billingAddress.svelte b/src/routes/(console)/account/payments/billingAddress.svelte index 5e9b888376..703ad7e902 100644 --- a/src/routes/(console)/account/payments/billingAddress.svelte +++ b/src/routes/(console)/account/payments/billingAddress.svelte @@ -29,7 +29,7 @@ export let data: PageData; - const locale: Models.Locale = data.locale; + const locale: Models.CloudLocale = data.locale; const countryList: Models.CountryList = data.countryList; let show = false; diff --git a/src/routes/(console)/account/payments/editAddressModal.svelte b/src/routes/(console)/account/payments/editAddressModal.svelte index 891b65103f..0fc1fdad9a 100644 --- a/src/routes/(console)/account/payments/editAddressModal.svelte +++ b/src/routes/(console)/account/payments/editAddressModal.svelte @@ -10,7 +10,7 @@ import type { Models } from '@appwrite.io/console'; export let show = false; - export let locale: Models.Locale; + export let locale: Models.CloudLocale; export let countryList: Models.CountryList; export let selectedAddress: Models.BillingAddress; diff --git a/src/routes/(console)/organization-[organization]/billing/billingAddress.svelte b/src/routes/(console)/organization-[organization]/billing/billingAddress.svelte index ebecd52319..6dbc5bfabb 100644 --- a/src/routes/(console)/organization-[organization]/billing/billingAddress.svelte +++ b/src/routes/(console)/organization-[organization]/billing/billingAddress.svelte @@ -22,7 +22,7 @@ } from '@appwrite.io/pink-icons-svelte'; import type { Models } from '@appwrite.io/console'; - export let locale: Models.Locale; + export let locale: Models.CloudLocale; export let countryList: Models.CountryList; export let organization: Models.Organization; export let billingAddress: Models.BillingAddress; diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index cc56a826cc..d8e9d5be82 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -295,10 +295,6 @@ }; // addons (additional members, projects, etc.) - const billingAddonNames: Record = { - addon_baa: 'HIPAA BAA' - }; - const addons = (currentAggregation?.resources || []) .filter( (r) => @@ -315,8 +311,8 @@ ? 'Additional members' : addon.resourceId === 'projects' ? 'Additional projects' - : (billingAddonNames[addon.resourceId] ?? - `${addon.resourceId} overage (${formatNum(addon.value)})`), + : addon.name || + `${addon.resourceId} overage (${formatNum(addon.value)})`, usage: '', price: formatCurrency(addon.amount) }, @@ -462,6 +458,18 @@ priceFormatter: ({ amount }) => formatCurrency(amount), includeProgress: false }), + ...resources + .filter((r) => r.resourceId?.startsWith('addon_') && (r.amount ?? 0) > 0) + .map((addon) => + createRow({ + id: `addon-${addon.resourceId}`, + label: addon.name || addon.resourceId, + resource: addon, + usageFormatter: ({ value }) => formatNum(value), + priceFormatter: ({ amount }) => formatCurrency(amount), + includeProgress: false + }) + ), createRow({ id: 'usage-details', label: `Usage details`, diff --git a/src/routes/(console)/organization-[organization]/billing/replaceAddress.svelte b/src/routes/(console)/organization-[organization]/billing/replaceAddress.svelte index 56798f55b3..926f21319e 100644 --- a/src/routes/(console)/organization-[organization]/billing/replaceAddress.svelte +++ b/src/routes/(console)/organization-[organization]/billing/replaceAddress.svelte @@ -13,7 +13,7 @@ import type { Models } from '@appwrite.io/console'; export let show = false; - export let locale: Models.Locale; + export let locale: Models.CloudLocale; export let countryList: Models.CountryList; let loading = true; diff --git a/src/routes/(console)/organization-[organization]/settings/Soc2.svelte b/src/routes/(console)/organization-[organization]/settings/Soc2.svelte index 167a802613..7d71dbd609 100644 --- a/src/routes/(console)/organization-[organization]/settings/Soc2.svelte +++ b/src/routes/(console)/organization-[organization]/settings/Soc2.svelte @@ -5,7 +5,7 @@ import type { Models } from '@appwrite.io/console'; let show = false; - export let locale: Models.Locale; + export let locale: Models.CloudLocale; export let countryList: Models.CountryList; diff --git a/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte b/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte index c4b3b645d1..34331de126 100644 --- a/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte +++ b/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte @@ -11,7 +11,7 @@ import type { Models } from '@appwrite.io/console'; export let show = false; - export let locale: Models.Locale; + export let locale: Models.CloudLocale; export let countryList: Models.CountryList; let email = ''; diff --git a/src/routes/(console)/project-[region]-[project]/settings/+page.svelte b/src/routes/(console)/project-[region]-[project]/settings/+page.svelte index 3961307590..9cb63b24ed 100644 --- a/src/routes/(console)/project-[region]-[project]/settings/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/settings/+page.svelte @@ -18,6 +18,8 @@ import UpdateVariables from '../updateVariables.svelte'; import { page } from '$app/state'; import UpdateLabels from './updateLabels.svelte'; + import PremiumGeoDB from './premiumGeoDB.svelte'; + import { isCloud } from '$lib/system'; import type { PageData } from './$types'; import { Alert } from '@appwrite.io/pink-svelte'; @@ -97,6 +99,9 @@ + {#if isCloud && $canWriteProjects} + + {/if} { depends(Dependencies.PROJECT_VARIABLES); depends(Dependencies.PROJECT_INSTALLATIONS); + depends(Dependencies.ADDONS); const limit = PAGE_LIMIT; const offset = Number(url.searchParams.get('offset') ?? 0); const variablesOffset = Number(url.searchParams.get('variablesOffset') ?? 0); const projectSdk = sdk.forProject(params.region, params.project); - const [variablesResult, installationsResult] = await Promise.allSettled([ - projectSdk.projectApi.listVariables({ - queries: [Query.limit(limit), Query.offset(variablesOffset)] - }), - projectSdk.vcs.listInstallations({ - queries: [Query.limit(limit), Query.offset(offset)] - }) - ]); + const [variablesResult, installationsResult, addonsResult, addonPriceResult] = + await Promise.allSettled([ + projectSdk.projectApi.listVariables({ + queries: [Query.limit(limit), Query.offset(variablesOffset)] + }), + projectSdk.vcs.listInstallations({ + queries: [Query.limit(limit), Query.offset(offset)] + }), + isCloud + ? sdk + .forConsoleIn(params.region) + .projects.listAddons({ projectId: params.project }) + .catch(() => null) + : Promise.resolve(null), + isCloud + ? sdk + .forConsoleIn(params.region) + .projects.getAddonPrice({ + projectId: params.project, + addon: Addon.Premiumgeodb + }) + .catch(() => null) + : Promise.resolve(null) + ]); const variables = variablesResult.status === 'fulfilled' ? variablesResult.value : (() => { - // Read-only users can be blocked from write-adjacent settings APIs. - // Only silence those permission errors so genuine load failures still surface. if (!isReadonlySettingsPermissionError(variablesResult.reason)) { throw variablesResult.reason; } @@ -45,8 +61,6 @@ export const load: PageLoad = async ({ depends, url, params }) => { installationsResult.status === 'fulfilled' ? installationsResult.value : (() => { - // Read-only users can be blocked from write-adjacent settings APIs. - // Only silence those permission errors so genuine load failures still surface. if (!isReadonlySettingsPermissionError(installationsResult.reason)) { throw installationsResult.reason; } @@ -57,11 +71,16 @@ export const load: PageLoad = async ({ depends, url, params }) => { }; })(); + const addons = addonsResult.status === 'fulfilled' ? addonsResult.value : null; + const addonPrice = addonPriceResult.status === 'fulfilled' ? addonPriceResult.value : null; + return { limit, offset, variablesOffset, variables, - installations + installations, + addons, + addonPrice }; }; diff --git a/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDB.svelte b/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDB.svelte new file mode 100644 index 0000000000..7d8a0b0739 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDB.svelte @@ -0,0 +1,258 @@ + + + + Premium Geo DB + Give your project richer location intelligence on every user. Tailor pricing by region, surface the + right currency and language, comply with local regulations, and spot suspicious sign-ins before they + cause damage — all without leaving Appwrite. + + +
+ Premium Geo DB +
+ {#if !planSupportsPremiumGeoDB && canUpgradeToPremiumGeoDB} +

+ Premium Geo DB is not available on your current plan. Upgrade your plan to + enable it. +

+ + {:else if !planSupportsPremiumGeoDB} +

+ Premium Geo DB is not available on your current plan. +

+ {:else if isPending} +
+ +
+

+ A payment is awaiting confirmation. If you've completed authentication, click + refresh to check the payment status. +

+ + {:else if isActive} +
+ {#if isScheduledForRemoval} + + {:else} + + {/if} +
+

+ {#if monthlyPriceLabel} + Premium Geo DB is enabled for this project at {monthlyPriceLabel}/month. + {:else} + Premium Geo DB is enabled for this project. + {/if} +

+ {#if isScheduledForRemoval} +

+ Premium Geo DB will be removed at the end of your current billing cycle. +

+ + {:else} + + {/if} + {:else} +

+ Enable Premium Geo DB for this project to collect detailed geolocation data on + every request.{#if monthlyPriceLabel} + This addon costs {monthlyPriceLabel}/month, prorated for your current + billing cycle. + {:else} + Billed prorated for your current cycle. + {/if} +

+ + {/if} +
+
+
+ + + +{#if premiumGeoDBAddon} + +{/if} diff --git a/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBDisableModal.svelte b/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBDisableModal.svelte new file mode 100644 index 0000000000..98a36efec6 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBDisableModal.svelte @@ -0,0 +1,54 @@ + + + +

+ Are you sure you want to disable the Premium Geo DB addon? The addon will remain active + until the end of your current billing cycle and will not be renewed. +

+ + + + + +
diff --git a/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBEnableModal.svelte b/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBEnableModal.svelte new file mode 100644 index 0000000000..322842dbec --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBEnableModal.svelte @@ -0,0 +1,144 @@ + + + + {#if addonPrice} +

+ By clicking Enable, the amount of + {formatCurrency(addonPrice.monthlyPrice)} will be added to your subscription and + your payment method will be charged + {formatCurrency(addonPrice.proratedAmount)} immediately for the remaining days in your + billing cycle. +

+ {:else} +

+ By clicking Enable, your payment method will be charged for the prorated amount + for the remaining days in your billing cycle, and the addon will be added to this + project's subscription for future cycles. +

+ {/if} +

+ Premium Geo DB enriches session and request data with premium geolocation details including + timezone, postal code, ISP, connection type, and organization. +

+ + {#if addonPrice} +
+
+ {addonPrice.name} + {formatCurrency(addonPrice.monthlyPrice)} / month +
+
+
+ Due today (prorated) + {formatCurrency(addonPrice.proratedAmount)} +
+

+ * Plus applicable tax and fees +

+
+ {/if} + + + + + +
+ +