From 2e5719a25873299e1bd98177be8a98237b29778c Mon Sep 17 00:00:00 2001 From: shortlight5980 Date: Wed, 1 Jul 2026 16:06:39 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9Asplit=20register=20methods=20from?= =?UTF-8?q?=20team=20sync=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/global/common/system/types/index.ts | 16 +++-- pro | 2 +- .../account/team/MemberTable.tsx | 4 +- .../account/team/OrgManage/index.tsx | 3 +- .../login/LoginForm/LoginForm.tsx | 58 ++++++++++--------- .../src/pageComponents/login/RegisterForm.tsx | 5 +- projects/app/src/pages/account/info/index.tsx | 3 +- projects/app/src/web/common/system/utils.ts | 24 ++++++++ .../app/test/web/common/system/utils.test.ts | 38 ++++++++++++ 9 files changed, 116 insertions(+), 37 deletions(-) create mode 100644 projects/app/test/web/common/system/utils.test.ts diff --git a/packages/global/common/system/types/index.ts b/packages/global/common/system/types/index.ts index 952143b76131..73f8414a386d 100644 --- a/packages/global/common/system/types/index.ts +++ b/packages/global/common/system/types/index.ts @@ -23,6 +23,10 @@ export type ExternalProviderWorkflowVarType = { url?: string; }; +export type FastGPTRegisterMethodType = 'email' | 'phone'; +export type FastGPTRegisterMethodCompatType = FastGPTRegisterMethodType | 'sync'; +export type FastGPTTeamModeType = 'multi' | 'single' | 'sync'; + /* fastgpt main */ export type FastGPTConfigFileType = { feConfigs: FastGPTFeConfigsType; @@ -42,10 +46,14 @@ export type FastGPTFeConfigsType = { show_emptyChat?: boolean; isPlus?: boolean; hideChatCopyrightSetting?: boolean; - register_method?: ['email' | 'phone' | 'sync']; - login_method?: ['email' | 'phone']; // Attention: login method is different with oauth - find_password_method?: ['email' | 'phone']; - bind_notification_method?: ['email' | 'phone']; + /** + * 用户自助注册方式。兼容期允许读取旧配置中的 sync,但新配置不再写入 sync。 + */ + register_method?: FastGPTRegisterMethodCompatType[]; + teamMode?: FastGPTTeamModeType; + login_method?: FastGPTRegisterMethodType[]; // Attention: login method is different with oauth + find_password_method?: FastGPTRegisterMethodType[]; + bind_notification_method?: FastGPTRegisterMethodType[]; googleClientVerKey?: string; /** * @deprecated MCP SSE 代理地址已迁移到环境变量 SSE_MCP_SERVER_PROXY_ENDPOINT。 diff --git a/pro b/pro index 2b3e11934332..5d37df6f0311 160000 --- a/pro +++ b/pro @@ -1 +1 @@ -Subproject commit 2b3e11934332dcefae9ee6847f65a2e15d32095a +Subproject commit 5d37df6f0311e257efc124683ed81f275bbfb103 diff --git a/projects/app/src/pageComponents/account/team/MemberTable.tsx b/projects/app/src/pageComponents/account/team/MemberTable.tsx index 365be85bdf1d..a5ef5bfec8bc 100644 --- a/projects/app/src/pageComponents/account/team/MemberTable.tsx +++ b/projects/app/src/pageComponents/account/team/MemberTable.tsx @@ -39,7 +39,7 @@ import { format } from 'date-fns/format'; import OrgTags from '@/components/support/user/team/OrgTags'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import { useCallback, useState, useMemo } from 'react'; -import { downloadFetch } from '@/web/common/system/utils'; +import { downloadFetch, getIsMemberSyncMode } from '@/web/common/system/utils'; import { type TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; import { useToast } from '@fastgpt/web/hooks/useToast'; import MyBox from '@fastgpt/web/components/common/MyBox'; @@ -59,7 +59,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { const { toast } = useToast(); const { userInfo, initUserInfo } = useUserStore(); const { feConfigs } = useSystemStore(); - const isSyncMode = feConfigs?.register_method?.includes('sync'); + const isSyncMode = getIsMemberSyncMode(feConfigs); const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v); diff --git a/projects/app/src/pageComponents/account/team/OrgManage/index.tsx b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx index 86c43a8cd9bb..54241bffa236 100644 --- a/projects/app/src/pageComponents/account/team/OrgManage/index.tsx +++ b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx @@ -36,6 +36,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; import { delRemoveMember } from '@/web/support/user/team/api'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import useOrg from '@/web/support/user/team/org/hooks/useOrg'; +import { getIsMemberSyncMode } from '@/web/common/system/utils'; const OrgInfoModal = dynamic(() => import('./OrgInfoModal')); const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal')); @@ -74,7 +75,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) { const { t } = useTranslation(); const { userInfo, isTeamAdmin } = useUserStore(); const { feConfigs } = useSystemStore(); - const isSyncMember = feConfigs.register_method?.includes('sync'); + const isSyncMember = getIsMemberSyncMode(feConfigs); const [editOrg, setEditOrg] = useState(); const [manageMemberOrg, setManageMemberOrg] = useState(); const [movingOrg, setMovingOrg] = useState(); diff --git a/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx b/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx index e8a7a07453ed..34e5b7545391 100644 --- a/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx +++ b/projects/app/src/pageComponents/login/LoginForm/LoginForm.tsx @@ -14,6 +14,7 @@ import { useMount } from 'ahooks'; import type { LangEnum } from '@fastgpt/global/common/i18n/type'; import type { LoginSuccessResponseType } from '@fastgpt/global/openapi/support/user/account/login/api'; import PolicyTip from './PolicyTip'; +import { getRegisterMethods } from '@/web/common/system/utils'; type LoginSuccessHandler = (res: LoginSuccessResponseType) => void | Promise; @@ -32,6 +33,9 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { const { feConfigs } = useSystemStore(); const query = useSearchParams(); const router = useRouter(); + const registerMethods = getRegisterMethods(feConfigs); + const hasRegisterMethod = registerMethods.length > 0; + const hasFindPasswordMethod = !!feConfigs?.find_password_method?.length; const { register, @@ -74,7 +78,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { } ); - const isCommunityVersion = !!(feConfigs?.register_method && !feConfigs?.isPlus); + const isCommunityVersion = hasRegisterMethod && !feConfigs?.isPlus; const placeholder = (() => { if (isCommunityVersion) { @@ -158,29 +162,31 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { {t('login:Login')} - - {feConfigs?.find_password_method && feConfigs.find_password_method.length > 0 && ( - setPageType('forgetPassword')} - fontSize="mini" - > - {t('login:forget_password')} - - )} - {feConfigs?.register_method && feConfigs.register_method.length > 0 && ( - <> + {(hasFindPasswordMethod || hasRegisterMethod) && ( + + {hasFindPasswordMethod && ( + setPageType('forgetPassword')} + fontSize="mini" + > + {t('login:forget_password')} + + )} + {hasFindPasswordMethod && hasRegisterMethod && ( + )} + {hasRegisterMethod && ( { > {t('login:register')} - - )} - + )} + + )} ); diff --git a/projects/app/src/pageComponents/login/RegisterForm.tsx b/projects/app/src/pageComponents/login/RegisterForm.tsx index 085ec08abd79..8b5672212981 100644 --- a/projects/app/src/pageComponents/login/RegisterForm.tsx +++ b/projects/app/src/pageComponents/login/RegisterForm.tsx @@ -18,6 +18,7 @@ import { import { checkPasswordRule } from '@fastgpt/global/common/string/password'; import type { LoginSuccessResponseType } from '@fastgpt/global/openapi/support/user/account/login/api'; import type { LangEnum } from '@fastgpt/global/common/i18n/type'; +import { getRegisterMethods } from '@/web/common/system/utils'; type LoginSuccessHandler = (res: LoginSuccessResponseType) => void | Promise; @@ -88,8 +89,8 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { } }; - const placeholder = feConfigs?.register_method - ?.map((item) => { + const placeholder = getRegisterMethods(feConfigs) + .map((item) => { switch (item) { case 'email': return t('common:support.user.login.Email'); diff --git a/projects/app/src/pages/account/info/index.tsx b/projects/app/src/pages/account/info/index.tsx index 6c9f8459f5d8..52d144bd2788 100644 --- a/projects/app/src/pages/account/info/index.tsx +++ b/projects/app/src/pages/account/info/index.tsx @@ -47,6 +47,7 @@ import { useUploadAvatar } from '@fastgpt/web/common/file/hooks/useUploadAvatar' import { getUploadAvatarPresignedUrl } from '@/web/common/file/api'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; import { i18nT } from '@fastgpt/global/common/i18n/utils'; +import { getIsMemberSyncMode } from '@/web/common/system/utils'; const RedeemCouponModal = dynamic(() => import('@/pageComponents/account/info/RedeemCouponModal'), { ssr: false @@ -196,7 +197,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => { letterSpacing: '0.15px' }; - const isSyncMember = feConfigs.register_method?.includes('sync'); + const isSyncMember = getIsMemberSyncMode(feConfigs); return ( {/* user info */} diff --git a/projects/app/src/web/common/system/utils.ts b/projects/app/src/web/common/system/utils.ts index f28b00494c73..9a685a5c9f5d 100644 --- a/projects/app/src/web/common/system/utils.ts +++ b/projects/app/src/web/common/system/utils.ts @@ -2,9 +2,33 @@ import { type EmbeddingModelItemType, type LLMModelItemType } from '@fastgpt/global/core/ai/model.schema'; +import type { + FastGPTFeConfigsType, + FastGPTRegisterMethodType +} from '@fastgpt/global/common/system/types'; import { useSystemStore } from './useSystemStore'; import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; +/** + * 获取真实支持的自助注册方式,兼容过滤旧配置中被混入的 sync 团队模式。 + */ +export const getRegisterMethods = (feConfigs?: FastGPTFeConfigsType): FastGPTRegisterMethodType[] => + feConfigs?.register_method?.filter( + (method): method is FastGPTRegisterMethodType => method === 'email' || method === 'phone' + ) ?? []; + +/** + * 判断是否为成员同步模式。teamMode 是当前权威字段;旧 register_method: ['sync'] + * 仅用于兼容缺少 teamMode 的历史配置,避免新旧字段冲突时前后端模式不一致。 + */ +export const getIsMemberSyncMode = (feConfigs?: FastGPTFeConfigsType) => { + if (feConfigs?.teamMode) { + return feConfigs.teamMode === 'sync'; + } + + return !!feConfigs?.register_method?.includes('sync'); +}; + export const downloadFetch = async ({ url, filename, diff --git a/projects/app/test/web/common/system/utils.test.ts b/projects/app/test/web/common/system/utils.test.ts new file mode 100644 index 000000000000..a503adc8debb --- /dev/null +++ b/projects/app/test/web/common/system/utils.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from 'vitest'; + +import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types'; +import { getIsMemberSyncMode, getRegisterMethods } from '@/web/common/system/utils'; + +const createFeConfigs = (overrides: Partial): FastGPTFeConfigsType => ({ + uploadFileMaxAmount: 10, + uploadFileMaxSize: 100, + ...overrides +}); + +describe('system fe config utils', () => { + it('filters legacy sync from register methods', () => { + expect(getRegisterMethods(createFeConfigs({ register_method: ['sync'] }))).toEqual([]); + expect( + getRegisterMethods(createFeConfigs({ register_method: ['email', 'sync', 'phone'] })) + ).toEqual(['email', 'phone']); + }); + + it('detects member sync mode from new teamMode and legacy register_method', () => { + expect(getIsMemberSyncMode(createFeConfigs({ teamMode: 'sync', register_method: [] }))).toBe( + true + ); + expect(getIsMemberSyncMode(createFeConfigs({ register_method: ['sync'] }))).toBe(true); + expect( + getIsMemberSyncMode(createFeConfigs({ teamMode: 'single', register_method: ['sync'] })) + ).toBe(false); + expect( + getIsMemberSyncMode(createFeConfigs({ teamMode: 'multi', register_method: ['sync'] })) + ).toBe(false); + expect( + getIsMemberSyncMode(createFeConfigs({ teamMode: 'multi', register_method: ['phone'] })) + ).toBe(false); + expect(getIsMemberSyncMode(createFeConfigs({ teamMode: 'single', register_method: [] }))).toBe( + false + ); + }); +});