Commit e64a2b04 by light5980 Committed by GitHub

fix:split register methods from team sync mode (#7230)

parent c8abd09d
...@@ -23,6 +23,10 @@ export type ExternalProviderWorkflowVarType = { ...@@ -23,6 +23,10 @@ export type ExternalProviderWorkflowVarType = {
url?: string; url?: string;
}; };
export type FastGPTRegisterMethodType = 'email' | 'phone';
export type FastGPTRegisterMethodCompatType = FastGPTRegisterMethodType | 'sync';
export type FastGPTTeamModeType = 'multi' | 'single' | 'sync';
/* fastgpt main */ /* fastgpt main */
export type FastGPTConfigFileType = { export type FastGPTConfigFileType = {
feConfigs: FastGPTFeConfigsType; feConfigs: FastGPTFeConfigsType;
...@@ -42,10 +46,14 @@ export type FastGPTFeConfigsType = { ...@@ -42,10 +46,14 @@ export type FastGPTFeConfigsType = {
show_emptyChat?: boolean; show_emptyChat?: boolean;
isPlus?: boolean; isPlus?: boolean;
hideChatCopyrightSetting?: boolean; hideChatCopyrightSetting?: boolean;
register_method?: ['email' | 'phone' | 'sync']; /**
login_method?: ['email' | 'phone']; // Attention: login method is different with oauth * 用户自助注册方式。兼容期允许读取旧配置中的 sync,但新配置不再写入 sync。
find_password_method?: ['email' | 'phone']; */
bind_notification_method?: ['email' | 'phone']; 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; googleClientVerKey?: string;
/** /**
* @deprecated MCP SSE 代理地址已迁移到环境变量 SSE_MCP_SERVER_PROXY_ENDPOINT。 * @deprecated MCP SSE 代理地址已迁移到环境变量 SSE_MCP_SERVER_PROXY_ENDPOINT。
......
Subproject commit 5f90c283f1e89ccfb40d17707f9f7603e0a1928e Subproject commit 11ed3ecf86e55b38e9d829440f9492ec2479dd72
...@@ -39,7 +39,7 @@ import { format } from 'date-fns/format'; ...@@ -39,7 +39,7 @@ import { format } from 'date-fns/format';
import OrgTags from '@/components/support/user/team/OrgTags'; import OrgTags from '@/components/support/user/team/OrgTags';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { useCallback, useState, useMemo } from 'react'; 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 { type TeamMemberItemType } from '@fastgpt/global/support/user/team/type';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
...@@ -59,7 +59,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -59,7 +59,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
const { toast } = useToast(); const { toast } = useToast();
const { userInfo, initUserInfo } = useUserStore(); const { userInfo, initUserInfo } = useUserStore();
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const isSyncMode = feConfigs?.register_method?.includes('sync'); const isSyncMode = getIsMemberSyncMode(feConfigs);
const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v); const { myTeams, onSwitchTeam } = useContextSelector(TeamContext, (v) => v);
......
...@@ -36,6 +36,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; ...@@ -36,6 +36,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { delRemoveMember } from '@/web/support/user/team/api'; import { delRemoveMember } from '@/web/support/user/team/api';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import useOrg from '@/web/support/user/team/org/hooks/useOrg'; import useOrg from '@/web/support/user/team/org/hooks/useOrg';
import { getIsMemberSyncMode } from '@/web/common/system/utils';
const OrgInfoModal = dynamic(() => import('./OrgInfoModal')); const OrgInfoModal = dynamic(() => import('./OrgInfoModal'));
const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal')); const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal'));
...@@ -74,7 +75,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) { ...@@ -74,7 +75,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { userInfo, isTeamAdmin } = useUserStore(); const { userInfo, isTeamAdmin } = useUserStore();
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const isSyncMember = feConfigs.register_method?.includes('sync'); const isSyncMember = getIsMemberSyncMode(feConfigs);
const [editOrg, setEditOrg] = useState<OrgFormType>(); const [editOrg, setEditOrg] = useState<OrgFormType>();
const [manageMemberOrg, setManageMemberOrg] = useState<OrgListItemType>(); const [manageMemberOrg, setManageMemberOrg] = useState<OrgListItemType>();
const [movingOrg, setMovingOrg] = useState<OrgListItemType>(); const [movingOrg, setMovingOrg] = useState<OrgListItemType>();
......
...@@ -14,6 +14,7 @@ import { useMount } from 'ahooks'; ...@@ -14,6 +14,7 @@ import { useMount } from 'ahooks';
import type { LangEnum } from '@fastgpt/global/common/i18n/type'; import type { LangEnum } from '@fastgpt/global/common/i18n/type';
import type { LoginSuccessResponseType } from '@fastgpt/global/openapi/support/user/account/login/api'; import type { LoginSuccessResponseType } from '@fastgpt/global/openapi/support/user/account/login/api';
import PolicyTip from './PolicyTip'; import PolicyTip from './PolicyTip';
import { getRegisterMethods } from '@/web/common/system/utils';
type LoginSuccessHandler = (res: LoginSuccessResponseType) => void | Promise<void>; type LoginSuccessHandler = (res: LoginSuccessResponseType) => void | Promise<void>;
...@@ -32,6 +33,9 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -32,6 +33,9 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const query = useSearchParams(); const query = useSearchParams();
const router = useRouter(); const router = useRouter();
const registerMethods = getRegisterMethods(feConfigs);
const hasRegisterMethod = registerMethods.length > 0;
const hasFindPasswordMethod = !!feConfigs?.find_password_method?.length;
const { const {
register, register,
...@@ -74,7 +78,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -74,7 +78,7 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
} }
); );
const isCommunityVersion = !!(feConfigs?.register_method && !feConfigs?.isPlus); const isCommunityVersion = hasRegisterMethod && !feConfigs?.isPlus;
const placeholder = (() => { const placeholder = (() => {
if (isCommunityVersion) { if (isCommunityVersion) {
...@@ -158,29 +162,31 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -158,29 +162,31 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
{t('login:Login')} {t('login:Login')}
</Button> </Button>
<Flex {(hasFindPasswordMethod || hasRegisterMethod) && (
mt={6} <Flex
align={'center'} mt={6}
justifyContent={'center'} align={'center'}
gap={0} justifyContent={'center'}
color={'primary.700'} gap={0}
fontWeight={'medium'} color={'primary.700'}
h={'16px'} fontWeight={'medium'}
lineHeight={'16px'} h={'16px'}
> lineHeight={'16px'}
{feConfigs?.find_password_method && feConfigs.find_password_method.length > 0 && ( >
<Box {hasFindPasswordMethod && (
cursor={'pointer'} <Box
_hover={{ textDecoration: 'underline' }} cursor={'pointer'}
onClick={() => setPageType('forgetPassword')} _hover={{ textDecoration: 'underline' }}
fontSize="mini" onClick={() => setPageType('forgetPassword')}
> fontSize="mini"
{t('login:forget_password')} >
</Box> {t('login:forget_password')}
)} </Box>
{feConfigs?.register_method && feConfigs.register_method.length > 0 && ( )}
<> {hasFindPasswordMethod && hasRegisterMethod && (
<Box display={['block', 'block']} mx={3} h={'12px'} w={'1px'} bg={'myGray.250'}></Box> <Box display={['block', 'block']} mx={3} h={'12px'} w={'1px'} bg={'myGray.250'}></Box>
)}
{hasRegisterMethod && (
<Box <Box
cursor={'pointer'} cursor={'pointer'}
_hover={{ textDecoration: 'underline' }} _hover={{ textDecoration: 'underline' }}
...@@ -190,9 +196,9 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -190,9 +196,9 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
> >
{t('login:register')} {t('login:register')}
</Box> </Box>
</> )}
)} </Flex>
</Flex> )}
</Box> </Box>
</FormLayout> </FormLayout>
); );
......
...@@ -18,6 +18,7 @@ import { ...@@ -18,6 +18,7 @@ import {
import { checkPasswordRule } from '@fastgpt/global/common/string/password'; import { checkPasswordRule } from '@fastgpt/global/common/string/password';
import type { LoginSuccessResponseType } from '@fastgpt/global/openapi/support/user/account/login/api'; import type { LoginSuccessResponseType } from '@fastgpt/global/openapi/support/user/account/login/api';
import type { LangEnum } from '@fastgpt/global/common/i18n/type'; import type { LangEnum } from '@fastgpt/global/common/i18n/type';
import { getRegisterMethods } from '@/web/common/system/utils';
type LoginSuccessHandler = (res: LoginSuccessResponseType) => void | Promise<void>; type LoginSuccessHandler = (res: LoginSuccessResponseType) => void | Promise<void>;
...@@ -88,8 +89,8 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => { ...@@ -88,8 +89,8 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
} }
}; };
const placeholder = feConfigs?.register_method const placeholder = getRegisterMethods(feConfigs)
?.map((item) => { .map((item) => {
switch (item) { switch (item) {
case 'email': case 'email':
return t('common:support.user.login.Email'); return t('common:support.user.login.Email');
......
...@@ -47,6 +47,7 @@ import { useUploadAvatar } from '@fastgpt/web/common/file/hooks/useUploadAvatar' ...@@ -47,6 +47,7 @@ import { useUploadAvatar } from '@fastgpt/web/common/file/hooks/useUploadAvatar'
import { getUploadAvatarPresignedUrl } from '@/web/common/file/api'; import { getUploadAvatarPresignedUrl } from '@/web/common/file/api';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { i18nT } from '@fastgpt/global/common/i18n/utils'; import { i18nT } from '@fastgpt/global/common/i18n/utils';
import { getIsMemberSyncMode } from '@/web/common/system/utils';
const RedeemCouponModal = dynamic(() => import('@/pageComponents/account/info/RedeemCouponModal'), { const RedeemCouponModal = dynamic(() => import('@/pageComponents/account/info/RedeemCouponModal'), {
ssr: false ssr: false
...@@ -251,7 +252,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => { ...@@ -251,7 +252,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
minW: '52px' minW: '52px'
} as const; } as const;
const isSyncMember = feConfigs.register_method?.includes('sync'); const isSyncMember = getIsMemberSyncMode(feConfigs);
return ( return (
<Box> <Box>
{/* user info */} {/* user info */}
......
...@@ -2,9 +2,33 @@ import { ...@@ -2,9 +2,33 @@ import {
type EmbeddingModelItemType, type EmbeddingModelItemType,
type LLMModelItemType type LLMModelItemType
} from '@fastgpt/global/core/ai/model.schema'; } from '@fastgpt/global/core/ai/model.schema';
import type {
FastGPTFeConfigsType,
FastGPTRegisterMethodType
} from '@fastgpt/global/common/system/types';
import { useSystemStore } from './useSystemStore'; import { useSystemStore } from './useSystemStore';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; 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 ({ export const downloadFetch = async ({
url, url,
filename, filename,
......
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>): 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
);
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment