Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@f063676",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@febca4d",
"@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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,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 { ID } from '@appwrite.io/console';

export let data;
Expand Down Expand Up @@ -93,6 +95,9 @@
<UpdateProtocols />
<UpdateServices />
<UpdateInstallations {...data.installations} limit={data.limit} offset={data.offset} />
{#if isCloud}
<PremiumGeoDB addons={data.addons} />
{/if}
<UpdateVariables
{sdkCreateVariable}
{sdkUpdateVariable}
Expand Down
15 changes: 12 additions & 3 deletions src/routes/(console)/project-[region]-[project]/settings/+page.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
import { isCloud } from '$lib/system';
import { sdk } from '$lib/stores/sdk';
import { Query } from '@appwrite.io/console';
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ depends, url, params }) => {
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 [variables, installations] = await Promise.all([
const [variables, installations, addons] = await Promise.all([
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)
]);

return {
limit,
offset,
variablesOffset,
variables,
installations
installations,
addons
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<script lang="ts">
import { Box, CardGrid } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { getChangePlanUrl, plansInfo } from '$lib/stores/billing';
import { currentPlan, organization } from '$lib/stores/organization';
import { page } from '$app/state';
import { get } from 'svelte/store';
import { invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Badge } from '@appwrite.io/pink-svelte';
import type { Models } from '@appwrite.io/console';
import PremiumGeoDBEnableModal from './premiumGeoDBEnableModal.svelte';
import PremiumGeoDBDisableModal from './premiumGeoDBDisableModal.svelte';

export let addons: Models.AddonList | null = null;

let showEnable = false;
let showDisable = false;
let reEnabling = false;
let cancelling = false;

$: planSupportsPremiumGeoDB = $currentPlan?.supportedAddons?.premiumGeoDB === true;
$: canUpgradeToPremiumGeoDB =
!planSupportsPremiumGeoDB && hasUpgradeablePlanWithPremiumGeoDB($currentPlan);
$: premiumGeoDBAddon = addons?.addons?.find(
(a) => a.key === 'premiumGeoDB' && (a.status === 'active' || a.status === 'pending')
);
$: isPending = premiumGeoDBAddon?.status === 'pending';
$: isActive = premiumGeoDBAddon?.status === 'active';
$: isScheduledForRemoval = isActive && premiumGeoDBAddon?.nextValue === 0;
Comment thread
greptile-apps[bot] marked this conversation as resolved.

function hasUpgradeablePlanWithPremiumGeoDB(plan: Models.BillingPlan): boolean {
if (!plan) return false;
const plans = get(plansInfo);
for (const [, p] of plans) {
if (p.order > plan.order && p.supportedAddons?.premiumGeoDB) {
return true;
}
}
return false;
}

async function handleCancelAndRetry() {
cancelling = true;
try {
await sdk.forConsoleIn(page.params.region).projects.deleteAddon({
projectId: page.params.project,
addonId: premiumGeoDBAddon.$id
});
await invalidate(Dependencies.ADDONS);
showEnable = true;
} catch (e) {
addNotification({
message: e.message,
type: 'error'
});
} finally {
cancelling = false;
}
}

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;
}
}
Comment on lines +67 to +86
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.

</script>
Comment thread
greptile-apps[bot] marked this conversation as resolved.

<CardGrid>
<svelte:fragment slot="title">Premium Geo DB</svelte:fragment>
Enrich session and request data with premium geolocation details such as timezone, postal code, ISP,
connection type, and organization. Useful for fine-grained analytics, fraud detection, and personalized
user experiences.
<svelte:fragment slot="aside">
<Box>
<h6>
<b>Premium Geo DB</b>
</h6>
{#if !planSupportsPremiumGeoDB && canUpgradeToPremiumGeoDB}
<p class="text u-margin-block-start-8">
Premium Geo DB is not available on your current plan. Upgrade your plan to
enable it.
</p>
<Button
secondary
class="u-margin-block-start-16"
href={getChangePlanUrl($organization?.$id)}>
<span class="text">Upgrade plan</span>
</Button>
{:else if !planSupportsPremiumGeoDB}
<p class="text u-margin-block-start-8">
Premium Geo DB is not available on your current plan.
</p>
{:else if isPending}
<div class="u-flex u-cross-center u-gap-8 u-margin-block-start-8">
<Badge variant="secondary" type="warning" content="Payment pending" />
</div>
<p class="text u-margin-block-start-8">
A payment is awaiting confirmation. If the payment was interrupted, you can
cancel and retry.
</p>
<Button
secondary
class="u-margin-block-start-16"
disabled={cancelling}
on:click={handleCancelAndRetry}>
<span class="text">Cancel & retry</span>
</Button>
{:else if isActive}
<div class="u-flex u-cross-center u-gap-8 u-margin-block-start-8">
{#if isScheduledForRemoval}
<Badge variant="secondary" type="warning" content="Scheduled for removal" />
{:else}
<Badge variant="secondary" type="success" content="Active" />
{/if}
</div>
<p class="text u-margin-block-start-8">
Premium Geo DB is enabled for this project.
</p>
{#if isScheduledForRemoval}
<p class="text u-margin-block-start-8">
Premium Geo DB will be removed at the end of your current billing cycle.
</p>
<Button
secondary
class="u-margin-block-start-16"
disabled={reEnabling}
on:click={handleReEnable}>
<span class="text">Keep Premium Geo DB</span>
</Button>
{:else}
<Button
secondary
class="u-margin-block-start-16"
on:click={() => (showDisable = true)}>
<span class="text">Disable Premium Geo DB</span>
</Button>
{/if}
{:else}
<p class="text u-margin-block-start-8">
Enable Premium Geo DB for this project to collect detailed geolocation data on
every request. Billed prorated for your current cycle.
</p>
<Button
secondary
class="u-margin-block-start-16"
on:click={() => (showEnable = true)}>
<span class="text">Enable Premium Geo DB</span>
</Button>
{/if}
</Box>
</svelte:fragment>
</CardGrid>

<PremiumGeoDBEnableModal bind:show={showEnable} />

{#if premiumGeoDBAddon}
<PremiumGeoDBDisableModal bind:show={showDisable} addonId={premiumGeoDBAddon.$id} />
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script lang="ts">
import { page } from '$app/state';
import { invalidate } from '$app/navigation';
import { Modal } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Dependencies } from '$lib/constants';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';

let {
show = $bindable(false),
addonId
}: {
show: boolean;
addonId: string;
} = $props();

let error = $state<string | null>(null);
let submitting = $state(false);

async function handleSubmit() {
submitting = true;
error = null;
try {
await sdk.forConsoleIn(page.params.region).projects.deleteAddon({
projectId: page.params.project,
addonId
});
await Promise.all([invalidate(Dependencies.ADDONS), invalidate(Dependencies.PROJECT)]);
addNotification({
message:
'Premium Geo DB addon will be removed at the end of your current billing cycle',
type: 'success'
});
show = false;
} catch (e) {
error = e.message;
} finally {
submitting = false;
}
}
</script>

<Modal bind:error bind:show onSubmit={handleSubmit} title="Disable Premium Geo DB">
<p class="text">
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.
</p>

<svelte:fragment slot="footer">
<Button text on:click={() => (show = false)}>Cancel</Button>
<Button secondary submit disabled={submitting}>Disable Premium Geo DB</Button>
</svelte:fragment>
</Modal>
Loading
Loading