4.8.5 test (#1805)

* perf: revert tip

* feat: create copy app

* perf: file stream read

* perf: read directory over 100 files

* perf: index

* fix: team chat api error

* lock

* fix: i18n file
This commit is contained in:
Archer
2024-06-21 10:09:00 +08:00
committed by GitHub
parent 980b4d3db5
commit 5cc01b8509
57 changed files with 8660 additions and 10755 deletions

View File

@@ -4,6 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
import { authFileToken } from '@fastgpt/service/support/permission/controller';
import { getDownloadStream, getFileById } from '@fastgpt/service/common/file/gridfs/controller';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { stream2Encoding } from '@fastgpt/service/common/file/gridfs/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -17,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('fileId is empty');
}
const [file, { fileStream, encoding }] = await Promise.all([
const [file, fileStream] = await Promise.all([
getFileById({ bucketName, fileId }),
getDownloadStream({ bucketName, fileId })
]);
@@ -26,16 +27,26 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
return Promise.reject(CommonErrEnum.fileNotFound);
}
const { stream, encoding } = await (async () => {
if (file.metadata?.encoding) {
return {
stream: fileStream,
encoding: file.metadata.encoding
};
}
return stream2Encoding(fileStream);
})();
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
res.setHeader('Cache-Control', 'public, max-age=3600');
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`);
fileStream.pipe(res);
stream.pipe(res);
fileStream.on('error', () => {
stream.on('error', () => {
res.status(500).end();
});
fileStream.on('end', () => {
stream.on('end', () => {
res.end();
});
} catch (error) {
@@ -47,6 +58,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}
export const config = {
api: {
responseLimit: '32mb'
responseLimit: '100mb'
}
};

View File

@@ -0,0 +1,50 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { onCreateApp } from './create';
export type copyAppQuery = {};
export type copyAppBody = { appId: string };
export type copyAppResponse = {
appId: string;
};
async function handler(
req: ApiRequestProps<copyAppBody, copyAppQuery>,
res: ApiResponseType<any>
): Promise<copyAppResponse> {
const [{ app, tmbId }] = await Promise.all([
authApp({
req,
authToken: true,
per: WritePermissionVal,
appId: req.body.appId
}),
authUserPer({
req,
authToken: true,
per: WritePermissionVal
})
]);
const appId = await onCreateApp({
parentId: app.parentId,
name: app.name + ' Copy',
intro: app.intro,
avatar: app.avatar,
type: app.type,
modules: app.modules,
edges: app.edges,
teamId: app.teamId,
tmbId,
pluginData: app.pluginData
});
return { appId };
}
export default NextAPI(handler);

View File

@@ -42,27 +42,22 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
// 更新模型
await MongoApp.updateOne(
{
_id: appId
},
{
...parseParentIdInMongo(parentId),
name,
type,
avatar,
intro,
defaultPermission,
...(teamTags && teamTags),
...(formatNodes && {
modules: formatNodes
}),
...(edges && {
edges
}),
...(chatConfig && { chatConfig })
}
);
await MongoApp.findByIdAndUpdate(appId, {
...parseParentIdInMongo(parentId),
...(name && { name }),
...(type && { type }),
...(avatar && { avatar }),
...(intro !== undefined && { intro }),
...(defaultPermission && { defaultPermission }),
...(teamTags && { teamTags }),
...(formatNodes && {
modules: formatNodes
}),
...(edges && {
edges
}),
...(chatConfig && { chatConfig })
});
}
export default NextAPI(handler);

View File

@@ -1,50 +1,42 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { DelHistoryProps } from '@/global/core/chat/api';
import { autChatCrud } from '@/service/support/permission/auth/chat';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
/* clear chat history */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { appId, chatId, shareId, outLinkUid } = req.query as DelHistoryProps;
async function handler(req: ApiRequestProps<{}, DelHistoryProps>, res: NextApiResponse) {
const { appId, chatId } = req.query;
await autChatCrud({
req,
authToken: true,
appId,
chatId,
shareId,
outLinkUid,
per: 'w'
});
await autChatCrud({
req,
authToken: true,
...req.query,
per: 'w'
});
await mongoSessionRun(async (session) => {
await MongoChatItem.deleteMany(
{
appId,
chatId
},
{ session }
);
await MongoChat.findOneAndRemove(
{
appId,
chatId
},
{ session }
);
});
await mongoSessionRun(async (session) => {
await MongoChatItem.deleteMany(
{
appId,
chatId
},
{ session }
);
await MongoChat.findOneAndRemove(
{
appId,
chatId
},
{ session }
);
});
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
jsonRes(res);
}
export default NextAPI(handler);

View File

@@ -1,42 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
/* clear chat history */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { outLinkUid, chatIds } = req.body as {
outLinkUid: string;
chatIds: string[];
};
if (!outLinkUid) {
throw new Error('shareId or outLinkUid is required');
}
const sliceIds = chatIds.slice(0, 50);
await MongoChat.updateMany(
{
chatId: { $in: sliceIds },
source: ChatSourceEnum.share,
outLinkUid: { $exists: false }
},
{
$set: {
outLinkUid
}
}
);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -4,37 +4,30 @@ import { connectToDatabase } from '@/service/mongo';
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
import { autChatCrud } from '@/service/support/permission/auth/chat';
import type { DeleteChatItemProps } from '@/global/core/chat/api.d';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { appId, chatId, contentId, shareId, outLinkUid } = req.query as DeleteChatItemProps;
async function handler(req: ApiRequestProps<{}, DeleteChatItemProps>, res: NextApiResponse) {
const { appId, chatId, contentId, shareId, outLinkUid } = req.query;
if (!contentId || !chatId) {
return jsonRes(res);
}
await autChatCrud({
req,
authToken: true,
appId,
chatId,
shareId,
outLinkUid,
per: 'w'
});
await MongoChatItem.deleteOne({
appId,
chatId,
dataId: contentId
});
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
if (!contentId || !chatId) {
return jsonRes(res);
}
await autChatCrud({
req,
authToken: true,
...req.query,
per: 'w'
});
await MongoChatItem.deleteOne({
appId,
chatId,
dataId: contentId
});
jsonRes(res);
}
export default NextAPI(handler);

View File

@@ -1,40 +1,30 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { UpdateHistoryProps } from '@/global/core/chat/api.d';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import { autChatCrud } from '@/service/support/permission/auth/chat';
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps } from '@fastgpt/service/type/next';
/* update chat top, custom title */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { appId, chatId, teamId, shareId, outLinkUid, customTitle, top } =
req.body as UpdateHistoryProps;
await autChatCrud({
req,
authToken: true,
appId,
teamId,
chatId,
shareId,
outLinkUid,
per: 'w'
});
async function handler(req: ApiRequestProps<UpdateHistoryProps>, res: NextApiResponse) {
const { appId, chatId, customTitle, top } = req.body;
await autChatCrud({
req,
authToken: true,
...req.body,
per: 'w'
});
await MongoChat.findOneAndUpdate(
{ appId, chatId },
{
updateTime: new Date(),
...(customTitle !== undefined && { customTitle }),
...(top !== undefined && { top })
}
);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
await MongoChat.findOneAndUpdate(
{ appId, chatId },
{
updateTime: new Date(),
...(customTitle !== undefined && { customTitle }),
...(top !== undefined && { top })
}
);
jsonRes(res);
}
export default NextAPI(handler);

View File

@@ -3,18 +3,17 @@ import { getPublishList, postRevertVersion } from '@/web/core/app/api/version';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import CustomRightDrawer from '@fastgpt/web/components/common/MyDrawer/CustomRightDrawer';
import { useTranslation } from 'next-i18next';
import { useMemoizedFn } from 'ahooks';
import { Box, Button, Flex } from '@chakra-ui/react';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import { useContextSelector } from 'use-context-selector';
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { AppContext } from './context';
import { useI18n } from '@/web/context/I18n';
import { AppSchema } from '@fastgpt/global/core/app/type';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
export type InitProps = {
nodes: AppSchema['modules'];
@@ -33,11 +32,11 @@ const PublishHistoriesSlider = ({
}) => {
const { t } = useTranslation();
const { appT } = useI18n();
const { openConfirm, ConfirmModal } = useConfirm({
content: t('core.workflow.publish.OnRevert version confirm')
});
const { appDetail, setAppDetail } = useContextSelector(AppContext, (v) => v);
const { appDetail, setAppDetail, reloadAppLatestVersion } = useContextSelector(
AppContext,
(v) => v
);
const appId = appDetail._id;
const [selectedHistoryId, setSelectedHistoryId] = useState<string>();
@@ -73,8 +72,8 @@ const PublishHistoriesSlider = ({
[initData, onClose]
);
const { mutate: onRevert, isLoading: isReverting } = useRequest({
mutationFn: async (data: AppVersionSchemaType) => {
const { runAsync: onRevert } = useRequest2(
async (data: AppVersionSchemaType) => {
if (!appId) return;
await postRevertVersion(appId, {
versionId: data._id,
@@ -90,10 +89,14 @@ const PublishHistoriesSlider = ({
}));
onCloseSlider(data);
reloadAppLatestVersion();
},
{
successToast: appT('version.Revert success')
}
});
);
const showLoading = isLoading || isReverting;
const showLoading = isLoading;
return (
<>
@@ -173,24 +176,28 @@ const PublishHistoriesSlider = ({
{formatTime2YMDHM(item.time)}
</Box>
{item._id === selectedHistoryId && (
<MyTooltip label={t('core.workflow.publish.OnRevert version')}>
<MyIcon
name={'core/workflow/revertVersion'}
w={'20px'}
color={'primary.600'}
onClick={(e) => {
e.stopPropagation();
openConfirm(() => onRevert(item))();
}}
/>
</MyTooltip>
<PopoverConfirm
showCancel
content={t('core.workflow.publish.OnRevert version confirm')}
onConfirm={() => onRevert(item)}
Trigger={
<Box>
<MyTooltip label={t('core.workflow.publish.OnRevert version')}>
<MyIcon
name={'core/workflow/revertVersion'}
w={'20px'}
color={'primary.600'}
/>
</MyTooltip>
</Box>
}
/>
)}
</Flex>
);
})}
</ScrollList>
</CustomRightDrawer>
<ConfirmModal />
</>
);
};

View File

@@ -98,7 +98,6 @@ const AppCard = () => {
</Button>
{appDetail.permission.hasWritePer && feConfigs?.show_team_chat && (
<Button
mr={3}
size={['sm', 'md']}
variant={'whitePrimary'}
leftIcon={<DragHandleIcon w={'16px'} />}

View File

@@ -20,6 +20,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { compareWorkflow } from '@/web/core/workflow/utils';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import { publishStatusStyle } from '../constants';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const Header = ({
appForm,
@@ -134,7 +135,13 @@ const Header = ({
<PopoverConfirm
showCancel
content={t('core.app.Publish Confirm')}
Trigger={<Button isDisabled={isPublished}>{t('core.app.Publish')}</Button>}
Trigger={
<Box>
<MyTooltip label={t('core.app.Publish app tip')}>
<Button isDisabled={isPublished}>{t('core.app.Publish')}</Button>
</MyTooltip>
</Box>
}
onConfirm={() => onSubmitPublish(appForm)}
/>
</>

View File

@@ -7,15 +7,22 @@ import { useContextSelector } from 'use-context-selector';
import { AppContext, TabEnum } from '../context';
import dynamic from 'next/dynamic';
import { Flex } from '@chakra-ui/react';
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
import { useTranslation } from 'next-i18next';
const Logs = dynamic(() => import('../Logs/index'));
const PublishChannel = dynamic(() => import('../Publish'));
const SimpleEdit = () => {
const { t } = useTranslation();
const { currentTab } = useContextSelector(AppContext, (v) => v);
const [appForm, setAppForm] = useState(getDefaultAppForm());
useBeforeunload({
tip: t('core.common.tip.leave page')
});
return (
<Flex h={'100%'} flexDirection={'column'} pr={3} pb={3}>
<Header appForm={appForm} setAppForm={setAppForm} />

View File

@@ -16,6 +16,7 @@ import { useRouter } from 'next/router';
import AppCard from '../WorkflowComponents/AppCard';
import { uiWorkflow2StoreWorkflow } from '../WorkflowComponents/utils';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const PublishHistories = dynamic(() => import('../PublishHistoriesSlider'));
const Header = () => {
@@ -53,6 +54,7 @@ const Header = () => {
router.push('/app/list');
} catch (error) {}
}, [onSaveWorkflow, router]);
// effect
useBeforeunload({
callback: onSaveWorkflow,
@@ -152,13 +154,17 @@ const Header = () => {
showCancel
content={t('core.app.Publish Confirm')}
Trigger={
<Button
ml={[2, 4]}
size={'sm'}
leftIcon={<MyIcon name={'common/publishFill'} w={['14px', '16px']} />}
>
{t('core.app.Publish')}
</Button>
<Box>
<MyTooltip label={t('core.app.Publish app tip')}>
<Button
ml={[2, 4]}
size={'sm'}
leftIcon={<MyIcon name={'common/publishFill'} w={['14px', '16px']} />}
>
{t('core.app.Publish')}
</Button>
</MyTooltip>
</Box>
}
onConfirm={() => onclickPublish()}
/>

View File

@@ -520,6 +520,7 @@ const WorkflowContextProvider = ({
historiesDefaultData ||
isSaving ||
nodes.length === 0 ||
edges.length === 0 ||
!!workflowDebugData
)
return;

View File

@@ -44,6 +44,7 @@ type AppContextType = {
chatConfig: AppChatConfigType;
}
| undefined;
reloadAppLatestVersion: () => void;
};
export const AppContext = createContext<AppContextType>({
@@ -72,7 +73,10 @@ export const AppContext = createContext<AppContextType>({
onPublish: function (data: PostPublishAppProps): Promise<void> {
throw new Error('Function not implemented.');
},
appLatestVersion: undefined
appLatestVersion: undefined,
reloadAppLatestVersion: function (): void {
throw new Error('Function not implemented.');
}
});
const AppContextProvider = ({ children }: { children: ReactNode }) => {
@@ -190,7 +194,8 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
onOpenTeamTagModal,
onDelApp,
onPublish,
appLatestVersion
appLatestVersion,
reloadAppLatestVersion
};
return (

View File

@@ -1,5 +1,5 @@
import React, { useMemo, useState } from 'react';
import { Box, Grid, Flex, IconButton, border } from '@chakra-ui/react';
import { Box, Grid, Flex, IconButton } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { delAppById, putAppById } from '@/web/core/app/api';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
@@ -34,16 +34,15 @@ const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditRe
const ConfigPerModal = dynamic(() => import('@/components/support/permission/ConfigPerModal'));
import type { EditHttpPluginProps } from './HttpPluginEditModal';
import { postCopyApp } from '@/web/core/app/api/app';
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
const ListItem = () => {
const { t } = useTranslation();
const { appT } = useI18n();
const router = useRouter();
const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail } = useContextSelector(
AppListContext,
(v) => v
);
const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail, parentId } =
useContextSelector(AppListContext, (v) => v);
const [loadingAppId, setLoadingAppId] = useState<string>();
const [editedApp, setEditedApp] = useState<EditResourceInfoFormType>();
@@ -68,27 +67,33 @@ const ListItem = () => {
}
});
const { openConfirm, ConfirmModal } = useConfirm({
const { openConfirm: openConfirmDel, ConfirmModal: DelConfirmModal } = useConfirm({
type: 'delete'
});
const { run: onclickDelApp } = useRequest2(
const { runAsync: onclickDelApp } = useRequest2(
(id: string) => {
setLoadingAppId(id);
return delAppById(id);
},
{
onSuccess() {
loadMyApps();
},
onFinally() {
setLoadingAppId(undefined);
},
successToast: t('common.Delete Success'),
errorToast: t('common.Delete Failed')
}
);
const { openConfirm: openConfirmCopy, ConfirmModal: ConfirmCopyModal } = useConfirm({
content: appT('Confirm copy app tip')
});
const { runAsync: onclickCopy } = useRequest2(postCopyApp, {
onSuccess({ appId }) {
router.push(`/app/detail?appId=${appId}`);
loadMyApps();
},
successToast: appT('Create copy success')
});
return (
<>
<Grid
@@ -218,6 +223,16 @@ const ListItem = () => {
: [])
]
},
{
children: [
{
icon: 'copy',
label: appT('Copy one app'),
onClick: () =>
openConfirmCopy(() => onclickCopy({ appId: app._id }))()
}
]
},
{
children: [
{
@@ -238,7 +253,7 @@ const ListItem = () => {
icon: 'delete',
label: t('common.Delete'),
onClick: () =>
openConfirm(
openConfirmDel(
() => onclickDelApp(app._id),
undefined,
app.type === AppTypeEnum.folder
@@ -280,7 +295,9 @@ const ListItem = () => {
</Grid>
{myApps.length === 0 && <EmptyTip text={'还没有应用,快去创建一个吧!'} pt={'30vh'} />}
<ConfirmModal />
<DelConfirmModal />
<ConfirmCopyModal />
{!!editedApp && (
<EditResourceModal
{...editedApp}

View File

@@ -163,7 +163,7 @@ const ChatHistorySlider = ({
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
{!isPc && appId && (
<Tabs
w={'180px'}
flex={'1 0 0'}
mr={2}
list={[
{ label: t('core.chat.Recent use'), id: TabEnum.recently },
@@ -176,7 +176,7 @@ const ChatHistorySlider = ({
)}
<Button
variant={'whitePrimary'}
flex={1}
flex={['0', 1]}
h={'100%'}
color={'primary.600'}
borderRadius={'xl'}

View File

@@ -298,7 +298,7 @@ const OutLink = ({
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'250px'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
<DrawerContent maxWidth={'75vw'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
{children}
</DrawerContent>
</Drawer>

View File

@@ -83,8 +83,8 @@ const OutLink = () => {
data: {
messages: prompts,
variables: {
...customVariables,
...variables
...variables,
...customVariables
},
appId,
teamId,
@@ -290,7 +290,7 @@ const OutLink = () => {
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'250px'}>{children}</DrawerContent>
<DrawerContent maxWidth={'75vw'}>{children}</DrawerContent>
</Drawer>
);
})(

View File

@@ -170,12 +170,14 @@ const FileSelector = ({
const items = e.dataTransfer.items;
const fileList: SelectFileItemType[] = [];
if (e.dataTransfer.items.length <= 1) {
const traverseFileTree = async (item: any) => {
return new Promise<void>((resolve, reject) => {
if (item.isFile) {
item.file((file: File) => {
const folderPath = (item.fullPath || '').split('/').slice(2, -1).join('/');
const firstEntry = items[0].webkitGetAsEntry();
if (firstEntry?.isDirectory && items.length === 1) {
{
const readFile = (entry: any) => {
return new Promise((resolve) => {
entry.file((file: File) => {
const folderPath = (entry.fullPath || '').split('/').slice(2, -1).join('/');
if (filterTypeReg.test(file.name)) {
fileList.push({
@@ -184,24 +186,45 @@ const FileSelector = ({
file
});
}
resolve();
resolve(file);
});
} else if (item.isDirectory) {
const dirReader = item.createReader();
});
};
const traverseFileTree = (dirReader: any) => {
return new Promise((resolve) => {
let fileNum = 0;
dirReader.readEntries(async (entries: any[]) => {
for (let i = 0; i < entries.length; i++) {
await traverseFileTree(entries[i]);
for await (const entry of entries) {
if (entry.isFile) {
await readFile(entry);
fileNum++;
} else if (entry.isDirectory) {
await traverseFileTree(entry.createReader());
}
}
resolve();
});
}
});
};
for await (const item of items) {
await traverseFileTree(item.webkitGetAsEntry());
// chrome: readEntries will return 100 entries at most
if (fileNum === 100) {
await traverseFileTree(dirReader);
}
resolve('');
});
});
};
for await (const item of items) {
const entry = item.webkitGetAsEntry();
if (entry) {
if (entry.isFile) {
await readFile(entry);
} else if (entry.isDirectory) {
//@ts-ignore
await traverseFileTree(entry.createReader());
}
}
}
}
} else {
} else if (firstEntry?.isFile) {
const files = Array.from(e.dataTransfer.files);
let isErr = files.some((item) => item.type === '');
if (isErr) {
@@ -220,6 +243,11 @@ const FileSelector = ({
file
}))
);
} else {
return toast({
title: fileT('upload error description'),
status: 'error'
});
}
selectFileCallback(fileList.slice(0, maxCount));

View File

@@ -55,6 +55,12 @@ export async function insertData2Dataset({
if (!indexes.find((index) => index.defaultIndex)) {
indexes.unshift(getDefaultIndex({ q, a }));
} else if (q && a && !indexes.find((index) => index.text === q)) {
// push a q index
indexes.push({
defaultIndex: false,
text: q
});
}
indexes = indexes.slice(0, 6);

View File

@@ -11,6 +11,7 @@ import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/au
import { AuthUserTypeEnum, ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { addLog } from '@fastgpt/service/common/system/log';
/*
outLink: Must be the owner
token: team owner and chat owner have all permissions
@@ -55,6 +56,7 @@ export async function autChatCrud({
// auth team space chat
if (spaceTeamId && teamToken) {
const { uid } = await authTeamSpaceToken({ teamId: spaceTeamId, teamToken });
addLog.debug('Auth team token', { uid, spaceTeamId, teamToken, chatUid: chat.outLinkUid });
if (!chat || (String(chat.teamId) === String(spaceTeamId) && chat.outLinkUid === uid)) {
return { uid };
}

View File

@@ -1,12 +1,12 @@
import 'i18next';
import common from '../../i18n/zh/common.json';
import dataset from '../../i18n/zh/dataset.json';
import app from '../../i18n/zh/app.json';
import file from '../../i18n/zh/file.json';
import publish from '../../i18n/zh/publish.json';
import workflow from '../../i18n/zh/workflow.json';
import user from '../../i18n/zh/user.json';
import chat from '../../i18n/zh/chat.json';
import common from '@fastgpt/web/i18n/zh/common.json';
import dataset from '@fastgpt/web/i18n/zh/dataset.json';
import app from '@fastgpt/web/i18n/zh/app.json';
import file from '@fastgpt/web/i18n/zh/file.json';
import publish from '@fastgpt/web/i18n/zh/publish.json';
import workflow from '@fastgpt/web/i18n/zh/workflow.json';
import user from '@fastgpt/web/i18n/zh/user.json';
import chat from '@fastgpt/web/i18n/zh/chat.json';
export interface I18nNamespaces {
common: typeof common;

View File

@@ -10,7 +10,7 @@ export const postUploadFiles = (
onUploadProgress: (progressEvent: AxiosProgressEvent) => void
) =>
POST<string>('/common/file/upload', data, {
timeout: 480000,
timeout: 600000,
onUploadProgress,
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8'

View File

@@ -6,6 +6,7 @@ import type {
transitionWorkflowBody,
transitionWorkflowResponse
} from '@/pages/api/core/app/transitionWorkflow';
import type { copyAppQuery, copyAppResponse } from '@/pages/api/core/app/copy';
/* folder */
export const postCreateAppFolder = (data: CreateAppFolderBody) =>
@@ -15,6 +16,7 @@ export const getAppFolderPath = (parentId: ParentIdType) =>
GET<ParentTreePathItemType[]>(`/core/app/folder/path`, { parentId });
/* detail */
export const postTransition2Workflow = (data: transitionWorkflowBody) =>
POST<transitionWorkflowResponse>('/core/app/transitionWorkflow', data);
export const postCopyApp = (data: copyAppQuery) => POST<copyAppResponse>('/core/app/copy', data);