feat: add Premium Geo DB addon to project settings#2981
Conversation
Adds a Premium Geo DB section to the project settings page so users can enable and disable the premium geolocation addon per-project on cloud. The section supports the full addon lifecycle: - Upgrade prompt when the current plan does not support the addon - Enable flow with optional 3DS payment authentication - Pending state with cancel & retry option - Active state with disable action - Scheduled-for-removal state with re-enable action Uses the new project-scoped SDK methods: listAddons, createPremiumGeoDBAddon, and deleteAddon. Bumps the @appwrite.io/console SDK pin accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR adds a Premium Geo DB section to project settings, enabling users to enable/disable the geolocation addon per-project on cloud. It also bumps the
Confidence Score: 3/5The payment flow in the enable modal and re-enable handler has correctness gaps that can leave addons stuck in pending state after both 3DS and non-3DS payments. Multiple known defects remain unaddressed in the payment confirmation path: the enable modal discards the Stripe outcome and omits the server-side addon confirmation step for non-redirect payments, and the re-enable handler in the parent component silently ignores a 3DS response entirely. These gaps mean a meaningful fraction of enable attempts will leave the addon in permanent pending state with no path to recover. The billing plan summary also removes a safety-net fallback for addon display names, which could cause existing BAA subscribers to see garbled line items. premiumGeoDBEnableModal.svelte and premiumGeoDB.svelte (handleReEnable) need the most attention before this ships; planSummary.svelte should be verified against the live billing API response shape. Important Files Changed
Reviews (22): Last reviewed commit: "Bump console SDK to @2642dc5 (current cl..." | Re-trigger Greptile |
Mirrors the BAA addon UX by fetching the addon price via organizations.getAddonPrice(Addon.Premiumgeodb) from the settings page loader, passing it through to the Premium Geo DB card and enable modal, and rendering the monthly/prorated breakdown with formatCurrency alongside the Enable CTA. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…name - Each project row in the organization billing breakdown now iterates its resources for addon_* entries (amount > 0) and renders them as child rows (e.g. Premium Geo DB under the project it was enabled on). The backend already filters project-scoped addons out of the team-level resources response, so the org "Addons" section shows only org-scoped addons (BAA, premiumGeoDBOrg) while project-scoped ones surface where they belong. - Org-level addon labels now read addon.name from the UsageResource payload that the getAggregation endpoint populates from billingAddons config. Dropped the hard-coded billingAddonNames map so new addons surface with their proper name without a console update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…geo-db-addon # Conflicts: # bun.lock # package.json # src/routes/(console)/project-[region]-[project]/settings/+page.svelte
…geo-db-addon # Conflicts: # bun.lock # package.json
…DK methods" This reverts commit 304142c.
…geo-db-addon # Conflicts: # src/routes/(console)/project-[region]-[project]/settings/+page.svelte # src/routes/(console)/project-[region]-[project]/settings/+page.ts
| ...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 | ||
| }) | ||
| ), |
There was a problem hiding this comment.
Possible double-counting of addon charges in plan summary
The top-level addons section (line 252–275) already creates a billing row for every addon_-prefixed resource found in currentAggregation.resources. If the billing API also surfaces those same addon resources inside projectData.resources (the per-project breakdown), each project-scoped addon (e.g. Premium Geo DB) will appear as both a top-level line item and a child row — showing the same charge twice to the user.
Before shipping, confirm whether the cloud billing API places project-level addon charges exclusively in breakdown[].resources (making the top-level filter skip them) or in both places. If the former, this code is correct; if the latter, the top-level addons filter needs to exclude resources that are already accounted for at the project level (or vice-versa).
…geo-db-addon # Conflicts: # bun.lock # package.json
…geo-db-addon # Conflicts: # bun.lock # package.json
…h for premium geoDB Two missing pieces vs the BAA addon flow: 1. After Stripe redirects back from 3DS with ?type=confirm-addon&addonId=..., the page didn't read the query params or call the confirmations endpoint, so the addon stayed pending forever. Port the onMount handler from BAA.svelte. 2. The pending-state action was "Cancel & retry" which deleted the addon and forced a fresh 3DS flow. That's wasteful when the payment just needs a server-side status sync. Replace with "Refresh" — calls the confirmations endpoint, which live-queries Stripe and activates the addon if the PaymentIntent has transitioned to succeeded. Backend cleans up on 402.
The console SDK bump in this PR renamed Locale to CloudLocale, breaking svelte-check with 7 errors across billing/Soc2/address components.
| ); | ||
| $: isPending = premiumGeoDBAddon?.status === 'pending'; | ||
| $: isActive = premiumGeoDBAddon?.status === 'active'; |
There was a problem hiding this comment.
Addon key casing inconsistency with BAA pattern
The BAA.svelte filter uses the all-lowercase string 'baa', consistent with Appwrite's API key convention. Here 'premiumGeoDB' (camelCase) is used. If the API returns the addon with a lowercase key (following the same pattern as BAA), this find will never match — premiumGeoDBAddon would always be undefined and the component would always render the Enable CTA even when the addon is already active or pending. The same camelCase string is also used in the onMount fallback lookup at line 65, so both detection paths would silently fail. Please confirm the exact key string the API returns for this addon. What string does the API's listAddons response return for the key field of the Premium Geo DB addon — 'premiumGeoDB', 'premiumgeodb', or something else? BAA uses 'baa' (all lowercase), so camelCase here looks inconsistent with that established pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Adds a Premium Geo DB section to the project settings page so users can enable and disable the premium geolocation addon per-project on cloud.
The section supports the full addon lifecycle, mirroring the BAA pattern at the organization level:
Uses the new project-scoped SDK methods shipped with the cloud update:
listAddons,createPremiumGeoDBAddon, anddeleteAddon. Bumps the@appwrite.io/consoleSDK pin accordingly.Test plan
🤖 Generated with Claude Code