diff --git a/src/hooks.client.ts b/src/hooks.client.ts
index c6404ee81..9c8679912 100644
--- a/src/hooks.client.ts
+++ b/src/hooks.client.ts
@@ -8,8 +8,7 @@ Sentry.init({
dsn: 'https://c7ce178bdedd486480317b72f282fd39@o1063647.ingest.us.sentry.io/4504158071422976',
tracesSampleRate: 1,
replaysSessionSampleRate: 0,
- replaysOnErrorSampleRate: 1,
- integrations: [Sentry.replayIntegration()]
+ replaysOnErrorSampleRate: 0
});
export const handleError: HandleClientError = Sentry.handleErrorWithSentry(
diff --git a/src/lib/elements/forms/inputSelect.svelte b/src/lib/elements/forms/inputSelect.svelte
index 844d3e898..75b7ea2ae 100644
--- a/src/lib/elements/forms/inputSelect.svelte
+++ b/src/lib/elements/forms/inputSelect.svelte
@@ -17,6 +17,7 @@
export let options: {
value: string | boolean | number | null;
label: string;
+ disabled?: boolean;
}[];
export let isMultiple = false;
export let fullWidth = false;
@@ -64,4 +65,4 @@
on:invalid={handleInvalid}
on:input
on:change
- bind:value />
+ bind:value />
\ No newline at end of file
diff --git a/src/lib/helpers/pricingRedirect.ts b/src/lib/helpers/pricingRedirect.ts
new file mode 100644
index 000000000..5de04212f
--- /dev/null
+++ b/src/lib/helpers/pricingRedirect.ts
@@ -0,0 +1,13 @@
+import { goto } from '$app/navigation';
+import { base } from '$app/paths';
+
+export function checkPricingRefAndRedirect(searchParams: URLSearchParams, shouldRegister = false) {
+ if (searchParams.has('type')) {
+ const paramType = searchParams.get('type');
+ if (paramType === 'createPro') {
+ shouldRegister
+ ? goto(`${base}/register?type=createPro`)
+ : goto(`${base}/create-organization?type=createPro`);
+ }
+ }
+}
diff --git a/src/lib/stores/specifications.ts b/src/lib/stores/specifications.ts
new file mode 100644
index 000000000..30bc5eabb
--- /dev/null
+++ b/src/lib/stores/specifications.ts
@@ -0,0 +1,8 @@
+import { page } from '$app/stores';
+import type { Models } from '@appwrite.io/console';
+import { derived } from 'svelte/store';
+
+export const specificationsList = derived(
+ page,
+ async ($page) => (await $page.data.specificationsList) as Models.SpecificationList
+);
diff --git a/src/lib/wizards/functions/components/specificationsTooltip.svelte b/src/lib/wizards/functions/components/specificationsTooltip.svelte
new file mode 100644
index 000000000..279f5a0f1
--- /dev/null
+++ b/src/lib/wizards/functions/components/specificationsTooltip.svelte
@@ -0,0 +1,24 @@
+
+
+
+
Additional CPU and RAM are available for Pro and Scale teams.
+
+
+
+
diff --git a/src/lib/wizards/functions/cover.svelte b/src/lib/wizards/functions/cover.svelte
index e88803c42..bf4484b8b 100644
--- a/src/lib/wizards/functions/cover.svelte
+++ b/src/lib/wizards/functions/cover.svelte
@@ -19,7 +19,8 @@
repositoryBehaviour: 'new',
repositoryName: template.id,
repositoryPrivate: true,
- repositoryId: null
+ repositoryId: null,
+ specification: null
});
wizard.start(CreateTemplate);
}
diff --git a/src/lib/wizards/functions/createManual.svelte b/src/lib/wizards/functions/createManual.svelte
index c8fe529ef..586b580c6 100644
--- a/src/lib/wizards/functions/createManual.svelte
+++ b/src/lib/wizards/functions/createManual.svelte
@@ -37,7 +37,18 @@
undefined,
undefined,
$createFunction.entrypoint,
- $createFunction.commands || undefined
+ $createFunction.commands || undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ $createFunction.specification
);
await sdk.forProject.functions.createDeployment(
response.$id,
diff --git a/src/lib/wizards/functions/createTemplate.svelte b/src/lib/wizards/functions/createTemplate.svelte
index 63aa03ab5..e41bf78dc 100644
--- a/src/lib/wizards/functions/createTemplate.svelte
+++ b/src/lib/wizards/functions/createTemplate.svelte
@@ -58,7 +58,8 @@
$template.providerRepositoryId || undefined,
$template.providerOwner || undefined,
runtimeDetail.providerRootDirectory || undefined,
- $template.providerVersion || undefined
+ $template.providerVersion || undefined,
+ $templateConfig.specification || undefined
);
if ($templateConfig.variables) {
@@ -78,7 +79,8 @@
customId: !!response.$id,
runtime: response.runtime,
deployment_type: $templateConfig.repositoryBehaviour,
- scopes: $templateConfig.scopes
+ scopes: $templateConfig.scopes,
+ specification: $templateConfig.specification
});
resetState();
} catch (error) {
diff --git a/src/lib/wizards/functions/steps/manualDetails.svelte b/src/lib/wizards/functions/steps/manualDetails.svelte
index 55fe5655c..9167474d0 100644
--- a/src/lib/wizards/functions/steps/manualDetails.svelte
+++ b/src/lib/wizards/functions/steps/manualDetails.svelte
@@ -6,16 +6,30 @@
import { onMount } from 'svelte';
import { createFunction } from '../store';
import { runtimesList } from '$lib/stores/runtimes';
+ import { specificationsList } from '$lib/stores/specifications';
+ import { isCloud } from '$lib/system';
+ import { organization } from '$lib/stores/organization';
+ import { BillingPlan } from '$lib/constants';
+ import SpecificationsTooltip from '../components/specificationsTooltip.svelte';
let showCustomId = false;
let options = [];
+ let specificationOptions = [];
onMount(async () => {
options = (await $runtimesList).runtimes.map((runtime) => ({
label: `${runtime.name} - ${runtime.version}`,
value: runtime.$id
}));
+
+ specificationOptions = (await $specificationsList).specifications.map((size) => ({
+ label:
+ `${size.cpus} CPU, ${size.memory} MB RAM` +
+ (!size.enabled ? ` (Upgrade to use this)` : ''),
+ value: size.slug,
+ disabled: !size.enabled
+ }));
});
@@ -39,6 +53,18 @@
{options}
required />
+
+
{#if !showCustomId}
(showCustomId = !showCustomId)}>
diff --git a/src/lib/wizards/functions/steps/templateConfiguration.svelte b/src/lib/wizards/functions/steps/templateConfiguration.svelte
index 481cd5026..fbce477fb 100644
--- a/src/lib/wizards/functions/steps/templateConfiguration.svelte
+++ b/src/lib/wizards/functions/steps/templateConfiguration.svelte
@@ -4,7 +4,12 @@
import { FormList, InputSelect, InputText } from '$lib/elements/forms';
import { WizardStep } from '$lib/layout';
import { runtimesList } from '$lib/stores/runtimes';
+ import { specificationsList } from '$lib/stores/specifications';
+ import { BillingPlan } from '$lib/constants';
+ import SpecificationsTooltip from '../components/specificationsTooltip.svelte';
import { template, templateConfig } from '../store';
+ import { organization } from '$lib/stores/organization';
+ import { isCloud } from '$lib/system';
let showCustomId = false;
@@ -12,6 +17,10 @@
if (!$templateConfig.runtime) {
throw new Error('Please select a runtime.');
}
+
+ if (!$templateConfig.specification) {
+ throw new Error('Please select a specification.');
+ }
}
async function loadRuntimes() {
@@ -27,6 +36,22 @@
return options;
}
+
+ async function loadSpecifications() {
+ const specificationOptions = (await $specificationsList).specifications.map((size) => ({
+ label:
+ `${size.cpus} CPU, ${size.memory} MB RAM` +
+ (!size.enabled ? ` (Upgrade to use this)` : ''),
+ value: size.slug,
+ disabled: !size.enabled
+ }));
+
+ if (!$templateConfig.specification && specificationOptions.length > 0) {
+ $templateConfig.specification = specificationOptions[0].value;
+ }
+
+ return specificationOptions;
+ }
@@ -61,6 +86,28 @@
{options}
bind:value={$templateConfig.runtime} />
{/await}
+ {#await loadSpecifications()}
+
+ {:then specificationOptions}
+
+ {/await}
diff --git a/src/lib/wizards/functions/store.ts b/src/lib/wizards/functions/store.ts
index 90cfdc55b..6613438a2 100644
--- a/src/lib/wizards/functions/store.ts
+++ b/src/lib/wizards/functions/store.ts
@@ -15,6 +15,7 @@ export const templateConfig = writable<{
repositoryId: string;
execute?: boolean;
scopes?: string[];
+ specification?: string;
}>();
export const repository = writable();
export const installation = writable();
diff --git a/src/routes/(console)/create-organization/+page.svelte b/src/routes/(console)/create-organization/+page.svelte
index c463a5d4a..63cc1f210 100644
--- a/src/routes/(console)/create-organization/+page.svelte
+++ b/src/routes/(console)/create-organization/+page.svelte
@@ -79,7 +79,11 @@
billingPlan = plan as BillingPlan;
}
}
- if (anyOrgFree) {
+ if (
+ anyOrgFree ||
+ ($page.url.searchParams.has('type') &&
+ $page.url.searchParams.get('type') === 'createPro')
+ ) {
billingPlan = BillingPlan.PRO;
}
});
diff --git a/src/routes/(console)/onboarding/+page.svelte b/src/routes/(console)/onboarding/+page.svelte
index 3f112011c..b4a9b4aba 100644
--- a/src/routes/(console)/onboarding/+page.svelte
+++ b/src/routes/(console)/onboarding/+page.svelte
@@ -17,6 +17,7 @@
import { tierToPlan, type Tier, plansInfo } from '$lib/stores/billing';
import { formatCurrency } from '$lib/helpers/numbers';
import { base } from '$app/paths';
+ import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
let name: string;
let id: string;
@@ -42,12 +43,7 @@
onMount(() => {
if (isCloud) {
- if ($page.url.searchParams.has('type')) {
- const paramType = $page.url.searchParams.get('type');
- if (paramType === 'createPro') {
- goto(`${base}/create-organization`);
- }
- }
+ checkPricingRefAndRedirect($page.url.searchParams);
}
});
diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte
index d16339072..76607b429 100644
--- a/src/routes/(console)/organization-[organization]/+page.svelte
+++ b/src/routes/(console)/organization-[organization]/+page.svelte
@@ -32,6 +32,7 @@
import { onMount } from 'svelte';
import { organization } from '$lib/stores/organization';
import { canWriteProjects } from '$lib/stores/roles';
+ import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
export let data;
@@ -123,12 +124,7 @@
onMount(async () => {
if (isCloud) {
regions = await sdk.forConsole.billing.listRegions();
- if ($page.url.searchParams.has('type')) {
- const paramType = $page.url.searchParams.get('type');
- if (paramType === 'createPro') {
- goto(`${base}/create-organization`);
- }
- }
+ checkPricingRefAndRedirect($page.url.searchParams);
}
});
diff --git a/src/routes/(console)/project-[project]/functions/+layout.ts b/src/routes/(console)/project-[project]/functions/+layout.ts
index cab65d7c3..8d7b68a16 100644
--- a/src/routes/(console)/project-[project]/functions/+layout.ts
+++ b/src/routes/(console)/project-[project]/functions/+layout.ts
@@ -8,10 +8,11 @@ import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async ({ depends }) => {
depends(Dependencies.FUNCTION_INSTALLATIONS);
- const [runtimesList, installations, templatesList] = await Promise.all([
+ const [runtimesList, installations, templatesList, specificationsList] = await Promise.all([
sdk.forProject.functions.listRuntimes(),
sdk.forProject.vcs.listInstallations([Query.limit(100)]),
- sdk.forProject.functions.listTemplates(undefined, undefined, 100)
+ sdk.forProject.functions.listTemplates(undefined, undefined, 100),
+ sdk.forProject.functions.listSpecifications()
]);
return {
@@ -19,6 +20,7 @@ export const load: LayoutLoad = async ({ depends }) => {
breadcrumbs: Breadcrumbs,
runtimesList,
installations,
- templatesList
+ templatesList,
+ specificationsList
};
};
diff --git a/src/routes/(console)/project-[project]/functions/function-[function]/settings/updateRuntime.svelte b/src/routes/(console)/project-[project]/functions/function-[function]/settings/updateRuntime.svelte
index d7925fea5..1f06dbe9c 100644
--- a/src/routes/(console)/project-[project]/functions/function-[function]/settings/updateRuntime.svelte
+++ b/src/routes/(console)/project-[project]/functions/function-[function]/settings/updateRuntime.svelte
@@ -3,30 +3,46 @@
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CardGrid, Heading } from '$lib/components';
- import { Dependencies } from '$lib/constants';
+ import { BillingPlan, Dependencies } from '$lib/constants';
import { Button, Form, FormList } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import { func } from '../store';
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
+ import { specificationsList } from '$lib/stores/specifications';
import { runtimesList } from '$lib/stores/runtimes';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
+ import { isCloud } from '$lib/system';
+ import { organization } from '$lib/stores/organization';
+ import SpecificationsTooltip from '$lib/wizards/functions/components/specificationsTooltip.svelte';
const functionId = $page.params.function;
let runtime: string = null;
+ let specification: string = null;
let options = [];
+ let specificationOptions = [];
onMount(async () => {
runtime ??= $func.runtime;
+ specification ??= $func.specification;
let runtimes = await $runtimesList;
+ let allowedSpecifications = (await $specificationsList).specifications;
options = runtimes.runtimes.map((runtime) => ({
label: `${runtime.name} - ${runtime.version}`,
value: runtime.$id
}));
+
+ specificationOptions = allowedSpecifications.map((size) => ({
+ label:
+ `${size.cpus} CPU, ${size.memory} MB RAM` +
+ (!size.enabled ? ` (Upgrade to use this)` : ''),
+ value: size.slug,
+ disabled: !size.enabled
+ }));
});
async function updateRuntime() {
@@ -51,11 +67,12 @@
$func.providerRepositoryId || undefined,
$func.providerBranch || undefined,
$func.providerSilentMode || undefined,
- $func.providerRootDirectory || undefined
+ $func.providerRootDirectory || undefined,
+ specification
);
await invalidate(Dependencies.FUNCTION);
addNotification({
- message: 'Runtime has been updated',
+ message: 'Runtime settings have been updated',
type: 'success'
});
trackEvent(Submit.FunctionUpdateName);
@@ -67,6 +84,8 @@
trackError(error, Submit.FunctionUpdateName);
}
}
+
+ $: isUpdateButtonEnabled = runtime !== $func?.runtime || specification !== $func?.specification;
-
+
diff --git a/src/routes/(console)/project-[project]/functions/function-[function]/usage/[[period]]/+page.svelte b/src/routes/(console)/project-[project]/functions/function-[function]/usage/[[period]]/+page.svelte
index dcd003a74..95e9e8a25 100644
--- a/src/routes/(console)/project-[project]/functions/function-[function]/usage/[[period]]/+page.svelte
+++ b/src/routes/(console)/project-[project]/functions/function-[function]/usage/[[period]]/+page.svelte
@@ -10,6 +10,8 @@
export let data: PageData;
$: total = data.executionsTotal;
$: count = data.executions;
+ $: gbHoursTotal = data.executionsMbSecondsTotal / 1000 / 3600;
+ $: mbSecondsCount = data.executionsMbSeconds;
@@ -47,4 +49,24 @@
]} />
{/if}
+
+ {#if mbSecondsCount}
+
+ {formatNumberWithCommas(gbHoursTotal)}
+ GB Hours
+
+ [
+ e.date,
+ Math.ceil((e.value / 1000 / 3600) * 1000) / 1000
+ ])
+ ]
+ }
+ ]} />
+
+ {/if}
diff --git a/src/routes/(public)/(guest)/register/+page.svelte b/src/routes/(public)/(guest)/register/+page.svelte
index f8fab2e6f..96fe43ae0 100644
--- a/src/routes/(public)/(guest)/register/+page.svelte
+++ b/src/routes/(public)/(guest)/register/+page.svelte
@@ -20,6 +20,7 @@
import { isCloud } from '$lib/system';
import { page } from '$app/stores';
import { redirectTo } from '$routes/store';
+ import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
export let data;
@@ -52,6 +53,8 @@
$page.url.searchParams.delete('redirect');
if (redirect) {
await goto(`${redirect}${$page.url.search}`);
+ } else if (isCloud) {
+ checkPricingRefAndRedirect($page.url.searchParams);
} else {
await goto(`${base}/${$page.url.search ?? ''}`);
}
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts
index 77fd3e8b1..134ef1e22 100644
--- a/src/routes/+layout.ts
+++ b/src/routes/+layout.ts
@@ -9,6 +9,8 @@ import { redirectTo } from './store';
import { base } from '$app/paths';
import type { Account } from '$lib/stores/user';
import type { AppwriteException } from '@appwrite.io/console';
+import { isCloud } from '$lib/system';
+import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect';
export const ssr = false;
@@ -46,6 +48,10 @@ export const load: LayoutLoad = async ({ depends, url, route }) => {
}
if (!isPublicRoute) {
+ if (isCloud) {
+ checkPricingRefAndRedirect(url.searchParams, true);
+ }
+
redirect(303, withParams(`${base}/login`, url.searchParams));
}
};