From c67e645469f47c67f0a40baf88543f81ae0eb02e Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Mon, 1 Sep 2025 21:03:58 +0800 Subject: [PATCH] perf: login page (#5571) --- document/content/docs/upgrading/4-12/4123.mdx | 1 + document/data/doc-last-modified.json | 3 ++- packages/web/i18n/en/common.json | 2 +- packages/web/i18n/zh-CN/common.json | 2 +- packages/web/i18n/zh-Hant/common.json | 2 +- .../app/src/global/support/api/userRes.d.ts | 2 +- .../login/ForgetPasswordForm.tsx | 4 ++-- .../login/LoginForm/LoginForm.tsx | 4 ++-- .../login/LoginForm/WechatForm.tsx | 6 ++--- .../src/pageComponents/login/LoginModal.tsx | 4 ++-- .../src/pageComponents/login/RegisterForm.tsx | 4 ++-- .../app/src/pageComponents/login/index.tsx | 14 ++++-------- projects/app/src/pages/chat/index.tsx | 11 ++++++++-- projects/app/src/pages/login/fastlogin.tsx | 4 ++-- projects/app/src/pages/login/index.tsx | 20 ++++++++++++++--- projects/app/src/pages/login/provider.tsx | 22 ++++++++++--------- projects/app/src/web/support/user/api.ts | 17 +++++++------- 17 files changed, 71 insertions(+), 51 deletions(-) diff --git a/document/content/docs/upgrading/4-12/4123.mdx b/document/content/docs/upgrading/4-12/4123.mdx index 60f53c947..a246e75b8 100644 --- a/document/content/docs/upgrading/4-12/4123.mdx +++ b/document/content/docs/upgrading/4-12/4123.mdx @@ -13,5 +13,6 @@ description: 'FastGPT V4.12.3 更新说明' ## 🐛 修复 +1. 单团队模式下,如果用户离开,则无法重新进入团队。 ## 🔨 工具更新 diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index 989c6aa02..468c06dab 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -97,7 +97,7 @@ "document/content/docs/protocol/terms.en.mdx": "2025-08-03T22:37:45+08:00", "document/content/docs/protocol/terms.mdx": "2025-08-03T22:37:45+08:00", "document/content/docs/toc.en.mdx": "2025-08-04T13:42:36+08:00", - "document/content/docs/toc.mdx": "2025-08-20T21:58:13+08:00", + "document/content/docs/toc.mdx": "2025-08-29T01:24:19+08:00", "document/content/docs/upgrading/4-10/4100.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-10/4101.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00", @@ -105,6 +105,7 @@ "document/content/docs/upgrading/4-12/4120.mdx": "2025-08-12T22:45:19+08:00", "document/content/docs/upgrading/4-12/4121.mdx": "2025-08-15T22:53:06+08:00", "document/content/docs/upgrading/4-12/4122.mdx": "2025-08-27T00:31:33+08:00", + "document/content/docs/upgrading/4-12/4123.mdx": "2025-08-29T01:24:19+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index cc59a05e9..597800eee 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -893,7 +893,6 @@ "model.type.reRank": "ReRank", "model.type.stt": "STT", "model.type.tts": "TTS", - "month": "Month", "move.confirm": "Confirm move", "move_success": "Moved Successfully", @@ -915,6 +914,7 @@ "no_more_data": "No More Data", "no_pay_way": "There is no suitable payment channel in the system", "no_select_data": "No Data Available", + "not_active_team": "You have no available team", "not_model_config": "No related model configured", "not_open": "Not Open", "not_permission": "The current subscription package does not support team operation logs", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 377d303e7..fe725f742 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -893,7 +893,6 @@ "model.type.reRank": "重排模型", "model.type.stt": "语音识别", "model.type.tts": "语音合成", - "month": "月", "move.confirm": "确认移动", "move_success": "移动成功", @@ -915,6 +914,7 @@ "no_more_data": "没有更多了~", "no_pay_way": "系统无合适的支付渠道", "no_select_data": "没有可选值", + "not_active_team": "你没有可用的团队", "not_model_config": "未配置相关模型", "not_open": "未开启", "not_permission": "当前订阅套餐不支持团队操作日志", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index 5c90a8f6a..acfb87354 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -892,7 +892,6 @@ "model.type.reRank": "重排模型", "model.type.stt": "語音辨識", "model.type.tts": "語音合成", - "month": "月", "move.confirm": "確認移動", "move_success": "移動成功", @@ -914,6 +913,7 @@ "no_more_data": "沒有更多資料了", "no_pay_way": "系統無合適的支付渠道", "no_select_data": "沒有可選擇的資料", + "not_active_team": "你沒有可用的團隊", "not_model_config": "未設定相關模型", "not_open": "未開啟", "not_permission": "當前訂閱套餐不支持團隊操作日誌", diff --git a/projects/app/src/global/support/api/userRes.d.ts b/projects/app/src/global/support/api/userRes.d.ts index e10785638..d9c9062c5 100644 --- a/projects/app/src/global/support/api/userRes.d.ts +++ b/projects/app/src/global/support/api/userRes.d.ts @@ -1,6 +1,6 @@ import type { UserType } from '@fastgpt/global/support/user/type.d'; import type { PromotionRecordSchema } from '@fastgpt/global/support/activity/type.d'; -export interface ResLogin { +export interface LoginSuccessResponse { user: UserType; token: string; } diff --git a/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx b/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx index 6b53fb081..5aab7043b 100644 --- a/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx +++ b/projects/app/src/pageComponents/login/ForgetPasswordForm.tsx @@ -4,7 +4,7 @@ import { useForm } from 'react-hook-form'; import { LoginPageTypeEnum } from '@/web/support/user/login/constants'; import { postFindPassword } from '@/web/support/user/api'; import { useSendCode } from '@/web/support/user/hooks/useSendCode'; -import type { ResLogin } from '@/global/support/api/userRes.d'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes.d'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; @@ -13,7 +13,7 @@ import { checkPasswordRule } from '@fastgpt/global/common/string/password'; interface Props { setPageType: Dispatch<`${LoginPageTypeEnum}`>; - loginSuccess: (e: ResLogin) => void; + loginSuccess: (e: LoginSuccessResponse) => void; } interface RegisterType { diff --git a/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx b/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx index 6021694f1..3d6740865 100644 --- a/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx +++ b/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx @@ -3,7 +3,7 @@ import { FormControl, Flex, Input, Button, Box } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { LoginPageTypeEnum } from '@/web/support/user/login/constants'; import { postLogin, getPreLogin } from '@/web/support/user/api'; -import type { ResLogin } from '@/global/support/api/userRes'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; @@ -13,7 +13,7 @@ import PolicyTip from './PolicyTip'; interface Props { setPageType: Dispatch<`${LoginPageTypeEnum}`>; - loginSuccess: (e: ResLogin) => void; + loginSuccess: (e: LoginSuccessResponse) => void; } interface LoginFormType { diff --git a/projects/app/src/pageComponents/login/LoginForm/WechatForm.tsx b/projects/app/src/pageComponents/login/LoginForm/WechatForm.tsx index b4e23a22a..13b648d45 100644 --- a/projects/app/src/pageComponents/login/LoginForm/WechatForm.tsx +++ b/projects/app/src/pageComponents/login/LoginForm/WechatForm.tsx @@ -1,6 +1,6 @@ import React, { type Dispatch } from 'react'; import { LoginPageTypeEnum } from '@/web/support/user/login/constants'; -import type { ResLogin } from '@/global/support/api/userRes'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes'; import { Box, Center, Flex, Link } from '@chakra-ui/react'; import { useQuery } from '@tanstack/react-query'; import { getWXLoginQR, getWXLoginResult } from '@/web/support/user/api'; @@ -23,7 +23,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import PolicyTip from './PolicyTip'; interface Props { - loginSuccess: (e: ResLogin) => void; + loginSuccess: (e: LoginSuccessResponse) => void; setPageType: Dispatch<`${LoginPageTypeEnum}`>; } @@ -55,7 +55,7 @@ const WechatForm = ({ setPageType, loginSuccess }: Props) => { { refetchInterval: 3 * 1000, enabled: !!wechatInfo?.code, - onSuccess(data: ResLogin | undefined) { + onSuccess(data: LoginSuccessResponse | undefined) { if (data) { removeFastGPTSem(); loginSuccess(data); diff --git a/projects/app/src/pageComponents/login/LoginModal.tsx b/projects/app/src/pageComponents/login/LoginModal.tsx index 3d8565eb0..ec71e1fc1 100644 --- a/projects/app/src/pageComponents/login/LoginModal.tsx +++ b/projects/app/src/pageComponents/login/LoginModal.tsx @@ -4,10 +4,10 @@ import { LoginContainer } from '@/pageComponents/login'; import I18nLngSelector from '@/components/Select/I18nLngSelector'; import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; -import type { ResLogin } from '@/global/support/api/userRes'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes'; type LoginModalProps = { - onSuccess?: (res: ResLogin) => void; + onSuccess: (e: LoginSuccessResponse) => any; }; const LoginModal = ({ onSuccess }: LoginModalProps) => { diff --git a/projects/app/src/pageComponents/login/RegisterForm.tsx b/projects/app/src/pageComponents/login/RegisterForm.tsx index 7aa0c36d4..23ae159b5 100644 --- a/projects/app/src/pageComponents/login/RegisterForm.tsx +++ b/projects/app/src/pageComponents/login/RegisterForm.tsx @@ -4,7 +4,7 @@ import { useForm } from 'react-hook-form'; import { LoginPageTypeEnum } from '@/web/support/user/login/constants'; import { postRegister } from '@/web/support/user/api'; import { useSendCode } from '@/web/support/user/hooks/useSendCode'; -import type { ResLogin } from '@/global/support/api/userRes'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { postCreateApp } from '@/web/core/app/api'; import { emptyTemplates } from '@/web/core/app/templates'; @@ -23,7 +23,7 @@ import { import { checkPasswordRule } from '@fastgpt/global/common/string/password'; interface Props { - loginSuccess: (e: ResLogin) => void; + loginSuccess: (e: LoginSuccessResponse) => void; setPageType: Dispatch<`${LoginPageTypeEnum}`>; } diff --git a/projects/app/src/pageComponents/login/index.tsx b/projects/app/src/pageComponents/login/index.tsx index f7131c848..a69db60c5 100644 --- a/projects/app/src/pageComponents/login/index.tsx +++ b/projects/app/src/pageComponents/login/index.tsx @@ -10,7 +10,7 @@ import { } from '@chakra-ui/react'; import { LoginPageTypeEnum } from '@/web/support/user/login/constants'; import { useSystemStore } from '@/web/common/system/useSystemStore'; -import type { ResLogin } from '@/global/support/api/userRes.d'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes.d'; import { useUserStore } from '@/web/support/user/useUserStore'; import { useChatStore } from '@/web/core/chat/context/useChatStore'; import dynamic from 'next/dynamic'; @@ -21,8 +21,6 @@ import { useTranslation } from 'next-i18next'; import LoginForm from '@/pageComponents/login/LoginForm/LoginForm'; import { GET } from '@/web/common/api/request'; import { getDocPath } from '@/web/common/system/doc'; -import { postAcceptInvitationLink } from '@/web/support/user/team/api'; -import { useRouter } from 'next/router'; const RegisterForm = dynamic(() => import('@/pageComponents/login/RegisterForm')); const ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm')); @@ -187,25 +185,21 @@ export const LoginContainer = ({ onSuccess }: { children?: React.ReactNode; - onSuccess?: (res: ResLogin) => void; + onSuccess: (res: LoginSuccessResponse) => void; }) => { const { t } = useTranslation(); const { feConfigs } = useSystemStore(); - const { setUserInfo } = useUserStore(); const { setLastChatAppId } = useChatStore(); const [pageType, setPageType] = useState<`${LoginPageTypeEnum}` | null>(null); const [showCommunityModal, setShowCommunityModal] = useState(false); - const router = useRouter(); - const { lastRoute = '' } = router.query as { lastRoute: string }; // login success handler const loginSuccess = useCallback( - async (res: ResLogin) => { - setUserInfo(res.user); + (res: LoginSuccessResponse) => { onSuccess?.(res); }, - [setUserInfo, onSuccess] + [onSuccess] ); // initialization logic diff --git a/projects/app/src/pages/chat/index.tsx b/projects/app/src/pages/chat/index.tsx index da4ac0a25..d58a2cc8b 100644 --- a/projects/app/src/pages/chat/index.tsx +++ b/projects/app/src/pages/chat/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import NextHead from '@/components/common/NextHead'; import { Box, Flex } from '@chakra-ui/react'; import { useChatStore } from '@/web/core/chat/context/useChatStore'; @@ -27,6 +27,8 @@ import { } from '@/web/core/chat/context/chatSettingContext'; import ChatTeamApp from '@/pageComponents/chat/ChatTeamApp'; import ChatFavouriteApp from '@/pageComponents/chat/ChatFavouriteApp'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes'; const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { const { isPc } = useSystem(); @@ -89,6 +91,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => { const Render = (props: { appId: string; isStandalone?: string }) => { const { appId, isStandalone } = props; const { chatId } = useChatStore(); + const { setUserInfo } = useUserStore(); const { feConfigs } = useSystemStore(); const { isInitedUser, userInfo, myApps } = useChat(appId); @@ -105,6 +108,10 @@ const Render = (props: { appId: string; isStandalone?: string }) => { }; }, [appId, chatId]); + const loginSuccess = useCallback(async (res: LoginSuccessResponse) => { + setUserInfo(res.user); + }, []); + // Waiting for user info to be initialized if (!isInitedUser) { return ( @@ -120,7 +127,7 @@ const Render = (props: { appId: string; isStandalone?: string }) => { <> - + ); } diff --git a/projects/app/src/pages/login/fastlogin.tsx b/projects/app/src/pages/login/fastlogin.tsx index 220e5d1a2..f8f6045a6 100644 --- a/projects/app/src/pages/login/fastlogin.tsx +++ b/projects/app/src/pages/login/fastlogin.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect } from 'react'; import { useRouter } from 'next/router'; -import type { ResLogin } from '@/global/support/api/userRes.d'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes.d'; import { useUserStore } from '@/web/support/user/useUserStore'; import { clearToken } from '@/web/support/user/auth'; import { postFastLogin } from '@/web/support/user/api'; @@ -23,7 +23,7 @@ const FastLogin = ({ const { toast } = useToast(); const { t } = useTranslation(); const loginSuccess = useCallback( - (res: ResLogin) => { + (res: LoginSuccessResponse) => { setUserInfo(res.user); setTimeout(() => { diff --git a/projects/app/src/pages/login/index.tsx b/projects/app/src/pages/login/index.tsx index 9523f63c0..d092e46c9 100644 --- a/projects/app/src/pages/login/index.tsx +++ b/projects/app/src/pages/login/index.tsx @@ -5,14 +5,22 @@ import { clearToken } from '@/web/support/user/auth'; import { useMount } from 'ahooks'; import LoginModal from '@/pageComponents/login/LoginModal'; import { postAcceptInvitationLink } from '@/web/support/user/team/api'; -import type { ResLogin } from '@/global/support/api/userRes'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { useTranslation } from 'next-i18next'; +import { useUserStore } from '@/web/support/user/useUserStore'; const Login = () => { const router = useRouter(); const { lastRoute = '' } = router.query as { lastRoute: string }; + const { t } = useTranslation(); + const { toast } = useToast(); + const { setUserInfo } = useUserStore(); const loginSuccess = useCallback( - async (res: ResLogin) => { + async (res: LoginSuccessResponse) => { + setUserInfo(res.user); + const decodeLastRoute = decodeURIComponent(lastRoute); const navigateTo = await (async () => { if (res.user.team.status !== 'active') { @@ -20,8 +28,14 @@ const Login = () => { const id = decodeLastRoute.split('invitelinkid=')[1]; await postAcceptInvitationLink(id); return '/dashboard/apps'; + } else { + toast({ + status: 'warning', + title: t('common:not_active_team') + }); } } + return decodeLastRoute && !decodeLastRoute.includes('/login') && decodeLastRoute.startsWith('/') @@ -29,7 +43,7 @@ const Login = () => { : '/dashboard/apps'; })(); - router.replace(navigateTo); + navigateTo && router.replace(navigateTo); }, [lastRoute, router] ); diff --git a/projects/app/src/pages/login/provider.tsx b/projects/app/src/pages/login/provider.tsx index c1ae77e4a..6f8a2b9ad 100644 --- a/projects/app/src/pages/login/provider.tsx +++ b/projects/app/src/pages/login/provider.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect } from 'react'; import { useRouter } from 'next/router'; import { useSystemStore } from '@/web/common/system/useSystemStore'; -import type { ResLogin } from '@/global/support/api/userRes.d'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes.d'; import { useUserStore } from '@/web/support/user/useUserStore'; import { clearToken } from '@/web/support/user/auth'; import { oauthLogin } from '@/web/support/user/api'; @@ -32,35 +32,37 @@ const provider = () => { const { toast } = useToast(); const lastRoute = loginStore?.lastRoute - ? decodeURIComponent(loginStore?.lastRoute) + ? decodeURIComponent(loginStore.lastRoute) : '/dashboard/apps'; const errorRedirectPage = lastRoute.startsWith('/chat') ? lastRoute : '/login'; - // const loginSuccess = useCallback(async () => { - // const decodeLastRoute = decodeURIComponent(lastRoute); - - // router.push(navigateTo); - // }, [lastRoute, router]); - const loginSuccess = useCallback( - async (res: ResLogin) => { + async (res: LoginSuccessResponse) => { const decodeLastRoute = decodeURIComponent(lastRoute); setUserInfo(res.user); + const navigateTo = await (async () => { if (res.user.team.status !== 'active') { if (decodeLastRoute.includes('/account/team?invitelinkid=')) { const id = decodeLastRoute.split('invitelinkid=')[1]; await postAcceptInvitationLink(id); return '/dashboard/apps'; + } else { + toast({ + status: 'warning', + title: t('common:not_active_team') + }); } } + return decodeLastRoute && !decodeLastRoute.includes('/login') && decodeLastRoute.startsWith('/') ? lastRoute : '/dashboard/apps'; })(); - router.replace(navigateTo); + + navigateTo && router.replace(navigateTo); }, [setUserInfo, router, lastRoute] ); diff --git a/projects/app/src/web/support/user/api.ts b/projects/app/src/web/support/user/api.ts index 5574f2635..5b8af1df4 100644 --- a/projects/app/src/web/support/user/api.ts +++ b/projects/app/src/web/support/user/api.ts @@ -1,6 +1,6 @@ import { GET, POST, PUT } from '@/web/common/api/request'; import { hashStr } from '@fastgpt/global/common/string/tools'; -import type { ResLogin } from '@/global/support/api/userRes.d'; +import type { LoginSuccessResponse } from '@/global/support/api/userRes.d'; import type { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants'; import type { UserUpdateParams } from '@/types/user'; import type { UserType } from '@fastgpt/global/support/user/type.d'; @@ -27,10 +27,11 @@ export const sendAuthCode = (data: { export const getTokenLogin = () => GET('/support/user/account/tokenLogin', {}, { maxQuantity: 1 }); export const oauthLogin = (params: OauthLoginProps) => - POST('/proApi/support/user/account/login/oauth', params); + POST('/proApi/support/user/account/login/oauth', params); export const postFastLogin = (params: FastLoginProps) => - POST('/proApi/support/user/account/login/fastLogin', params); -export const ssoLogin = (params: any) => GET('/proApi/support/user/account/sso', params); + POST('/proApi/support/user/account/login/fastLogin', params); +export const ssoLogin = (params: any) => + GET('/proApi/support/user/account/sso', params); export const postRegister = ({ username, @@ -41,7 +42,7 @@ export const postRegister = ({ msclkid, fastgpt_sem }: AccountRegisterBody) => - POST(`/proApi/support/user/account/register/emailAndPhone`, { + POST(`/proApi/support/user/account/register/emailAndPhone`, { username, code, inviterId, @@ -60,7 +61,7 @@ export const postFindPassword = ({ code: string; password: string; }) => - POST(`/proApi/support/user/account/password/updateByCode`, { + POST(`/proApi/support/user/account/password/updateByCode`, { username, code, password: hashStr(password) @@ -88,7 +89,7 @@ export const updateContact = (data: { contact: string; verifyCode: string }) => }; export const postLogin = ({ password, ...props }: PostLoginProps) => - POST('/support/user/account/loginByPassword', { + POST('/support/user/account/loginByPassword', { ...props, password: hashStr(password) }); @@ -101,7 +102,7 @@ export const getWXLoginQR = () => GET('/proApi/support/user/account/login/wx/getQR'); export const getWXLoginResult = (params: WxLoginProps) => - POST(`/proApi/support/user/account/login/wx/getResult`, params); + POST(`/proApi/support/user/account/login/wx/getResult`, params); export const getCaptchaPic = (username: string) => GET<{