diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 96c84bfad..012c0b2de 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,6 +41,7 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY }}" + "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" publish-cloud-stage: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index c83f0cb42..ae34af89d 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,6 @@ dist # SvelteKit build / generate output .svelte-kit + +# Sentry Config File +.sentryclirc diff --git a/Dockerfile b/Dockerfile index 221460c05..0c0b55f83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,11 +22,14 @@ ARG PUBLIC_CONSOLE_MODE ARG PUBLIC_APPWRITE_ENDPOINT ARG PUBLIC_GROWTH_ENDPOINT ARG PUBLIC_STRIPE_KEY +ARG SENTRY_AUTH_TOKEN ENV PUBLIC_APPWRITE_ENDPOINT=$PUBLIC_APPWRITE_ENDPOINT ENV PUBLIC_GROWTH_ENDPOINT=$PUBLIC_GROWTH_ENDPOINT ENV PUBLIC_CONSOLE_MODE=$PUBLIC_CONSOLE_MODE ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY +ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN +ENV NODE_OPTIONS=--max_old_space_size=8192 RUN pnpm run sync && pnpm run build diff --git a/compose.yml b/compose.yml index 6d26917d5..cfbb54934 100644 --- a/compose.yml +++ b/compose.yml @@ -8,6 +8,7 @@ services: PUBLIC_APPWRITE_ENDPOINT: ${PUBLIC_APPWRITE_ENDPOINT} PUBLIC_GROWTH_ENDPOINT: ${PUBLIC_GROWTH_ENDPOINT} PUBLIC_STRIPE_KEY: ${PUBLIC_STRIPE_KEY} + SENTRY_AUTH_TOKEN: ${SENTRY_AUTH_TOKEN} develop: watch: - action: rebuild diff --git a/package.json b/package.json index 0cd53ba74..932fcb5e1 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@appwrite.io/pink": "0.25.0", "@appwrite.io/pink-icons": "0.25.0", "@popperjs/core": "^2.11.8", + "@sentry/sveltekit": "^8.26.0", "@stripe/stripe-js": "^3.5.0", "ai": "^2.2.37", "analytics": "^0.8.14", diff --git a/src/hooks.client.ts b/src/hooks.client.ts index ad54c45cc..c6404ee81 100644 --- a/src/hooks.client.ts +++ b/src/hooks.client.ts @@ -1,14 +1,27 @@ +import * as Sentry from '@sentry/sveltekit'; import { AppwriteException } from '@appwrite.io/console'; import type { HandleClientError } from '@sveltejs/kit'; +import { isCloud, isProd } from '$lib/system'; -export const handleError: HandleClientError = async ({ error, message, status }) => { - if (error instanceof AppwriteException) { - status = error.code === 0 ? undefined : error.code; - message = error.message; +Sentry.init({ + enabled: isCloud && isProd, + dsn: 'https://c7ce178bdedd486480317b72f282fd39@o1063647.ingest.us.sentry.io/4504158071422976', + tracesSampleRate: 1, + replaysSessionSampleRate: 0, + replaysOnErrorSampleRate: 1, + integrations: [Sentry.replayIntegration()] +}); + +export const handleError: HandleClientError = Sentry.handleErrorWithSentry( + async ({ error, message, status }) => { + if (error instanceof AppwriteException) { + status = error.code === 0 ? undefined : error.code; + message = error.message; + } + + return { + message, + status + }; } - - return { - message, - status - }; -}; +); diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 000000000..e54c183db --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,14 @@ +import { sequence } from '@sveltejs/kit/hooks'; +import { handleErrorWithSentry, sentryHandle } from '@sentry/sveltekit'; +import * as Sentry from '@sentry/sveltekit'; +import { isCloud, isProd } from '$lib/system'; + +Sentry.init({ + enabled: isCloud && isProd, + dsn: 'https://c7ce178bdedd486480317b72f282fd39@o1063647.ingest.us.sentry.io/4504158071422976', + tracesSampleRate: 1.0 +}); + +export const handle = sequence(sentryHandle()); + +export const handleError = handleErrorWithSentry(); diff --git a/src/lib/components/billing/planComparisonBox.svelte b/src/lib/components/billing/planComparisonBox.svelte index e297d8e75..c48dabfc3 100644 --- a/src/lib/components/billing/planComparisonBox.svelte +++ b/src/lib/components/billing/planComparisonBox.svelte @@ -1,7 +1,7 @@ -
+
(isOverflowing = v)}> { (p) => { newFile.$id = p.$id; newFile.progress = p.progress; - newFile.completed = p.progress === 100 ? true : false; + newFile.completed = p.progress === 100; updateFile(p.$id, newFile); } ); @@ -91,6 +91,7 @@ const createUploader = () => { removeFromQueue: (id: string) => { update((n) => { n.files = n.files.filter((f) => f.$id !== id); + n.isOpen = n.files.length !== 0; return n; }); }, diff --git a/src/lib/system.ts b/src/lib/system.ts index ac69dcffd..15f745577 100644 --- a/src/lib/system.ts +++ b/src/lib/system.ts @@ -23,5 +23,7 @@ export const ENV = { export const MODE = VARS.CONSOLE_MODE === Mode.CLOUD ? Mode.CLOUD : Mode.SELF_HOSTED; export const isCloud = MODE === Mode.CLOUD; export const isSelfHosted = MODE !== Mode.CLOUD; +export const isDev = ENV.DEV; +export const isProd = ENV.PROD; export const hasStripePublicKey = !!VARS.PUBLIC_STRIPE_KEY; export const GRACE_PERIOD_OVERRIDE = false; diff --git a/src/lib/wizards/functions/steps/createRepository.svelte b/src/lib/wizards/functions/steps/createRepository.svelte index d6126890c..e5610476d 100644 --- a/src/lib/wizards/functions/steps/createRepository.svelte +++ b/src/lib/wizards/functions/steps/createRepository.svelte @@ -50,7 +50,7 @@ const callbackState = { from: 'github', to: 'template', - step: '4', + step: '5', template: $template.id, templateConfig: JSON.stringify($templateConfig) }; diff --git a/src/routes/(console)/account/mfa.svelte b/src/routes/(console)/account/mfa.svelte index 38078c2ab..61d7e73af 100644 --- a/src/routes/(console)/account/mfa.svelte +++ b/src/routes/(console)/account/mfa.svelte @@ -7,6 +7,7 @@ import { sdk } from '$lib/stores/sdk'; import { AuthenticatorType, type Models } from '@appwrite.io/console'; import QrFrame from '$lib/images/qr2.svg'; + import { addNotification } from '$lib/stores/notifications'; export let showSetup = false; @@ -28,6 +29,10 @@ await sdk.forConsole.account.updateMfaAuthenticator(AuthenticatorType.Totp, code); await Promise.all([invalidate(Dependencies.ACCOUNT), invalidate(Dependencies.FACTORS)]); showSetup = false; + addNotification({ + type: 'success', + message: 'Authenticator app connected successfully' + }); trackEvent(Submit.AccountAuthenticatorUpdate); } catch (e) { error = e.message; diff --git a/src/routes/(console)/account/updateMfa.svelte b/src/routes/(console)/account/updateMfa.svelte index 077784d09..220fea907 100644 --- a/src/routes/(console)/account/updateMfa.svelte +++ b/src/routes/(console)/account/updateMfa.svelte @@ -109,13 +109,18 @@
-
+
-
- Authenticator app - +
+ Authenticator app + {#if $factors.totp} + connected + {/if} +
+ Use an authentication app to generate two-factor authentication codes.
@@ -141,7 +146,7 @@
-
+
@@ -185,7 +190,7 @@
-
+
diff --git a/src/routes/(console)/onboarding/+page.svelte b/src/routes/(console)/onboarding/+page.svelte index 1e4373c63..e714c1a5a 100644 --- a/src/routes/(console)/onboarding/+page.svelte +++ b/src/routes/(console)/onboarding/+page.svelte @@ -32,11 +32,11 @@ { value: BillingPlan.PRO, label: `${tierToPlan(BillingPlan.PRO).name} - ${formatCurrency($plansInfo.get(BillingPlan.PRO).price)}/month + add-ons` - }, - { - value: BillingPlan.SCALE, - label: `${tierToPlan(BillingPlan.SCALE).name} - ${formatCurrency($plansInfo.get(BillingPlan.SCALE).price)}/month + usage` } + // { + // value: BillingPlan.SCALE, + // label: `${tierToPlan(BillingPlan.SCALE).name} - ${formatCurrency($plansInfo.get(BillingPlan.SCALE).price)}/month + usage` + // } ] : []; diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 0e394e4a4..705b96abe 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -149,9 +149,9 @@ type: 'success', isHtml: true, message: ` - ${$organization.name} has been changed to ${ - tierToPlan(billingPlan).name - } plan.` + ${$organization.name} will change to ${ + tierToPlan(billingPlan).name + } plan at the end of the current billing cycle.` }); trackEvent(Submit.OrganizationDowngrade, { @@ -162,7 +162,7 @@ type: 'error', message: e.message }); - trackError(e, isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade); + trackError(e, Submit.OrganizationDowngrade); } } @@ -213,22 +213,12 @@ await invalidate(Dependencies.ORGANIZATION); await goto(`${base}/organization-${org.$id}`); - if (isUpgrade) { - addNotification({ - type: 'success', - message: 'Your organization has been upgraded' - }); - } else { - addNotification({ - type: 'success', - isHtml: true, - message: ` - ${$organization.name} will change to ${ - tierToPlan(billingPlan).name - } plan at the end of the current billing cycle.` - }); - } - trackEvent(isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade, { + addNotification({ + type: 'success', + message: 'Your organization has been upgraded' + }); + + trackEvent(Submit.OrganizationUpgrade, { plan: tierToPlan(billingPlan)?.name }); } catch (e) { @@ -236,7 +226,7 @@ type: 'error', message: e.message }); - trackError(e, isUpgrade ? Submit.OrganizationUpgrade : Submit.OrganizationDowngrade); + trackError(e, Submit.OrganizationUpgrade); } } @@ -324,6 +314,7 @@ bind:value={feedbackDowngradeReason} /> diff --git a/src/routes/(console)/organization-[organization]/createMember.svelte b/src/routes/(console)/organization-[organization]/createMember.svelte index 6e0509539..cbc3d0341 100644 --- a/src/routes/(console)/organization-[organization]/createMember.svelte +++ b/src/routes/(console)/organization-[organization]/createMember.svelte @@ -1,4 +1,5 @@ -
+
Template language
diff --git a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/+page.svelte b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/+page.svelte index 7b5de902a..89b73c96b 100644 --- a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/+page.svelte +++ b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/+page.svelte @@ -44,30 +44,11 @@ $: hasValidAttributes = $collection?.attributes?.some((attr) => attr.status === 'available'); - -
- Documents -
- -
- - - -
- -
+{#key $page.params.collection} + +
+ Documents +
+ + + +
+ +
+ +
+
-
- {#if hasAttributes && hasValidAttributes} - {#if data.documents.total} -
+ {#if hasAttributes && hasValidAttributes} + {#if data.documents.total} +
- - {:else if $hasPageQueries} - -
-
- Sorry, we couldn't find any documents. -

There are no documents that match your filters.

+ + {:else if $hasPageQueries} + +
+
+ Sorry, we couldn't find any documents. +

There are no documents that match your filters.

+
+
+ +
-
+ + {:else} + + {/if} + {:else} + (showCreateDropdown = true)}> +
+ Create an attribute to get started. +

+ Need a hand? Learn more in our documentation. +

+
+
+ + -
+
-
- {:else} - + {/if} - {:else} - (showCreateDropdown = true)}> -
- Create an attribute to get started. -

- Need a hand? Learn more in our documentation. -

-
-
- - - - -
-
- {/if} - + +{/key} diff --git a/src/routes/(console)/project-[project]/functions/function-[function]/+page.svelte b/src/routes/(console)/project-[project]/functions/function-[function]/+page.svelte index 620493a7d..36ba1d69e 100644 --- a/src/routes/(console)/project-[project]/functions/function-[function]/+page.svelte +++ b/src/routes/(console)/project-[project]/functions/function-[function]/+page.svelte @@ -96,7 +96,7 @@ }, { id: 'size', - title: 'Deployment size', + title: 'Size', type: 'integer', show: true, width: 140, diff --git a/src/routes/(console)/project-[project]/functions/function-[function]/deploymentDomains.svelte b/src/routes/(console)/project-[project]/functions/function-[function]/deploymentDomains.svelte index 68b475340..bd441f17c 100644 --- a/src/routes/(console)/project-[project]/functions/function-[function]/deploymentDomains.svelte +++ b/src/routes/(console)/project-[project]/functions/function-[function]/deploymentDomains.svelte @@ -8,14 +8,17 @@
- - + + {domain.rules[0].domain} - - -