From 71ae60071b4e6dd14eb11f293d048efe69ff5133 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Wed, 1 Apr 2026 18:45:23 +0530 Subject: [PATCH 01/10] Enable email verification in publish workflow --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4036ac982a..8335294ac5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -84,7 +84,7 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_APPWRITE_MULTI_REGION=true" - "PUBLIC_CONSOLE_EMAIL_VERIFICATION=false" + "PUBLIC_CONSOLE_EMAIL_VERIFICATION=true" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" From 6f67cbff2e57d0d324ae8da32186746bf3190b90 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Wed, 1 Apr 2026 19:18:11 +0530 Subject: [PATCH 02/10] use console var --- .env.example | 1 - .github/workflows/publish.yml | 4 ---- AGENTS.md | 1 - Dockerfile | 2 -- build.js | 1 - src/lib/system.ts | 1 - src/routes/(console)/+layout.ts | 24 ++++++++++++++++++++-- src/routes/(console)/verify-email/+page.ts | 16 ++++++++++++--- src/routes/+layout.ts | 11 +--------- 9 files changed, 36 insertions(+), 25 deletions(-) diff --git a/.env.example b/.env.example index 30973b2c1e..14f47df658 100644 --- a/.env.example +++ b/.env.example @@ -4,5 +4,4 @@ PUBLIC_APPWRITE_MULTI_REGION=false PUBLIC_APPWRITE_ENDPOINT=http://localhost/v1 PUBLIC_STRIPE_KEY= PUBLIC_GROWTH_ENDPOINT= -PUBLIC_CONSOLE_EMAIL_VERIFICATION=false PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=true \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8335294ac5..4bb77fbcbc 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,7 +41,6 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_APPWRITE_MULTI_REGION=true" - "PUBLIC_CONSOLE_EMAIL_VERIFICATION=true" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY }}" @@ -84,7 +83,6 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_APPWRITE_MULTI_REGION=true" - "PUBLIC_CONSOLE_EMAIL_VERIFICATION=true" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" @@ -124,7 +122,6 @@ jobs: build-args: | "PUBLIC_CONSOLE_MODE=self-hosted" "PUBLIC_APPWRITE_MULTI_REGION=false" - "PUBLIC_CONSOLE_EMAIL_VERIFICATION=false" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=true" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" @@ -164,7 +161,6 @@ jobs: build-args: | "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_APPWRITE_MULTI_REGION=false" - "PUBLIC_CONSOLE_EMAIL_VERIFICATION=false" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" diff --git a/AGENTS.md b/AGENTS.md index 86083b656c..993b448089 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -222,7 +222,6 @@ Set via `.env` (copy `.env.example`). All prefixed with `PUBLIC_` for SvelteKit: | `PUBLIC_STRIPE_KEY` | -- | Stripe public key (cloud only) | | `PUBLIC_GROWTH_ENDPOINT` | -- | Analytics endpoint | | `PUBLIC_CONSOLE_FEATURE_FLAGS` | -- | Feature flags | -| `PUBLIC_CONSOLE_EMAIL_VERIFICATION` | `false` | Require email verification | | `PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS` | `true` | Mock AI in dev | ## Common pitfalls diff --git a/Dockerfile b/Dockerfile index 691652e7cb..11440e89bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,6 @@ ADD ./static /app/static ARG PUBLIC_CONSOLE_MODE ARG PUBLIC_CONSOLE_FEATURE_FLAGS ARG PUBLIC_APPWRITE_MULTI_REGION -ARG PUBLIC_CONSOLE_EMAIL_VERIFICATION ARG PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ARG PUBLIC_APPWRITE_ENDPOINT ARG PUBLIC_GROWTH_ENDPOINT @@ -31,7 +30,6 @@ ENV PUBLIC_GROWTH_ENDPOINT=$PUBLIC_GROWTH_ENDPOINT ENV PUBLIC_CONSOLE_MODE=$PUBLIC_CONSOLE_MODE ENV PUBLIC_CONSOLE_FEATURE_FLAGS=$PUBLIC_CONSOLE_FEATURE_FLAGS ENV PUBLIC_APPWRITE_MULTI_REGION=$PUBLIC_APPWRITE_MULTI_REGION -ENV PUBLIC_CONSOLE_EMAIL_VERIFICATION=$PUBLIC_CONSOLE_EMAIL_VERIFICATION ENV PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=$PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY ENV PUBLIC_CONSOLE_FINGERPRINT_KEY=$PUBLIC_CONSOLE_FINGERPRINT_KEY diff --git a/build.js b/build.js index 1b99db2213..f852e5e9a9 100644 --- a/build.js +++ b/build.js @@ -28,7 +28,6 @@ async function main() { logEnv('MULTI REGION', env?.PUBLIC_APPWRITE_MULTI_REGION); logEnv('APPWRITE ENDPOINT', env?.PUBLIC_APPWRITE_ENDPOINT, 'relative'); logEnv('GROWTH ENDPOINT', env?.PUBLIC_GROWTH_ENDPOINT); - logEnv('CONSOLE EMAIL VERIFICATION', env?.PUBLIC_CONSOLE_EMAIL_VERIFICATION); logEnv('CONSOLE MOCK AI SUGGESTIONS', env?.PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS); log(); logDelimiter(); diff --git a/src/lib/system.ts b/src/lib/system.ts index 4532609925..0c9a44b76e 100644 --- a/src/lib/system.ts +++ b/src/lib/system.ts @@ -11,7 +11,6 @@ export const VARS = { APPWRITE_ENDPOINT: env.PUBLIC_APPWRITE_ENDPOINT ?? undefined, GROWTH_ENDPOINT: env.PUBLIC_GROWTH_ENDPOINT ?? undefined, PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined, - EMAIL_VERIFICATION: env.PUBLIC_CONSOLE_EMAIL_VERIFICATION === 'true', MOCK_AI_SUGGESTIONS: (env.PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ?? 'true') === 'true' }; diff --git a/src/routes/(console)/+layout.ts b/src/routes/(console)/+layout.ts index b221432942..74be72830c 100644 --- a/src/routes/(console)/+layout.ts +++ b/src/routes/(console)/+layout.ts @@ -7,9 +7,17 @@ import { makePlansMap } from '$lib/helpers/billing'; import { plansInfo as plansInfoStore } from '$lib/stores/billing'; import { normalizeConsoleVariables } from '$lib/helpers/domains'; import { syncServerTime } from '$lib/helpers/fingerprint'; +import { redirect } from '@sveltejs/kit'; +import { resolve } from '$app/paths'; -export const load: LayoutLoad = async ({ depends, parent }) => { - const { organizations, plansInfo } = await parent(); +function isEmailVerificationEnabledFromBackend(flag: string | undefined): boolean { + if (!flag) return false; + const normalized = flag.toLowerCase(); + return normalized === 'enabled' || normalized === 'true' || normalized === '1'; +} + +export const load: LayoutLoad = async ({ depends, parent, url }) => { + const { organizations, plansInfo, account } = await parent(); depends(Dependencies.RUNTIMES); depends(Dependencies.CONSOLE_VARIABLES); @@ -42,6 +50,18 @@ export const load: LayoutLoad = async ({ depends, parent }) => { const consoleVariables = normalizeConsoleVariables(rawConsoleVariables); + const emailVerificationEnabled = isEmailVerificationEnabledFromBackend( + consoleVariables._APP_CONSOLE_EMAIL_VERIFICATION + ); + + if (isCloud && account && !account.emailVerification && emailVerificationEnabled) { + const isVerifyEmailPage = url.pathname === resolve('/verify-email'); + + if (!isVerifyEmailPage) { + redirect(303, resolve('/verify-email')); + } + } + let fallbackPlansInfoArray = plansInfo; if (!fallbackPlansInfoArray) { fallbackPlansInfoArray = makePlansMap(plansArray); diff --git a/src/routes/(console)/verify-email/+page.ts b/src/routes/(console)/verify-email/+page.ts index d97e736667..dc3e1ae6d3 100644 --- a/src/routes/(console)/verify-email/+page.ts +++ b/src/routes/(console)/verify-email/+page.ts @@ -4,14 +4,24 @@ import type { PageLoad } from './$types'; import { Dependencies } from '$lib/constants'; import { sdk } from '$lib/stores/sdk'; import { addNotification } from '$lib/stores/notifications'; -import { VARS } from '$lib/system'; + +function isEmailVerificationEnabledFromBackend(flag: string | undefined): boolean { + if (!flag) return false; + const normalized = flag.toLowerCase(); + return normalized === 'enabled' || normalized === 'true' || normalized === '1'; +} export const load: PageLoad = async ({ parent, depends, url }) => { - if (!VARS.EMAIL_VERIFICATION) { + const { account, consoleVariables } = await parent(); + + const emailVerificationEnabled = isEmailVerificationEnabledFromBackend( + consoleVariables?._APP_CONSOLE_EMAIL_VERIFICATION + ); + + if (!emailVerificationEnabled) { redirect(303, resolve('/')); } - const { account } = await parent(); depends(Dependencies.ACCOUNT); const user = url.searchParams.get('userId') ?? null; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 5bdef81d7c..41d8c3e175 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -9,7 +9,7 @@ import { redirectTo } from './store'; import { resolve } from '$app/paths'; import type { Account } from '$lib/stores/user'; import { type AppwriteException, Platform } from '@appwrite.io/console'; -import { isCloud, VARS } from '$lib/system'; +import { isCloud } from '$lib/system'; import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect'; import { getTeamOrOrganizationList } from '$lib/stores/organization'; import { makePlansMap } from '$lib/helpers/billing'; @@ -32,15 +32,6 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { } if (account) { - if (isCloud && !account.emailVerification && VARS.EMAIL_VERIFICATION) { - const isConsoleRoute = route.id?.startsWith('/(console)'); - const isVerifyEmailPage = url.pathname === resolve('/verify-email'); - - if (isConsoleRoute && !isVerifyEmailPage) { - redirect(303, resolve('/verify-email')); - } - } - const plansInfo = await getPlatformPlans(); plansInfoStore.set(plansInfo); From 6dab360dd9ab35d794b28bcfc5277b59c63dd645 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Wed, 1 Apr 2026 19:50:52 +0530 Subject: [PATCH 03/10] used backend one --- bun.lock | 4 ++-- package.json | 2 +- src/lib/helpers/emailVerification.ts | 11 +++++++++++ src/routes/(console)/+layout.ts | 18 +++++++----------- src/routes/(console)/verify-email/+page.ts | 11 ++--------- 5 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 src/lib/helpers/emailVerification.ts diff --git a/bun.lock b/bun.lock index dd34a80e7d..a4f83a4c1f 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "@appwrite/console", "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@d223f36", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@467cd21", "@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", @@ -113,7 +113,7 @@ "@analytics/type-utils": ["@analytics/type-utils@0.6.4", "", {}, "sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw=="], - "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@d223f36", { "dependencies": { "json-bigint": "1.0.0" } }], + "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@467cd21", { "dependencies": { "json-bigint": "1.0.0" } }], "@appwrite.io/pink-icons": ["@appwrite.io/pink-icons@0.25.0", "", {}, "sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q=="], diff --git a/package.json b/package.json index 588c697938..91e5325526 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@d223f36", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@467cd21", "@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", diff --git a/src/lib/helpers/emailVerification.ts b/src/lib/helpers/emailVerification.ts new file mode 100644 index 0000000000..8be3d008fb --- /dev/null +++ b/src/lib/helpers/emailVerification.ts @@ -0,0 +1,11 @@ +import type { Models } from '@appwrite.io/console'; + +export function isEmailVerificationEnabled( + consoleVariables: Models.ConsoleVariables | undefined +): boolean { + if (!consoleVariables) { + return false; + } + + return String(consoleVariables._APP_CONSOLE_EMAIL_VERIFICATION) === '1'; +} diff --git a/src/routes/(console)/+layout.ts b/src/routes/(console)/+layout.ts index 74be72830c..8f628c5a6a 100644 --- a/src/routes/(console)/+layout.ts +++ b/src/routes/(console)/+layout.ts @@ -9,12 +9,7 @@ import { normalizeConsoleVariables } from '$lib/helpers/domains'; import { syncServerTime } from '$lib/helpers/fingerprint'; import { redirect } from '@sveltejs/kit'; import { resolve } from '$app/paths'; - -function isEmailVerificationEnabledFromBackend(flag: string | undefined): boolean { - if (!flag) return false; - const normalized = flag.toLowerCase(); - return normalized === 'enabled' || normalized === 'true' || normalized === '1'; -} +import { isEmailVerificationEnabled } from '$lib/helpers/emailVerification'; export const load: LayoutLoad = async ({ depends, parent, url }) => { const { organizations, plansInfo, account } = await parent(); @@ -50,11 +45,12 @@ export const load: LayoutLoad = async ({ depends, parent, url }) => { const consoleVariables = normalizeConsoleVariables(rawConsoleVariables); - const emailVerificationEnabled = isEmailVerificationEnabledFromBackend( - consoleVariables._APP_CONSOLE_EMAIL_VERIFICATION - ); - - if (isCloud && account && !account.emailVerification && emailVerificationEnabled) { + if ( + isCloud && + account && + !account.emailVerification && + isEmailVerificationEnabled(consoleVariables) + ) { const isVerifyEmailPage = url.pathname === resolve('/verify-email'); if (!isVerifyEmailPage) { diff --git a/src/routes/(console)/verify-email/+page.ts b/src/routes/(console)/verify-email/+page.ts index dc3e1ae6d3..f74bc448c7 100644 --- a/src/routes/(console)/verify-email/+page.ts +++ b/src/routes/(console)/verify-email/+page.ts @@ -4,19 +4,12 @@ import type { PageLoad } from './$types'; import { Dependencies } from '$lib/constants'; import { sdk } from '$lib/stores/sdk'; import { addNotification } from '$lib/stores/notifications'; - -function isEmailVerificationEnabledFromBackend(flag: string | undefined): boolean { - if (!flag) return false; - const normalized = flag.toLowerCase(); - return normalized === 'enabled' || normalized === 'true' || normalized === '1'; -} +import { isEmailVerificationEnabled } from '$lib/helpers/emailVerification'; export const load: PageLoad = async ({ parent, depends, url }) => { const { account, consoleVariables } = await parent(); - const emailVerificationEnabled = isEmailVerificationEnabledFromBackend( - consoleVariables?._APP_CONSOLE_EMAIL_VERIFICATION - ); + const emailVerificationEnabled = isEmailVerificationEnabled(consoleVariables); if (!emailVerificationEnabled) { redirect(303, resolve('/')); From 42393f806c3557c9885686babaec2f77d53079ab Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Thu, 2 Apr 2026 11:06:52 +0530 Subject: [PATCH 04/10] fix comparsion --- src/lib/helpers/emailVerification.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/helpers/emailVerification.ts b/src/lib/helpers/emailVerification.ts index 8be3d008fb..7d5a93e04c 100644 --- a/src/lib/helpers/emailVerification.ts +++ b/src/lib/helpers/emailVerification.ts @@ -7,5 +7,5 @@ export function isEmailVerificationEnabled( return false; } - return String(consoleVariables._APP_CONSOLE_EMAIL_VERIFICATION) === '1'; + return String(consoleVariables._APP_CONSOLE_EMAIL_VERIFICATION) === 'true'; } From d204d6b4ce3d33c21038dddf93e60767c09d4302 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Thu, 2 Apr 2026 12:29:57 +0530 Subject: [PATCH 05/10] fix frontend --- src/lib/helpers/emailVerification.ts | 6 ++++ src/routes/(console)/+error.svelte | 12 ++++++-- src/routes/(console)/+layout.ts | 33 +++++++++----------- src/routes/+layout.ts | 46 ++++++++++++++++++++++------ 4 files changed, 68 insertions(+), 29 deletions(-) diff --git a/src/lib/helpers/emailVerification.ts b/src/lib/helpers/emailVerification.ts index 7d5a93e04c..0ff81bf743 100644 --- a/src/lib/helpers/emailVerification.ts +++ b/src/lib/helpers/emailVerification.ts @@ -1,5 +1,7 @@ import type { Models } from '@appwrite.io/console'; +const EMAIL_VERIFICATION_REQUIRED_ERROR_TYPE = 'user_email_not_verified'; + export function isEmailVerificationEnabled( consoleVariables: Models.ConsoleVariables | undefined ): boolean { @@ -9,3 +11,7 @@ export function isEmailVerificationEnabled( return String(consoleVariables._APP_CONSOLE_EMAIL_VERIFICATION) === 'true'; } + +export function isEmailVerificationRequiredError(type: string | undefined): boolean { + return type === EMAIL_VERIFICATION_REQUIRED_ERROR_TYPE; +} diff --git a/src/routes/(console)/+error.svelte b/src/routes/(console)/+error.svelte index 7c01dcbb27..6bdd3495bc 100644 --- a/src/routes/(console)/+error.svelte +++ b/src/routes/(console)/+error.svelte @@ -1,9 +1,17 @@ - diff --git a/src/routes/(console)/+layout.ts b/src/routes/(console)/+layout.ts index 8f628c5a6a..a2058f17ac 100644 --- a/src/routes/(console)/+layout.ts +++ b/src/routes/(console)/+layout.ts @@ -2,17 +2,17 @@ import { sdk } from '$lib/stores/sdk'; import { isCloud } from '$lib/system'; import type { LayoutLoad } from './$types'; import { Dependencies } from '$lib/constants'; -import { Platform, Query } from '@appwrite.io/console'; +import { AppwriteException, Platform, Query } from '@appwrite.io/console'; import { makePlansMap } from '$lib/helpers/billing'; import { plansInfo as plansInfoStore } from '$lib/stores/billing'; import { normalizeConsoleVariables } from '$lib/helpers/domains'; import { syncServerTime } from '$lib/helpers/fingerprint'; import { redirect } from '@sveltejs/kit'; import { resolve } from '$app/paths'; -import { isEmailVerificationEnabled } from '$lib/helpers/emailVerification'; +import { isEmailVerificationRequiredError } from '$lib/helpers/emailVerification'; -export const load: LayoutLoad = async ({ depends, parent, url }) => { - const { organizations, plansInfo, account } = await parent(); +export const load: LayoutLoad = async ({ depends, parent }) => { + const { organizations, plansInfo } = await parent(); depends(Dependencies.RUNTIMES); depends(Dependencies.CONSOLE_VARIABLES); @@ -41,22 +41,15 @@ export const load: LayoutLoad = async ({ depends, parent, url }) => { return response.json() as { version?: string }; }), sdk.forConsole.console.variables() - ]); - - const consoleVariables = normalizeConsoleVariables(rawConsoleVariables); - - if ( - isCloud && - account && - !account.emailVerification && - isEmailVerificationEnabled(consoleVariables) - ) { - const isVerifyEmailPage = url.pathname === resolve('/verify-email'); - - if (!isVerifyEmailPage) { + ]).catch((error) => { + if (error instanceof AppwriteException && isEmailVerificationRequiredError(error.type)) { redirect(303, resolve('/verify-email')); } - } + + throw error; + }); + + const consoleVariables = normalizeConsoleVariables(rawConsoleVariables); let fallbackPlansInfoArray = plansInfo; if (!fallbackPlansInfoArray) { @@ -81,6 +74,10 @@ export const load: LayoutLoad = async ({ depends, parent, url }) => { }) ).total; } catch (e) { + if (e instanceof AppwriteException && isEmailVerificationRequiredError(e.type)) { + redirect(303, resolve('/verify-email')); + } + projectsCount = 0; } } diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 41d8c3e175..9db343ecba 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -8,12 +8,13 @@ import type { LayoutLoad } from './$types'; import { redirectTo } from './store'; import { resolve } from '$app/paths'; import type { Account } from '$lib/stores/user'; -import { type AppwriteException, Platform } from '@appwrite.io/console'; +import { AppwriteException, Platform } from '@appwrite.io/console'; import { isCloud } from '$lib/system'; import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect'; import { getTeamOrOrganizationList } from '$lib/stores/organization'; import { makePlansMap } from '$lib/helpers/billing'; import { plansInfo as plansInfoStore } from '$lib/stores/billing'; +import { isEmailVerificationRequiredError } from '$lib/helpers/emailVerification'; export const ssr = false; @@ -32,14 +33,33 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { } if (account) { - const plansInfo = await getPlatformPlans(); - plansInfoStore.set(plansInfo); - - return { - plansInfo, - account: account, - organizations: await getTeamOrOrganizationList() - }; + try { + const [plansInfo, organizations] = await Promise.all([ + getPlatformPlans(), + getTeamOrOrganizationList() + ]); + + plansInfoStore.set(plansInfo); + + return { + plansInfo, + account: account, + organizations + }; + } catch (error) { + if ( + error instanceof AppwriteException && + isEmailVerificationRequiredError(error.type) + ) { + const verifyEmailUrl = resolve('/verify-email'); + + if (url.pathname !== verifyEmailUrl) { + redirect(303, withParams(verifyEmailUrl, url.searchParams)); + } + } + + throw error; + } } const isPublicRoute = route.id?.startsWith('/(public)'); @@ -57,6 +77,14 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { redirect(303, withParams(mfaUrl, url.searchParams)); } + if (isEmailVerificationRequiredError(error.type)) { + const verifyEmailUrl = resolve('/verify-email'); + + if (url.pathname !== verifyEmailUrl) { + redirect(303, withParams(verifyEmailUrl, url.searchParams)); + } + } + if (!isPublicRoute) { if (isCloud) { checkPricingRefAndRedirect(url.searchParams, true); From 0e963cdfe4924a38a191b7a56c46186ba3140cdb Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Thu, 2 Apr 2026 14:04:48 +0530 Subject: [PATCH 06/10] remove llop --- src/routes/(console)/+layout.ts | 15 ++++++++++----- src/routes/(console)/verify-email/+page.ts | 9 +-------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/routes/(console)/+layout.ts b/src/routes/(console)/+layout.ts index a2058f17ac..e80e95de2f 100644 --- a/src/routes/(console)/+layout.ts +++ b/src/routes/(console)/+layout.ts @@ -11,7 +11,7 @@ import { redirect } from '@sveltejs/kit'; import { resolve } from '$app/paths'; import { isEmailVerificationRequiredError } from '$lib/helpers/emailVerification'; -export const load: LayoutLoad = async ({ depends, parent }) => { +export const load: LayoutLoad = async ({ depends, parent, url }) => { const { organizations, plansInfo } = await parent(); depends(Dependencies.RUNTIMES); @@ -19,6 +19,11 @@ export const load: LayoutLoad = async ({ depends, parent }) => { depends(Dependencies.ORGANIZATION); const { endpoint, project } = sdk.forConsole.client.config; + const verifyEmailUrl = resolve('/verify-email'); + const shouldRedirectToVerifyEmail = (error: unknown) => + error instanceof AppwriteException && + isEmailVerificationRequiredError(error.type) && + url.pathname !== verifyEmailUrl; const plansArrayPromise = plansInfo || !isCloud @@ -42,8 +47,8 @@ export const load: LayoutLoad = async ({ depends, parent }) => { }), sdk.forConsole.console.variables() ]).catch((error) => { - if (error instanceof AppwriteException && isEmailVerificationRequiredError(error.type)) { - redirect(303, resolve('/verify-email')); + if (shouldRedirectToVerifyEmail(error)) { + redirect(303, verifyEmailUrl); } throw error; @@ -74,8 +79,8 @@ export const load: LayoutLoad = async ({ depends, parent }) => { }) ).total; } catch (e) { - if (e instanceof AppwriteException && isEmailVerificationRequiredError(e.type)) { - redirect(303, resolve('/verify-email')); + if (shouldRedirectToVerifyEmail(e)) { + redirect(303, verifyEmailUrl); } projectsCount = 0; diff --git a/src/routes/(console)/verify-email/+page.ts b/src/routes/(console)/verify-email/+page.ts index f74bc448c7..c97cca8dc0 100644 --- a/src/routes/(console)/verify-email/+page.ts +++ b/src/routes/(console)/verify-email/+page.ts @@ -4,16 +4,9 @@ import type { PageLoad } from './$types'; import { Dependencies } from '$lib/constants'; import { sdk } from '$lib/stores/sdk'; import { addNotification } from '$lib/stores/notifications'; -import { isEmailVerificationEnabled } from '$lib/helpers/emailVerification'; export const load: PageLoad = async ({ parent, depends, url }) => { - const { account, consoleVariables } = await parent(); - - const emailVerificationEnabled = isEmailVerificationEnabled(consoleVariables); - - if (!emailVerificationEnabled) { - redirect(303, resolve('/')); - } + const { account } = await parent(); depends(Dependencies.ACCOUNT); From 6110c0c22bf146020b90ba3fe16123b4b93efeec Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Thu, 2 Apr 2026 14:27:44 +0530 Subject: [PATCH 07/10] fix --- src/hooks.client.ts | 5 ++- src/lib/helpers/emailVerification.ts | 30 +++++++++++++- src/routes/(console)/+error.svelte | 4 +- src/routes/(console)/+layout.ts | 58 ++++++++++++++++++++++++---- src/routes/+layout.ts | 48 ++++++++++++++++------- 5 files changed, 118 insertions(+), 27 deletions(-) diff --git a/src/hooks.client.ts b/src/hooks.client.ts index 4a6565025a..44da3b9075 100644 --- a/src/hooks.client.ts +++ b/src/hooks.client.ts @@ -1,6 +1,7 @@ import * as Sentry from '@sentry/sveltekit'; import { isCloud, isProd } from '$lib/system'; import { AppwriteException } from '@appwrite.io/console'; +import { isVerifyEmailRedirectError } from '$lib/helpers/emailVerification'; import type { HandleClientError } from '@sveltejs/kit'; Sentry.init({ @@ -12,7 +13,9 @@ Sentry.init({ }); export const handleError: HandleClientError = ({ error, message, status }) => { - console.error(error); + if (!isVerifyEmailRedirectError(error)) { + console.error(error); + } let type; if (error instanceof AppwriteException) { diff --git a/src/lib/helpers/emailVerification.ts b/src/lib/helpers/emailVerification.ts index 0ff81bf743..f4dba24176 100644 --- a/src/lib/helpers/emailVerification.ts +++ b/src/lib/helpers/emailVerification.ts @@ -1,6 +1,7 @@ -import type { Models } from '@appwrite.io/console'; +import { AppwriteException, type Models } from '@appwrite.io/console'; const EMAIL_VERIFICATION_REQUIRED_ERROR_TYPE = 'user_email_not_verified'; +const CONSOLE_ACCOUNT_VERIFICATION_REQUIRED_TYPE = 'console_account_verification_required'; export function isEmailVerificationEnabled( consoleVariables: Models.ConsoleVariables | undefined @@ -15,3 +16,30 @@ export function isEmailVerificationEnabled( export function isEmailVerificationRequiredError(type: string | undefined): boolean { return type === EMAIL_VERIFICATION_REQUIRED_ERROR_TYPE; } + +function matchesVerifyEmailGate(type: string | undefined, message: string | undefined): boolean { + if (isEmailVerificationRequiredError(type)) return true; + if (type === CONSOLE_ACCOUNT_VERIFICATION_REQUIRED_TYPE) return true; + if (message?.includes('Console account verification is required')) return true; + return false; +} + +/** + * Console APIs may return `user_email_not_verified`, `console_account_verification_required`, + * or a message-only error when email verification is enforced for the console account. + */ +export function isVerifyEmailRedirectError(error: unknown): boolean { + if (error instanceof AppwriteException) { + return matchesVerifyEmailGate(error.type, error.message); + } + if (typeof error === 'object' && error !== null && 'message' in error) { + const msg = (error as { message: unknown }).message; + if (typeof msg !== 'string') return false; + const typ = + 'type' in error && typeof (error as { type: unknown }).type === 'string' + ? (error as { type: string }).type + : undefined; + return matchesVerifyEmailGate(typ, msg); + } + return false; +} diff --git a/src/routes/(console)/+error.svelte b/src/routes/(console)/+error.svelte index 6bdd3495bc..57691dd85a 100644 --- a/src/routes/(console)/+error.svelte +++ b/src/routes/(console)/+error.svelte @@ -3,12 +3,12 @@ import { base, resolve } from '$app/paths'; import { page } from '$app/state'; import { Button } from '$lib/elements/forms'; - import { isEmailVerificationRequiredError } from '$lib/helpers/emailVerification'; + import { isVerifyEmailRedirectError } from '$lib/helpers/emailVerification'; import { Container } from '$lib/layout'; import { Typography } from '@appwrite.io/pink-svelte'; $effect(() => { - if (isEmailVerificationRequiredError(page.error.type)) { + if (isVerifyEmailRedirectError(page.error)) { goto(resolve('/verify-email'), { replaceState: true }); } }); diff --git a/src/routes/(console)/+layout.ts b/src/routes/(console)/+layout.ts index e80e95de2f..2904bb5c6b 100644 --- a/src/routes/(console)/+layout.ts +++ b/src/routes/(console)/+layout.ts @@ -1,29 +1,71 @@ import { sdk } from '$lib/stores/sdk'; import { isCloud } from '$lib/system'; import type { LayoutLoad } from './$types'; +import type { Account } from '$lib/stores/user'; import { Dependencies } from '$lib/constants'; -import { AppwriteException, Platform, Query } from '@appwrite.io/console'; +import { Platform, Query, type Models } from '@appwrite.io/console'; import { makePlansMap } from '$lib/helpers/billing'; import { plansInfo as plansInfoStore } from '$lib/stores/billing'; import { normalizeConsoleVariables } from '$lib/helpers/domains'; import { syncServerTime } from '$lib/helpers/fingerprint'; import { redirect } from '@sveltejs/kit'; import { resolve } from '$app/paths'; -import { isEmailVerificationRequiredError } from '$lib/helpers/emailVerification'; +import { isVerifyEmailRedirectError } from '$lib/helpers/emailVerification'; export const load: LayoutLoad = async ({ depends, parent, url }) => { - const { organizations, plansInfo } = await parent(); + const parentData = await parent(); + const { organizations, plansInfo } = parentData; + const account = parentData.account as Account | undefined; + + const { endpoint, project } = sdk.forConsole.client.config; + const verifyEmailUrl = resolve('/verify-email'); + + // While unverified, several console APIs (not only teams) may return 401; avoid failing the layout. + if (url.pathname === verifyEmailUrl && account && !account.emailVerification) { + depends(Dependencies.RUNTIMES); + depends(Dependencies.CONSOLE_VARIABLES); + depends(Dependencies.ORGANIZATION); + + const [preferences, rawConsoleVariables, versionData] = await Promise.all([ + sdk.forConsole.account.getPrefs(), + sdk.forConsole.console.variables().catch(() => ({}) as Models.ConsoleVariables), + fetch(`${endpoint}/health/version`, { + headers: { 'X-Appwrite-Project': project as string } + }) + .then(async (response) => { + const dateHeader = response.headers.get('Date'); + const parsed = dateHeader ? new Date(dateHeader).getTime() : NaN; + if (Number.isFinite(parsed)) { + syncServerTime(Math.floor(parsed / 1000)); + } + return response.json() as { version?: string }; + }) + .catch(() => null) + ]); + + const consoleVariables = normalizeConsoleVariables(rawConsoleVariables); + + plansInfoStore.set(plansInfo ?? null); + + return { + roles: [], + scopes: [], + preferences, + currentOrgId: undefined, + organizations, + consoleVariables, + allProjectsCount: 0, + plansInfo: plansInfo ?? null, + version: versionData?.version ?? null + }; + } depends(Dependencies.RUNTIMES); depends(Dependencies.CONSOLE_VARIABLES); depends(Dependencies.ORGANIZATION); - const { endpoint, project } = sdk.forConsole.client.config; - const verifyEmailUrl = resolve('/verify-email'); const shouldRedirectToVerifyEmail = (error: unknown) => - error instanceof AppwriteException && - isEmailVerificationRequiredError(error.type) && - url.pathname !== verifyEmailUrl; + isVerifyEmailRedirectError(error) && url.pathname !== verifyEmailUrl; const plansArrayPromise = plansInfo || !isCloud diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 9db343ecba..0f566dc91e 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -8,20 +8,24 @@ import type { LayoutLoad } from './$types'; import { redirectTo } from './store'; import { resolve } from '$app/paths'; import type { Account } from '$lib/stores/user'; -import { AppwriteException, Platform } from '@appwrite.io/console'; +import { AppwriteException, Platform, type Models } from '@appwrite.io/console'; import { isCloud } from '$lib/system'; import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect'; import { getTeamOrOrganizationList } from '$lib/stores/organization'; import { makePlansMap } from '$lib/helpers/billing'; import { plansInfo as plansInfoStore } from '$lib/stores/billing'; -import { isEmailVerificationRequiredError } from '$lib/helpers/emailVerification'; +import { isVerifyEmailRedirectError } from '$lib/helpers/emailVerification'; export const ssr = false; +const EMPTY_ORGANIZATIONS: Models.TeamList = { total: 0, teams: [] }; + export const load: LayoutLoad = async ({ depends, url, route }) => { depends(Dependencies.ACCOUNT); depends(Dependencies.ORGANIZATIONS); + const verifyEmailPath = resolve('/verify-email'); + const [account, error] = (await sdk.forConsole.account .get() .then((response) => [response, null]) @@ -33,6 +37,18 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { } if (account) { + // `/v1/teams` (and org list on cloud) returns 401 until the console account is verified; + // do not call that API on this route while still unverified. + if (url.pathname === verifyEmailPath && !account.emailVerification) { + const plansInfo = await getPlatformPlans(); + plansInfoStore.set(plansInfo); + return { + plansInfo, + account, + organizations: EMPTY_ORGANIZATIONS + }; + } + try { const [plansInfo, organizations] = await Promise.all([ getPlatformPlans(), @@ -47,15 +63,19 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { organizations }; } catch (error) { - if ( - error instanceof AppwriteException && - isEmailVerificationRequiredError(error.type) - ) { - const verifyEmailUrl = resolve('/verify-email'); - - if (url.pathname !== verifyEmailUrl) { - redirect(303, withParams(verifyEmailUrl, url.searchParams)); + if (isVerifyEmailRedirectError(error)) { + if (url.pathname !== verifyEmailPath) { + redirect(303, withParams(verifyEmailPath, url.searchParams)); } + + // Already on verify-email: do not rethrow; the teams API is blocked until verified. + const plansInfo = await getPlatformPlans(); + plansInfoStore.set(plansInfo); + return { + plansInfo, + account, + organizations: EMPTY_ORGANIZATIONS + }; } throw error; @@ -77,11 +97,9 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { redirect(303, withParams(mfaUrl, url.searchParams)); } - if (isEmailVerificationRequiredError(error.type)) { - const verifyEmailUrl = resolve('/verify-email'); - - if (url.pathname !== verifyEmailUrl) { - redirect(303, withParams(verifyEmailUrl, url.searchParams)); + if (isVerifyEmailRedirectError(error)) { + if (url.pathname !== verifyEmailPath) { + redirect(303, withParams(verifyEmailPath, url.searchParams)); } } From 213a54ebeb5421495f57f01578bc3e3cfed7fda3 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Thu, 2 Apr 2026 15:46:00 +0530 Subject: [PATCH 08/10] fix --- src/lib/stores/billing.ts | 35 +++++++++++++++++++++++++++++------ src/routes/+layout.ts | 6 +++--- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index 1589dec057..cdd3fc53bc 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -78,10 +78,16 @@ export const showBudgetAlert = derived( ); function getPlansInfoStore(): BillingPlansMap | null { - return get(plansInfo) ?? get(page).data?.plansInfo ?? null; + return get(plansInfo) ?? get(page).data?.plansInfo ?? new Map(); } -function makeBillingPlan(billingPlanOrId: string | Models.BillingPlan): Models.BillingPlan { +function makeBillingPlan( + billingPlanOrId: string | Models.BillingPlan | null | undefined +): Models.BillingPlan | null { + if (!billingPlanOrId) { + return null; + } + return typeof billingPlanOrId === 'string' ? billingIdToPlan(billingPlanOrId) : billingPlanOrId; } @@ -89,21 +95,35 @@ export function getRoleLabel(role: string) { return roles.find((r) => r.value === role)?.label ?? role; } -export function isStarterPlan(billingPlanOrId: string | Models.BillingPlan): boolean { +export function isStarterPlan( + billingPlanOrId: string | Models.BillingPlan | null | undefined +): boolean { const billingPlan = makeBillingPlan(billingPlanOrId); return planHasGroup(billingPlan, BillingPlanGroup.Starter); } -export function canUpgrade(billingPlanOrId: string | Models.BillingPlan): boolean { +export function canUpgrade( + billingPlanOrId: string | Models.BillingPlan | null | undefined +): boolean { const billingPlan = makeBillingPlan(billingPlanOrId); + if (!billingPlan?.$id) { + return false; + } + const nextTier = getNextTierBillingPlan(billingPlan.$id); // defaults back to PRO, so adjust the check! return billingPlan.$id !== nextTier.$id; } -export function canDowngrade(billingPlanOrId: string | Models.BillingPlan): boolean { +export function canDowngrade( + billingPlanOrId: string | Models.BillingPlan | null | undefined +): boolean { const billingPlan = makeBillingPlan(billingPlanOrId); + if (!billingPlan?.$id) { + return false; + } + const nextTier = getPreviousTierBillingPlan(billingPlan.$id); // defaults back to Starter, so adjust the check! @@ -111,7 +131,7 @@ export function canDowngrade(billingPlanOrId: string | Models.BillingPlan): bool } export function planHasGroup( - billingPlanOrId: string | Models.BillingPlan, + billingPlanOrId: string | Models.BillingPlan | null | undefined, group: BillingPlanGroup ): boolean { const billingPlan = makeBillingPlan(billingPlanOrId); @@ -567,6 +587,9 @@ export function checkForMarkedForDeletion(org: Models.Organization) { export async function checkForMissingPaymentMethod() { const starterPlan = getBasePlanFromGroup(BillingPlanGroup.Starter); + if (!starterPlan?.$id) { + return; + } const orgs = await sdk.forConsole.organizations.list({ queries: [ diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 0f566dc91e..4ebbe0eeef 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -39,8 +39,8 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { if (account) { // `/v1/teams` (and org list on cloud) returns 401 until the console account is verified; // do not call that API on this route while still unverified. - if (url.pathname === verifyEmailPath && !account.emailVerification) { - const plansInfo = await getPlatformPlans(); + if (url.pathname === verifyEmailPath) { + const plansInfo = await getPlatformPlans().catch(() => null); plansInfoStore.set(plansInfo); return { plansInfo, @@ -69,7 +69,7 @@ export const load: LayoutLoad = async ({ depends, url, route }) => { } // Already on verify-email: do not rethrow; the teams API is blocked until verified. - const plansInfo = await getPlatformPlans(); + const plansInfo = await getPlatformPlans().catch(() => null); plansInfoStore.set(plansInfo); return { plansInfo, From fc43e009bed9dc948d9274726ccbbdea5660adfd Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Thu, 2 Apr 2026 17:23:18 +0530 Subject: [PATCH 09/10] remove code not needed --- src/lib/helpers/emailVerification.ts | 47 +++++++++------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/lib/helpers/emailVerification.ts b/src/lib/helpers/emailVerification.ts index f4dba24176..7fe96eb700 100644 --- a/src/lib/helpers/emailVerification.ts +++ b/src/lib/helpers/emailVerification.ts @@ -1,45 +1,28 @@ -import { AppwriteException, type Models } from '@appwrite.io/console'; +import { AppwriteException } from '@appwrite.io/console'; -const EMAIL_VERIFICATION_REQUIRED_ERROR_TYPE = 'user_email_not_verified'; -const CONSOLE_ACCOUNT_VERIFICATION_REQUIRED_TYPE = 'console_account_verification_required'; - -export function isEmailVerificationEnabled( - consoleVariables: Models.ConsoleVariables | undefined -): boolean { - if (!consoleVariables) { - return false; - } - - return String(consoleVariables._APP_CONSOLE_EMAIL_VERIFICATION) === 'true'; -} - -export function isEmailVerificationRequiredError(type: string | undefined): boolean { - return type === EMAIL_VERIFICATION_REQUIRED_ERROR_TYPE; -} - -function matchesVerifyEmailGate(type: string | undefined, message: string | undefined): boolean { - if (isEmailVerificationRequiredError(type)) return true; - if (type === CONSOLE_ACCOUNT_VERIFICATION_REQUIRED_TYPE) return true; - if (message?.includes('Console account verification is required')) return true; - return false; -} - -/** - * Console APIs may return `user_email_not_verified`, `console_account_verification_required`, - * or a message-only error when email verification is enforced for the console account. - */ +/** True when access is blocked until the console account email is verified. */ export function isVerifyEmailRedirectError(error: unknown): boolean { if (error instanceof AppwriteException) { - return matchesVerifyEmailGate(error.type, error.message); + return ( + error.type === 'user_email_not_verified' || + error.type === 'console_account_verification_required' || + (error.message?.includes('Console account verification is required') ?? false) + ); } - if (typeof error === 'object' && error !== null && 'message' in error) { + + if (error && typeof error === 'object' && 'message' in error) { const msg = (error as { message: unknown }).message; if (typeof msg !== 'string') return false; const typ = 'type' in error && typeof (error as { type: unknown }).type === 'string' ? (error as { type: string }).type : undefined; - return matchesVerifyEmailGate(typ, msg); + return ( + typ === 'user_email_not_verified' || + typ === 'console_account_verification_required' || + msg.includes('Console account verification is required') + ); } + return false; } From 740bdb4e247b6bc259ab704c36f79e28b8dc08ea Mon Sep 17 00:00:00 2001 From: Harsh Mahajan Date: Thu, 2 Apr 2026 17:32:17 +0530 Subject: [PATCH 10/10] address greptile comments --- src/routes/(console)/+error.svelte | 5 +++-- src/routes/(console)/+layout.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/routes/(console)/+error.svelte b/src/routes/(console)/+error.svelte index 57691dd85a..41863e61a4 100644 --- a/src/routes/(console)/+error.svelte +++ b/src/routes/(console)/+error.svelte @@ -8,8 +8,9 @@ import { Typography } from '@appwrite.io/pink-svelte'; $effect(() => { - if (isVerifyEmailRedirectError(page.error)) { - goto(resolve('/verify-email'), { replaceState: true }); + const verifyEmailPath = resolve('/verify-email'); + if (isVerifyEmailRedirectError(page.error) && page.url.pathname !== verifyEmailPath) { + goto(verifyEmailPath, { replaceState: true }); } }); diff --git a/src/routes/(console)/+layout.ts b/src/routes/(console)/+layout.ts index 2904bb5c6b..f40123b438 100644 --- a/src/routes/(console)/+layout.ts +++ b/src/routes/(console)/+layout.ts @@ -27,7 +27,7 @@ export const load: LayoutLoad = async ({ depends, parent, url }) => { depends(Dependencies.ORGANIZATION); const [preferences, rawConsoleVariables, versionData] = await Promise.all([ - sdk.forConsole.account.getPrefs(), + sdk.forConsole.account.getPrefs().catch(() => ({}) as Models.DefaultPreferences), sdk.forConsole.console.variables().catch(() => ({}) as Models.ConsoleVariables), fetch(`${endpoint}/health/version`, { headers: { 'X-Appwrite-Project': project as string }