Skip to content
This repository was archived by the owner on Sep 21, 2026. It is now read-only.

Commit 536e098

Browse files
authored
Merge pull request #3181 from appwrite/fix-soc2-request-plan-gate
Fix soc2 request plan gate
2 parents 4d7ffd6 + 96ac9f8 commit 536e098

3 files changed

Lines changed: 100 additions & 55 deletions

File tree

‎src/lib/stores/billing.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,20 @@ export function isStarterPlan(
134134
return planHasGroup(billingPlan, BillingPlanGroup.Starter);
135135
}
136136

137+
export function planSupportsSoc2(
138+
billingPlanOrId: string | Models.BillingPlan | null | undefined
139+
): boolean {
140+
const billingPlan = makeBillingPlan(billingPlanOrId);
141+
if (!billingPlan) {
142+
return false;
143+
}
144+
145+
// no SOC-2 entitlement exists on the plan model, so key off a real API field
146+
// instead of guessing: SOC-2 is only offered on negotiated/custom contracts,
147+
// never on a self-service plan (including self-service Scale).
148+
return billingPlan.selfService === false;
149+
}
150+
137151
export function canUpgrade(
138152
billingPlanOrId: string | Models.BillingPlan | null | undefined
139153
): boolean {

‎src/routes/(console)/organization-[organization]/settings/Soc2.svelte‎

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@
22
import { Box, CardGrid } from '$lib/components';
33
import { Button } from '$lib/elements/forms';
44
import Soc2Modal from './Soc2Modal.svelte';
5+
import { planSupportsSoc2 } from '$lib/stores/billing';
6+
import { currentPlan } from '$lib/stores/organization';
57
import type { Models } from '@appwrite.io/console';
68
7-
let show = false;
8-
export let locale: Models.Locale;
9-
export let countryList: Models.CountryList;
9+
let {
10+
locale,
11+
countryList
12+
}: {
13+
locale: Models.Locale;
14+
countryList: Models.CountryList;
15+
} = $props();
16+
17+
let show = $state(false);
18+
19+
const supportsSoc2 = $derived(planSupportsSoc2($currentPlan));
1020
</script>
1121

1222
<CardGrid>
@@ -22,16 +32,32 @@
2232
compliance with trust service criteria such as security, availability, processing
2333
integrity, confidentiality, and privacy.
2434
</p>
25-
<Button
26-
secondary
27-
external
28-
class="u-margin-block-start-16"
29-
on:click={() => (show = true)}
30-
event="request_soc-2">
31-
<span class="text">Request SOC-2</span>
32-
</Button>
35+
{#if supportsSoc2}
36+
<Button
37+
secondary
38+
external
39+
class="u-margin-block-start-16"
40+
on:click={() => (show = true)}
41+
event="request_soc-2">
42+
<span class="text">Request SOC-2</span>
43+
</Button>
44+
{:else}
45+
<p class="text u-margin-block-start-8">
46+
SOC-2 reports are only available on an Enterprise contract. Contact our sales
47+
team to discuss upgrading your organization.
48+
</p>
49+
<Button
50+
secondary
51+
external
52+
class="u-margin-block-start-16"
53+
href="https://appwrite.io/contact-us/enterprise">
54+
<span class="text">Contact sales</span>
55+
</Button>
56+
{/if}
3357
</Box>
3458
</svelte:fragment>
3559
</CardGrid>
3660

37-
<Soc2Modal {locale} {countryList} bind:show />
61+
{#if supportsSoc2}
62+
<Soc2Modal {locale} {countryList} bind:show />
63+
{/if}

‎src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte‎

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@
77
import { organization } from '$lib/stores/organization';
88
import { user } from '$lib/stores/user';
99
import { VARS } from '$lib/system';
10-
import { onMount } from 'svelte';
10+
import { untrack } from 'svelte';
1111
import type { Models } from '@appwrite.io/console';
1212
13-
export let show = false;
14-
export let locale: Models.Locale;
15-
export let countryList: Models.CountryList;
13+
let {
14+
show = $bindable(false),
15+
locale,
16+
countryList
17+
}: {
18+
show?: boolean;
19+
locale: Models.Locale;
20+
countryList: Models.CountryList;
21+
} = $props();
1622
17-
let email = '';
18-
let employees: string = null;
19-
let employeesOptions = [
23+
let email = $state($user?.email ?? '');
24+
let employees = $state<string>(null);
25+
const employeesOptions = [
2026
{
2127
value: '1-5',
2228
label: '1-5'
@@ -35,50 +41,49 @@
3541
}
3642
];
3743
38-
let country = '';
39-
let countryOptions = [];
40-
41-
let role = '';
42-
let website = '';
43-
44-
let error: string;
45-
46-
onMount(async () => {
47-
if (locale.countryCode) {
48-
country = locale.countryCode;
49-
}
50-
countryOptions = countryList.countries.map((country) => {
44+
let country = $state(untrack(() => locale?.countryCode ?? ''));
45+
const countryOptions = $derived(
46+
(countryList?.countries ?? []).map((country) => {
5147
return {
5248
value: country.code,
5349
label: country.name
5450
};
55-
});
56-
email = $user.email;
57-
});
51+
})
52+
);
53+
54+
let role = $state('');
55+
let website = $state('');
56+
57+
let error = $state<string>(null);
5858
5959
async function handleSubmit() {
60+
const formData = new FormData();
61+
formData.append('subject', 'SOC-2 Request');
62+
formData.append('email', email);
63+
formData.append('firstName', ($user?.name ?? '').slice(0, 40));
64+
formData.append(
65+
'message',
66+
`SOC-2 request for ${$organization?.name ?? ''} (${$organization?.$id ?? ''})`
67+
);
68+
formData.append('tags[]', 'cloud');
69+
formData.append(
70+
'metaFields',
71+
JSON.stringify({
72+
category: 'SOC-2',
73+
userName: $user?.name ?? '',
74+
orgId: $organization?.$id ?? '',
75+
userId: $user?.$id ?? '',
76+
billingPlan: $organization?.billingPlanId ?? '',
77+
employees: employees,
78+
country: country,
79+
role: role,
80+
website: website
81+
})
82+
);
83+
6084
const response = await fetch(`${VARS.GROWTH_ENDPOINT}/support`, {
6185
method: 'POST',
62-
headers: {
63-
'Content-Type': 'application/json'
64-
},
65-
body: JSON.stringify({
66-
subject: 'SOC-2 Request',
67-
email: email,
68-
firstName: ($user?.name ?? '').slice(0, 40),
69-
message: `SOC-2 request for ${$organization?.name ?? ''} (${$organization?.$id ?? ''})`,
70-
tags: ['cloud'],
71-
metaFields: {
72-
category: 'SOC-2',
73-
userName: $user?.name ?? '',
74-
orgId: $organization?.$id ?? '',
75-
userId: $user?.$id ?? '',
76-
employees: employees,
77-
country: country,
78-
role: role,
79-
website: website
80-
}
81-
})
86+
body: formData
8287
});
8388
trackEvent(Submit.RequestSoc2);
8489
if (response.status !== 200) {

0 commit comments

Comments
 (0)