From 045ff0509c459df8397b8a52957671a2fa9ec62d Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 1 Oct 2025 19:05:58 +0800 Subject: [PATCH] chore: fix login --- .../services/js-services/cache/index.ts | 4 ++- .../services/js-services/http/gotrue.ts | 35 ++++++++++++------- .../services/js-services/http/http_api.ts | 2 +- src/application/session/sign_in.ts | 10 +++--- src/pages/LoginPage.tsx | 26 +++++++++++++- 5 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/application/services/js-services/cache/index.ts b/src/application/services/js-services/cache/index.ts index 9785f427..40a51624 100644 --- a/src/application/services/js-services/cache/index.ts +++ b/src/application/services/js-services/cache/index.ts @@ -124,7 +124,6 @@ export async function getUser( strategy: StrategyType = StrategyType.CACHE_AND_NETWORK ) { const exist = userId && (await hasUserCache(userId)); - const data = await db.users.get(userId); switch (strategy) { case StrategyType.CACHE_ONLY: { @@ -132,6 +131,7 @@ export async function getUser( throw new Error('No cache found'); } + const data = await db.users.get(userId); return data; } @@ -140,6 +140,7 @@ export async function getUser( return revalidateUser(fetcher); } + const data = await db.users.get(userId); return data; } @@ -150,6 +151,7 @@ export async function getUser( void revalidateUser(fetcher); } + const data = await db.users.get(userId); return data; } diff --git a/src/application/services/js-services/http/gotrue.ts b/src/application/services/js-services/http/gotrue.ts index d5752ebd..e31738fe 100644 --- a/src/application/services/js-services/http/gotrue.ts +++ b/src/application/services/js-services/http/gotrue.ts @@ -184,24 +184,21 @@ export async function signInOTP({ }); const data = response?.data; - console.debug('signInOTP response data:', data); + console.log('[signInOTP] Response data:', data); if (data) { if (!data.code) { - // Save token to localStorage FIRST so axios interceptor can use it - console.debug('Saving token data:', data); + // Save token first so axios interceptor can use it + console.log('[signInOTP] Saving token to localStorage'); saveGoTrueAuth(JSON.stringify(data)); - if (type === 'magiclink') { - emit(EventType.SESSION_VALID); - } - // Verify token with AppFlowy Cloud to create user if needed + let isNewUser = false; try { - console.debug('[signInOTP] Calling verifyToken'); - await verifyToken(data.access_token); - - await new Promise(resolve => setTimeout(resolve, 500)); + console.log('[signInOTP] Calling verifyToken'); + const result = await verifyToken(data.access_token); + isNewUser = result.is_new; + console.log('[signInOTP] verifyToken completed, isNewUser:', isNewUser); } catch (error) { console.error('[signInOTP] Failed to verify token with AppFlowy Cloud:', error); emit(EventType.SESSION_INVALID); @@ -211,7 +208,21 @@ export async function signInOTP({ }); } - afterAuth(); + // Emit session valid only after everything is complete + if (type === 'magiclink') { + emit(EventType.SESSION_VALID); + } + + // For new users, always redirect to /app (don't use saved redirectTo) + if (isNewUser) { + console.log('[signInOTP] New user, clearing old data and redirecting to /app'); + localStorage.removeItem('redirectTo'); + // Use replace to avoid adding to history and ensure clean navigation + window.location.replace('/app'); + } else { + console.log('[signInOTP] Existing user, calling afterAuth'); + afterAuth(); + } } else { emit(EventType.SESSION_INVALID); return Promise.reject({ diff --git a/src/application/services/js-services/http/http_api.ts b/src/application/services/js-services/http/http_api.ts index 85915c14..a701989a 100644 --- a/src/application/services/js-services/http/http_api.ts +++ b/src/application/services/js-services/http/http_api.ts @@ -1,8 +1,8 @@ +import { getFileUploadUrl, getFileUrl } from '@/utils/file-storage-url'; import axios, { AxiosInstance, AxiosResponse } from 'axios'; import dayjs from 'dayjs'; import { omit } from 'lodash-es'; import { nanoid } from 'nanoid'; -import { getFileUploadUrl, getFileUrl } from '@/utils/file-storage-url'; import { GlobalComment, Reaction } from '@/application/comment.type'; import { ERROR_CODE } from '@/application/constants'; diff --git a/src/application/session/sign_in.ts b/src/application/session/sign_in.ts index df08f4ce..903816f2 100644 --- a/src/application/session/sign_in.ts +++ b/src/application/session/sign_in.ts @@ -1,19 +1,19 @@ -export function saveRedirectTo (redirectTo: string) { +export function saveRedirectTo(redirectTo: string) { localStorage.setItem('redirectTo', redirectTo); } -export function getRedirectTo () { +export function getRedirectTo() { return localStorage.getItem('redirectTo'); } -export function clearRedirectTo () { +export function clearRedirectTo() { localStorage.removeItem('redirectTo'); } export const AUTH_CALLBACK_PATH = '/auth/callback'; export const AUTH_CALLBACK_URL = `${window.location.origin}${AUTH_CALLBACK_PATH}`; -export function withSignIn () { +export function withSignIn() { return function ( // eslint-disable-next-line _target: any, @@ -40,7 +40,7 @@ export function withSignIn () { }; } -export function afterAuth () { +export function afterAuth() { const redirectTo = getRedirectTo(); clearRedirectTo(); diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index d1bef1b1..82c90b34 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -15,9 +15,33 @@ function LoginPage() { const action = search.get('action') || ''; const email = search.get('email') || ''; const force = search.get('force') === 'true'; - const redirectTo = search.get('redirectTo') || ''; + const rawRedirectTo = search.get('redirectTo') || ''; const isAuthenticated = useContext(AFConfigContext)?.isAuthenticated || false; + // Sanitize redirectTo - remove workspace-specific URLs from previous sessions + const redirectTo = useMemo(() => { + if (!rawRedirectTo) return ''; + + try { + const url = new URL(decodeURIComponent(rawRedirectTo)); + const pathname = url.pathname; + + // Check if URL contains workspace/view UUIDs (user-specific paths) + // Pattern matches /app/{uuid}/{uuid} or /app/{uuid} + const hasUserSpecificIds = /\/app\/[a-f0-9-]{36}/.test(pathname); + + if (hasUserSpecificIds) { + // Replace with clean /app path + return encodeURIComponent(`${url.origin}/app`); + } + + return rawRedirectTo; + } catch (e) { + // If URL parsing fails, return original + return rawRedirectTo; + } + }, [rawRedirectTo]); + useEffect(() => { if (action === LOGIN_ACTION.CHANGE_PASSWORD || force) { return;