Skip to content

feat: add Premium Geo DB addon to project settings#2981

Open
lohanidamodar wants to merge 17 commits intomainfrom
feat/project-premium-geo-db-addon
Open

feat: add Premium Geo DB addon to project settings#2981
lohanidamodar wants to merge 17 commits intomainfrom
feat/project-premium-geo-db-addon

Conversation

@lohanidamodar
Copy link
Copy Markdown
Member

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:

  • Upgrade prompt when the current plan does not support the addon
  • Enable flow with optional 3DS payment authentication
  • Pending state with cancel & retry option (if payment was interrupted)
  • Active state with disable action
  • Scheduled-for-removal state with re-enable action

Uses the new project-scoped SDK methods shipped with the cloud update: listAddons, createPremiumGeoDBAddon, and deleteAddon. Bumps the @appwrite.io/console SDK pin accordingly.

Test plan

  • Navigate to a cloud project's settings as an org owner on a plan that supports Premium Geo DB → section appears with Enable CTA
  • Click Enable → modal confirms and enables the addon (3DS where required); section transitions to Active
  • Click Disable → confirms removal at end of billing cycle; section transitions to Scheduled-for-removal
  • Click Keep Premium Geo DB while Scheduled-for-removal → returns to Active
  • On a plan that doesn't support the addon → shows Upgrade plan CTA / "not available" copy
  • On self-hosted mode → section is hidden

🤖 Generated with Claude Code

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-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 18, 2026

Greptile Summary

This PR adds a Premium Geo DB addon section to the project settings page, mirroring the BAA pattern from organization settings. It introduces three new Svelte components for the addon lifecycle (enable, disable, pending/re-enable states), a bumped SDK pin with project-scoped addon APIs, and per-project addon rows in the billing plan summary.

  • premiumGeoDB.svelte drives the full lifecycle UI (upgrade prompt → enable → pending → active → scheduled-for-removal) using Svelte 4 Options API, while its child modals use Svelte 5 runes — a style split from the BAA reference implementation.
  • planSummary.svelte drops the hardcoded billingAddonNames map in favor of addon.name from the API, and adds per-project addon rows to the breakdown children alongside the existing top-level addon filter — the two levels share the same addon_-prefix scan, which warrants confirming with the billing API that project-scoped addon charges are not surfaced at both aggregation levels.
  • The page loader adds listAddons and getAddonPrice with .catch(() => null) suppression, silently returning null on any billing API error rather than re-throwing non-permission failures as the existing variable/installation loaders do.

Confidence Score: 3/5

The payment flow has multiple unresolved gaps in premiumGeoDB.svelte that leave users stuck in the pending state after 3DS redirects or when re-enabling a scheduled-for-removal addon with a card that requires authentication.

The core addon lifecycle UI and modal structure are solid, but the 3DS return path is not handled in premiumGeoDB.svelte — there is no onMount that calls confirmAddonPayment when ?type=confirm-addon appears in the URL after a Stripe redirect. Additionally, handleReEnable silently discards the API response, so any re-enable attempt that requires 3DS never initiates the payment challenge, leaving the addon in pending without any path to activation.

premiumGeoDB.svelte needs the 3DS return handler and a corrected re-enable flow; planSummary.svelte needs the double-counting scenario confirmed with the billing API team before merging.

Important Files Changed

Filename Overview
src/routes/(console)/project-[region]-[project]/settings/premiumGeoDB.svelte New component implementing the full Premium Geo DB addon lifecycle (upgrade prompt, enable, pending, active, scheduled-for-removal). Uses Svelte 4 Options API while child modals use Svelte 5 runes. Payment flow has known gaps around 3DS return handling and the re-enable path.
src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBEnableModal.svelte Enable modal correctly detects clientSecret in the API response and initiates 3DS via confirmPayment. Handles 409 gracefully. Uses Svelte 5 runes with legacy slot API.
src/routes/(console)/project-[region]-[project]/settings/premiumGeoDBDisableModal.svelte Disable modal is straightforward — calls deleteAddon, invalidates dependencies, shows success notification. Uses mixed Svelte 5 runes script with Svelte 4 on:click and slot in template.
src/routes/(console)/project-[region]-[project]/settings/+page.ts Adds listAddons and getAddonPrice to the page loader with cloud-only guards. Both API calls swallow all errors via .catch(() => null), departing from the selective re-throw pattern used for variables and installations.
src/routes/(console)/project-[region]-[project]/settings/+page.svelte Mounts PremiumGeoDB behind isCloud && $canWriteProjects guard; correctly passes addons and addonPrice props from loader data.
src/routes/(console)/organization-[organization]/billing/planSummary.svelte Removes the hardcoded billingAddonNames map and adds per-project addon rows using API-provided addon.name. Top-level addons filter and new per-project addon rows both use the same id: addon-${addon.resourceId} format — potential double-counting if the billing API surfaces project-level addons at both aggregation levels.
package.json Bumps @appwrite.io/console SDK pin from 93d2dfa to 352239b to include project-scoped listAddons, createPremiumGeoDBAddon, deleteAddon, and getAddonPrice.

Reviews (14): Last reviewed commit: "feat(settings): tighten Premium Geo DB c..." | Re-trigger Greptile

Comment on lines +64 to +83
async function handleReEnable() {
reEnabling = true;
try {
await sdk.forConsoleIn(page.params.region).projects.createPremiumGeoDBAddon({
projectId: page.params.project
});
await Promise.all([invalidate(Dependencies.ADDONS), invalidate(Dependencies.PROJECT)]);
addNotification({
message: 'Premium Geo DB addon has been re-enabled',
type: 'success'
});
} catch (e) {
addNotification({
message: e.message,
type: 'error'
});
} finally {
reEnabling = false;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 handleReEnable silently drops 3DS response

createPremiumGeoDBAddon can return either a Models.Addon (immediate success) or a Models.PaymentAuthentication (3DS required). handleReEnable discards the return value entirely, so if re-enabling requires 3DS the payment challenge is never initiated — the notification fires as "re-enabled" while the addon actually sits in pending state waiting for a payment that will never complete.

The enable modal handles this correctly via the 'clientSecret' in result check and subsequent confirmPayment call. handleReEnable should mirror that same pattern, exactly as BAA.svelte's handleReEnable does.

lohanidamodar and others added 4 commits April 19, 2026 11:13
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
…geo-db-addon

# Conflicts:
#	src/routes/(console)/project-[region]-[project]/settings/+page.svelte
#	src/routes/(console)/project-[region]-[project]/settings/+page.ts
Comment on lines +399 to +410
...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
})
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant