perf: full text collection and search code;perf: rename function (#3519)

* perf: full text collection and search code

* perf: rename function

* perf: notify modal

* remove invalid code

* perf: sso login

* perf: pay process
This commit is contained in:
Archer
2025-01-03 14:33:13 +08:00
committed by archer
parent 20c6c202d2
commit c0d0c629c5
30 changed files with 423 additions and 270 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { Box, Button, Flex, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react';
@@ -10,6 +10,7 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useUserStore } from '@/web/support/user/useUserStore';
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { useMount } from 'ahooks';
const NotSufficientModal = ({ type }: { type: NotSufficientModalType }) => {
const { t } = useTranslation();
@@ -73,11 +74,9 @@ const RechargeModal = ({
const { t } = useTranslation();
const { teamPlanStatus, initTeamPlanStatus } = useUserStore();
useEffect(() => {
(async () => {
await initTeamPlanStatus();
})();
}, [initTeamPlanStatus]);
useMount(() => {
initTeamPlanStatus();
});
const planName = useMemo(() => {
if (!teamPlanStatus?.standard?.currentSubLevel) return '';
@@ -94,8 +93,8 @@ const RechargeModal = ({
title={t('common:user.Pay')}
onClose={onClose}
isCentered
minW={['100%', '1200px']}
minH={['100%', '800px']}
minW={'90%'}
maxH={'90%'}
>
<ModalBody px={'52px'}>
<Flex alignItems={'center'} mb={6}>

View File

@@ -1,5 +1,5 @@
import MyModal from '@fastgpt/web/components/common/MyModal';
import React, { useEffect, useRef } from 'react';
import React, { useCallback, useEffect, useRef } from 'react';
import { useTranslation } from 'next-i18next';
import { Box, ModalBody } from '@chakra-ui/react';
import { checkBalancePayResult } from '@/web/support/wallet/bill/api';
@@ -25,25 +25,25 @@ const QRCodePayModal = ({
billId,
onSuccess
}: QRPayProps & { tip?: string; onSuccess?: () => any }) => {
const router = useRouter();
const { t } = useTranslation();
const { toast } = useToast();
const dom = useRef<HTMLDivElement>(null);
const drawCode = useCallback(() => {
if (dom.current && window.QRCode && !dom.current.innerHTML) {
new window.QRCode(dom.current, {
text: codeUrl,
width: qrCodeSize,
height: qrCodeSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: window.QRCode.CorrectLevel.H
});
}
}, [codeUrl]);
useEffect(() => {
let timer: NodeJS.Timeout;
const drawCode = () => {
if (dom.current && window.QRCode && !dom.current.innerHTML) {
new window.QRCode(dom.current, {
text: codeUrl,
width: qrCodeSize,
height: qrCodeSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: window.QRCode.CorrectLevel.H
});
}
};
const check = async () => {
try {
const res = await checkBalancePayResult(billId);
@@ -54,9 +54,6 @@ const QRCodePayModal = ({
title: res,
status: 'success'
});
setTimeout(() => {
router.reload();
}, 1000);
return;
} catch (error) {
toast({
@@ -75,11 +72,15 @@ const QRCodePayModal = ({
check();
return () => clearTimeout(timer);
}, [billId, onSuccess, toast]);
}, [billId, drawCode, onSuccess, toast]);
return (
<>
<Script src={getWebReqUrl('/js/qrcode.min.js')} strategy="lazyOnload"></Script>
<Script
src={getWebReqUrl('/js/qrcode.min.js')}
strategy="lazyOnload"
onLoad={drawCode}
></Script>
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
<ModalBody textAlign={'center'} pb={10} whiteSpace={'pre-wrap'}>

View File

@@ -75,7 +75,7 @@ const TeamSelector = ({
key={'manage'}
alignItems={'center'}
borderRadius={'md'}
cursor={'default'}
cursor={'pointer'}
gap={3}
onClick={() => router.push('/account/team')}
>

View File

@@ -4,7 +4,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import { useToggle } from 'ahooks';
import { useMemo } from 'react';
import IconButton from './IconButton';
import { getChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
function OrgTreeNode({
org,
@@ -20,7 +20,7 @@ function OrgTreeNode({
index?: number;
}) {
const children = useMemo(
() => list.filter((item) => item.path === getChildrenPath(org)),
() => list.filter((item) => item.path === getOrgChildrenPath(org)),
[org, list]
);
const [isExpanded, toggleIsExpanded] = useToggle(index === 0);

View File

@@ -36,7 +36,7 @@ import dynamic from 'next/dynamic';
import MyBox from '@fastgpt/web/components/common/MyBox';
import Path from '@/components/common/folder/Path';
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
import { getChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
const OrgInfoModal = dynamic(() => import('./OrgInfoModal'));
const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal'));
@@ -88,17 +88,20 @@ function OrgTable() {
});
const currentOrgs = useMemo(() => {
if (orgs.length === 0) return [];
// Auto select the first org(root org is team)
if (parentPath === '') {
setParentPath(`/${orgs[0].pathId}`);
setParentPath(getOrgChildrenPath(orgs[0]));
return [];
}
return orgs
.filter((org) => org.path === parentPath)
.map((item) => {
return {
...item,
// Member + org
count:
item.members.length + orgs.filter((org) => org.path === getChildrenPath(item)).length
item.members.length + orgs.filter((org) => org.path === getOrgChildrenPath(item)).length
};
});
}, [orgs, parentPath]);
@@ -109,6 +112,7 @@ function OrgTable() {
return orgs.find((org) => org.pathId === currentOrgId);
}, [orgs, parentPath]);
const paths = useMemo(() => {
const splitPath = parentPath.split('/').filter(Boolean);
return splitPath
@@ -118,7 +122,7 @@ function OrgTable() {
if (org.path === '') return;
return {
parentId: getChildrenPath(org),
parentId: getOrgChildrenPath(org),
parentName: org.name
};
})
@@ -175,7 +179,10 @@ function OrgTable() {
{currentOrgs.map((org) => (
<Tr key={org._id} overflow={'unset'}>
<Td>
<HStack cursor={'pointer'} onClick={() => setParentPath(getChildrenPath(org))}>
<HStack
cursor={'pointer'}
onClick={() => setParentPath(getOrgChildrenPath(org))}
>
<MemberTag name={org.name} avatar={org.avatar} />
<Tag size="sm">{org.count}</Tag>
<MyIcon
@@ -286,13 +293,13 @@ function OrgTable() {
});
}}
/>
<ActionButton
icon="common/administrator"
text={t('account_team:manage_member')}
onClick={() => setManageMemberOrg(currentOrg)}
/>
{currentOrg?.path !== '' && (
<>
<ActionButton
icon="common/administrator"
text={t('account_team:manage_member')}
onClick={() => setManageMemberOrg(currentOrg)}
/>
<ActionButton
icon="common/file/move"
text={t('account_team:move_org')}

View File

@@ -0,0 +1,14 @@
import { NextAPI } from '@/service/middleware/entry';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
import { NextApiRequest, NextApiResponse } from 'next';
async function handler(req: NextApiRequest, res: NextApiResponse) {
await authCert({ req, authRoot: true });
return { success: true };
}
export default NextAPI(handler);

View File

@@ -355,6 +355,9 @@ const TestHistories = React.memo(function TestHistories({
boxShadow: '1',
'& .delete': {
display: 'block'
},
'& .time': {
display: 'none'
}
}}
cursor={'pointer'}
@@ -381,16 +384,14 @@ const TestHistories = React.memo(function TestHistories({
<Box flex={1} mr={2} wordBreak={'break-all'} fontWeight={'400'}>
{item.text}
</Box>
<Box flex={'0 0 70px'}>
<Box className="time" flex={'0 0 auto'} fontSize={'xs'} color={'myGray.500'}>
{t(formatTimeToChatTime(item.time) as any).replace('#', ':')}
</Box>
<MyTooltip label={t('common:core.dataset.test.delete test history')}>
<Box w={'14px'} h={'14px'}>
<Box className="delete" display={'none'} w={'0.8rem'} h={'0.8rem'} ml={1}>
<MyIcon
className="delete"
name={'delete'}
w={'14px'}
display={'none'}
w={'0.8rem'}
_hover={{ color: 'red.600' }}
onClick={(e) => {
e.stopPropagation();

View File

@@ -3,17 +3,16 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { AbsoluteCenter, Box, Button, Flex } from '@chakra-ui/react';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import { OAuthEnum } from '@fastgpt/global/support/user/constant';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { customAlphabet } from 'nanoid';
import { useRouter } from 'next/router';
import { Dispatch, useCallback, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'next-i18next';
import I18nLngSelector from '@/components/Select/I18nLngSelector';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
import { isWecomTerminal } from '@fastgpt/service/support/user/wecom';
import { PUT } from '@fastgpt/service/common/api/plusRequest';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8);
import { checkIsWecomTerminal } from '@fastgpt/global/support/user/login/constants';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import Avatar from '@fastgpt/web/components/common/Avatar';
import dynamic from 'next/dynamic';
interface Props {
children: React.ReactNode;
@@ -21,18 +20,38 @@ interface Props {
pageType: `${LoginPageTypeEnum}`;
}
type OAuthItem = {
label: string;
provider: OAuthEnum | LoginPageTypeEnum;
icon: any;
pageType?: LoginPageTypeEnum;
redirectUrl?: string;
};
const FormLayout = ({ children, setPageType, pageType }: Props) => {
const { t } = useTranslation();
const router = useRouter();
const { setLoginStore, feConfigs } = useSystemStore();
const { lastRoute = '/app/list' } = router.query as { lastRoute: string };
const state = useRef(nanoid());
const redirectUri = `${location.origin}/login/provider`;
const { isPc } = useSystem();
const isWecom = isWecomTerminal();
const oAuthList = [
const { lastRoute = '/app/list' } = router.query as { lastRoute: string };
const state = useRef(getNanoid(8));
const redirectUri = `${location.origin}/login/provider`;
const isWecomWorkTerminal = checkIsWecomTerminal();
const oAuthList: OAuthItem[] = [
...(feConfigs?.sso?.url
? [
{
label: feConfigs.sso.title || 'Unknown',
provider: OAuthEnum.sso,
icon: feConfigs.sso.icon,
redirectUrl: `${feConfigs.sso.url}/login/oauth/authorize?redirect_uri=${encodeURIComponent(redirectUri)}&state=${state.current}`
}
]
: []),
...(feConfigs?.oauth?.wechat && pageType !== LoginPageTypeEnum.wechat
? [
{
@@ -90,7 +109,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
label: t('login:wecom'),
provider: OAuthEnum.wecom,
icon: 'common/wecom',
redirectUrl: isWecom
redirectUrl: isWecomWorkTerminal
? `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${feConfigs?.oauth?.wecom?.corpid}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&agentid=${feConfigs?.oauth?.wecom?.agentid}&state=${state.current}#wechat_redirect`
: `https://login.work.weixin.qq.com/wwlogin/sso/login?login_type=CorpApp&appid=${feConfigs?.oauth?.wecom?.corpid}&agentid=${feConfigs?.oauth?.wecom?.agentid}&redirect_uri=${redirectUri}&state=${state.current}`
}
@@ -114,39 +133,31 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
);
const onClickOauth = useCallback(
async (item: any) => {
item.redirectUrl &&
async (item: OAuthItem) => {
if (item.redirectUrl) {
setLoginStore({
provider: item.provider,
provider: item.provider as OAuthEnum,
lastRoute,
state: state.current
});
item.redirectUrl && router.replace(item.redirectUrl, '_self');
router.replace(item.redirectUrl, '_self');
}
item.pageType && setPageType(item.pageType);
},
[lastRoute, setLoginStore, setPageType]
[lastRoute, router, setLoginStore, setPageType]
);
const onClickSso = useCallback(() => {
if (!feConfigs?.sso?.url) return;
setLoginStore({
provider: OAuthEnum.sso,
lastRoute,
state: state.current
});
const url = `${feConfigs.sso.url}/login/oauth/authorize?redirect_uri=${encodeURIComponent(redirectUri)}&state=${state.current}`;
window.open(url, '_self');
}, [feConfigs?.sso?.url, lastRoute, redirectUri, setLoginStore]);
useEffect(() => {
if (feConfigs?.sso?.autoLogin) {
onClickSso();
const sso = oAuthList.find((item) => item.provider === OAuthEnum.sso);
const wecom = oAuthList.find((item) => item.provider === OAuthEnum.wecom);
if (feConfigs?.sso?.autoLogin && sso) {
// sso auto
onClickOauth(sso);
} else if (isWecomWorkTerminal && wecom) {
// Auto wecom login
onClickOauth(wecom);
}
if (isWecom) {
onClickOauth(oAuthList.find((item) => item.provider === OAuthEnum.wecom));
}
}, [feConfigs?.sso?.autoLogin, isWecom]);
}, [feConfigs?.sso?.autoLogin, isWecomWorkTerminal, onClickOauth]);
return (
<Flex flexDirection={'column'} h={'100%'}>
@@ -189,28 +200,13 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
h={'40px'}
borderRadius={'sm'}
fontWeight={'medium'}
leftIcon={<MyIcon name={item.icon as any} w={'20px'} />}
leftIcon={<Avatar src={item.icon as any} w={'20px'} />}
onClick={() => onClickOauth(item)}
>
{item.label}
</Button>
</Box>
))}
{feConfigs?.sso?.url && (
<Box mt={4} color={'primary.700'} cursor={'pointer'} textAlign={'center'}>
<Button
variant={'whitePrimary'}
w={'100%'}
h={'40px'}
borderRadius={'sm'}
leftIcon={<MyImage alt="" src={feConfigs.sso.icon as any} w="20px" />}
onClick={onClickSso}
>
{feConfigs.sso.title}
</Button>
</Box>
)}
</Box>
</>
)}
@@ -218,4 +214,6 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
);
};
export default FormLayout;
export default dynamic(() => Promise.resolve(FormLayout), {
ssr: false
});

View File

@@ -16,7 +16,6 @@ import type { ResLogin } from '@/global/support/api/userRes.d';
import { useRouter } from 'next/router';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useChatStore } from '@/web/core/chat/context/useChatStore';
import LoginForm from './components/LoginForm/LoginForm';
import dynamic from 'next/dynamic';
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
import { clearToken, setToken } from '@/web/support/user/auth';
@@ -29,6 +28,7 @@ import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { GET } from '@/web/common/api/request';
import { getDocPath } from '@/web/common/system/doc';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import LoginForm from './components/LoginForm/LoginForm';
const RegisterForm = dynamic(() => import('./components/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
@@ -42,7 +42,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
const { t } = useTranslation();
const { lastRoute = '' } = router.query as { lastRoute: string };
const { feConfigs } = useSystemStore();
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}`>();
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}`>(LoginPageTypeEnum.passwordLogin);
const { setUserInfo } = useUserStore();
const { setLastChatAppId } = useChatStore();
const { isOpen, onOpen, onClose } = useDisclosure();

View File

@@ -13,16 +13,24 @@ import { getToken } from '@/web/support/user/auth';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRouter } from 'next/router';
const PriceBox = () => {
const { userInfo } = useUserStore();
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const router = useRouter();
const { data: teamSubPlan } = useQuery(['getTeamPlanStatus'], getTeamPlanStatus, {
enabled: !!getToken() || !!userInfo
});
const onPaySuccess = () => {
setTimeout(() => {
router.reload();
}, 1000);
};
return (
<Flex
h={'100%'}
@@ -45,7 +53,7 @@ const PriceBox = () => {
title: feConfigs?.systemTitle
})}
</Box>
<StandardPlan standardPlan={teamSubPlan?.standard} />
<StandardPlan standardPlan={teamSubPlan?.standard} onPaySuccess={onPaySuccess} />
<HStack mt={8} color={'blue.700'} ml={8}>
<MyIcon name={'infoRounded'} w={'1rem'} />
<Box fontSize={'sm'} fontWeight={'500'}>
@@ -62,7 +70,7 @@ const PriceBox = () => {
<Box mt={2} mb={8} color={'myGray.600'} fontSize={'md'}>
{t('common:support.wallet.subscription.Extra plan tip')}
</Box>
<ExtraPlan />
<ExtraPlan onPaySuccess={onPaySuccess} />
</VStack>
{/* points */}

View File

@@ -12,6 +12,7 @@ import { DatasetDataItemType } from '@fastgpt/global/core/dataset/type';
import { getVectorModel } from '@fastgpt/service/core/ai/model';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { ClientSession } from '@fastgpt/service/common/mongo';
import { MongoDatasetDataText } from '@fastgpt/service/core/dataset/data/dataTextSchema';
/* insert data.
* 1. create data id
@@ -42,7 +43,8 @@ export async function insertData2Dataset({
const qaStr = getDefaultIndex({ q, a }).text;
// empty indexes check, if empty, create default index
// 1. Get vector indexes and insert
// Empty indexes check, if empty, create default index
indexes =
Array.isArray(indexes) && indexes.length > 0
? indexes.map((index) => ({
@@ -77,7 +79,7 @@ export async function insertData2Dataset({
)
);
// create mongo data
// 2. Create mongo data
const [{ _id }] = await MongoDatasetData.create(
[
{
@@ -98,6 +100,20 @@ export async function insertData2Dataset({
{ session }
);
// 3. Create mongo data text
await MongoDatasetDataText.create(
[
{
teamId,
datasetId,
collectionId,
dataId: _id,
fullTextToken: jiebaSplit({ text: qaStr })
}
],
{ session }
);
return {
insertId: _id,
tokens: result.reduce((acc, cur) => acc + cur.tokens, 0)
@@ -225,11 +241,18 @@ export async function updateData2Dataset({
// update mongo other data
mongoData.q = q || mongoData.q;
mongoData.a = a ?? mongoData.a;
mongoData.fullTextToken = jiebaSplit({ text: mongoData.q + mongoData.a });
mongoData.fullTextToken = jiebaSplit({ text: `${mongoData.q}\n${mongoData.a}`.trim() });
// @ts-ignore
mongoData.indexes = newIndexes;
await mongoData.save({ session });
// update mongo data text
await MongoDatasetDataText.updateOne(
{ dataId: mongoData._id },
{ fullTextToken: jiebaSplit({ text: `${mongoData.q}\n${mongoData.a}`.trim() }) },
{ session }
);
// delete vector
const deleteIdList = patchResult
.filter((item) => item.type === 'delete' || item.type === 'update')

View File

@@ -166,9 +166,9 @@ const rebuildData = async ({
// get new mongoData insert to training
const newRebuildingData = await MongoDatasetData.findOneAndUpdate(
{
rebuilding: true,
teamId: mongoData.teamId,
datasetId: mongoData.datasetId,
rebuilding: true
datasetId: mongoData.datasetId
},
{
$unset: {

View File

@@ -42,8 +42,8 @@ type State = {
gitStar: number;
loadGitStar: () => Promise<void>;
notSufficientModalType: NotSufficientModalType | undefined;
setNotSufficientModalType: (val: NotSufficientModalType | undefined) => void;
notSufficientModalType?: NotSufficientModalType;
setNotSufficientModalType: (val?: NotSufficientModalType) => void;
initDataBufferId?: string;
feConfigs: FastGPTFeConfigsType;
@@ -115,7 +115,7 @@ export const useSystemStore = create<State>()(
},
notSufficientModalType: undefined,
setNotSufficientModalType(type: NotSufficientModalType | undefined) {
setNotSufficientModalType(type) {
set((state) => {
state.notSufficientModalType = type;
});