mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
feat: chat slider support folder (#1759)
* feat: docker-compose version * feat: chat slider support folder * lazy behavior * pref: code sandbox size
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 'V4.8.4(进行中)'
|
title: 'V4.8.4'
|
||||||
description: 'FastGPT V4.8.4 更新说明'
|
description: 'FastGPT V4.8.4 更新说明'
|
||||||
icon: 'upgrade'
|
icon: 'upgrade'
|
||||||
draft: false
|
draft: false
|
||||||
|
@@ -114,15 +114,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
@@ -72,15 +72,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
@@ -53,15 +53,15 @@ services:
|
|||||||
wait $$!
|
wait $$!
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
|
@@ -51,6 +51,7 @@ export const iconPaths = {
|
|||||||
'common/routePushLight': () => import('./icons/common/routePushLight.svg'),
|
'common/routePushLight': () => import('./icons/common/routePushLight.svg'),
|
||||||
'common/saveFill': () => import('./icons/common/saveFill.svg'),
|
'common/saveFill': () => import('./icons/common/saveFill.svg'),
|
||||||
'common/searchLight': () => import('./icons/common/searchLight.svg'),
|
'common/searchLight': () => import('./icons/common/searchLight.svg'),
|
||||||
|
'common/select': () => import('./icons/common/select.svg'),
|
||||||
'common/selectLight': () => import('./icons/common/selectLight.svg'),
|
'common/selectLight': () => import('./icons/common/selectLight.svg'),
|
||||||
'common/settingLight': () => import('./icons/common/settingLight.svg'),
|
'common/settingLight': () => import('./icons/common/settingLight.svg'),
|
||||||
'common/text/t': () => import('./icons/common/text/t.svg'),
|
'common/text/t': () => import('./icons/common/text/t.svg'),
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" fill="none">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M5.42469 4.94872C5.16434 5.20906 5.16434 5.63117 5.42469 5.89152C5.68504 6.15187 6.10715 6.15187 6.3675 5.89152L8.00002 4.25901L9.63253 5.89152C9.89288 6.15187 10.315 6.15187 10.5753 5.89152C10.8357 5.63117 10.8357 5.20906 10.5753 4.94872L8.47142 2.8448C8.21107 2.58445 7.78896 2.58445 7.52861 2.8448L5.42469 4.94872ZM5.42469 10.8375C5.16434 11.0979 5.16434 11.52 5.42469 11.7803L7.52861 13.8843C7.56115 13.9168 7.59623 13.9453 7.63319 13.9697C7.89196 14.1405 8.24361 14.1121 8.47142 13.8843L10.5753 11.7803C10.8357 11.52 10.8357 11.0979 10.5753 10.8375C10.315 10.5772 9.89288 10.5772 9.63253 10.8375L8.00002 12.47L6.3675 10.8375C6.10715 10.5772 5.68504 10.5772 5.42469 10.8375Z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 823 B |
@@ -81,7 +81,14 @@ const MyMenu = ({
|
|||||||
}, [offset]);
|
}, [offset]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu offset={computeOffset} isOpen={isOpen} autoSelect={false} direction={'ltr'} isLazy>
|
<Menu
|
||||||
|
offset={computeOffset}
|
||||||
|
isOpen={isOpen}
|
||||||
|
autoSelect={false}
|
||||||
|
direction={'ltr'}
|
||||||
|
isLazy
|
||||||
|
lazyBehavior={'keepMounted'}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onMouseEnter={() => {
|
onMouseEnter={() => {
|
||||||
|
48
packages/web/components/common/MyPopover/index.tsx
Normal file
48
packages/web/components/common/MyPopover/index.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverTrigger,
|
||||||
|
PopoverContent,
|
||||||
|
useDisclosure,
|
||||||
|
PlacementWithLogical
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
const MyPopover = ({
|
||||||
|
Trigger,
|
||||||
|
placement,
|
||||||
|
offset,
|
||||||
|
trigger,
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
Trigger: React.ReactNode;
|
||||||
|
placement?: PlacementWithLogical;
|
||||||
|
offset?: [number, number];
|
||||||
|
trigger?: 'hover' | 'click';
|
||||||
|
children: (e: { onClose: () => void }) => React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
const firstFieldRef = React.useRef(null);
|
||||||
|
|
||||||
|
const { onOpen, onClose, isOpen } = useDisclosure();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
isOpen={isOpen}
|
||||||
|
initialFocusRef={firstFieldRef}
|
||||||
|
onOpen={onOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
placement={placement}
|
||||||
|
offset={offset}
|
||||||
|
closeOnBlur={false}
|
||||||
|
trigger={trigger}
|
||||||
|
openDelay={100}
|
||||||
|
closeDelay={100}
|
||||||
|
isLazy
|
||||||
|
lazyBehavior="keepMounted"
|
||||||
|
>
|
||||||
|
<PopoverTrigger>{Trigger}</PopoverTrigger>
|
||||||
|
<PopoverContent p={4}>{children({ onClose })}</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MyPopover;
|
@@ -73,6 +73,7 @@
|
|||||||
"Last use time": "Last use time",
|
"Last use time": "Last use time",
|
||||||
"Load Failed": "Load Failed",
|
"Load Failed": "Load Failed",
|
||||||
"Loading": "Loading...",
|
"Loading": "Loading...",
|
||||||
|
"More": "More",
|
||||||
"More settings": "More settings",
|
"More settings": "More settings",
|
||||||
"Move": "Move",
|
"Move": "Move",
|
||||||
"MultipleRowSelect": {
|
"MultipleRowSelect": {
|
||||||
@@ -430,6 +431,7 @@
|
|||||||
"Quote": "Quote",
|
"Quote": "Quote",
|
||||||
"Quote Amount": "Dataset quotes ({{amount}} items)",
|
"Quote Amount": "Dataset quotes ({{amount}} items)",
|
||||||
"Read Mark Description": "View introduction to marking function",
|
"Read Mark Description": "View introduction to marking function",
|
||||||
|
"Recent use": "Recent use",
|
||||||
"Record": "Voice input",
|
"Record": "Voice input",
|
||||||
"Restart": "Restart conversation",
|
"Restart": "Restart conversation",
|
||||||
"Select File": "Select file",
|
"Select File": "Select file",
|
||||||
|
@@ -73,6 +73,7 @@
|
|||||||
"Last use time": "最后使用时间",
|
"Last use time": "最后使用时间",
|
||||||
"Load Failed": "加载失败",
|
"Load Failed": "加载失败",
|
||||||
"Loading": "加载中...",
|
"Loading": "加载中...",
|
||||||
|
"More": "更多",
|
||||||
"More settings": "更多设置",
|
"More settings": "更多设置",
|
||||||
"Move": "移动",
|
"Move": "移动",
|
||||||
"MultipleRowSelect": {
|
"MultipleRowSelect": {
|
||||||
@@ -431,6 +432,7 @@
|
|||||||
"Quote": "引用",
|
"Quote": "引用",
|
||||||
"Quote Amount": "知识库引用({{amount}}条)",
|
"Quote Amount": "知识库引用({{amount}}条)",
|
||||||
"Read Mark Description": "查看标注功能介绍",
|
"Read Mark Description": "查看标注功能介绍",
|
||||||
|
"Recent use": "最近使用",
|
||||||
"Record": "语音输入",
|
"Record": "语音输入",
|
||||||
"Restart": "重开对话",
|
"Restart": "重开对话",
|
||||||
"Select File": "选择文件",
|
"Select File": "选择文件",
|
||||||
|
@@ -1,12 +1,17 @@
|
|||||||
### FastGPT V4.8.2
|
### FastGPT V4.8.4
|
||||||
|
|
||||||
|
1. 新增 - 应用使用新权限系统。
|
||||||
|
2. 新增 - 应用支持文件夹。
|
||||||
|
3. 优化 - 文本分割增加连续换行、制表符清除,避免大文本性能问题。
|
||||||
|
4. 重要修复 - 修复系统插件运行池数据污染问题,由于从内存获取,会导致全局污染。
|
||||||
|
5. 修复 - Debug 模式下,相同 source 和 target 内容,导致连线显示异常。
|
||||||
|
6. 修复 - 定时执行初始化错误。
|
||||||
|
7. 修复 - 应用调用传参异常。
|
||||||
|
8. 修复 - ctrl + cv 复杂节点时,nodeId错误。
|
||||||
|
9. 调整组件库全局theme。
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
- 新增 - 知识库重新选择向量模型重建
|
|
||||||
- 新增 - 对话框支持问题模糊检索提示,可自定义预设问题词库
|
|
||||||
- 新增 - js代码运行节点
|
|
||||||
- 新增 - 外部文件源知识库: [点击查看文档](https://doc.fastai.site/docs/course/externalfile/)
|
|
||||||
- 新增 - 内容提取节点增加完全提取成功输出
|
|
||||||
- 新增 - HTTP节点增加错误输出,可以自行判断处理
|
|
||||||
- 优化 - 插件输入的 debug 模式,支持全量参数输入渲染
|
|
||||||
- [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro)
|
- [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro)
|
||||||
- [使用文档](https://doc.fastgpt.in/docs/intro/)
|
- [使用文档](https://doc.fastgpt.in/docs/intro/)
|
||||||
- [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)
|
- [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)
|
@@ -13,7 +13,6 @@ const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
|
|||||||
alt=""
|
alt=""
|
||||||
w={w}
|
w={w}
|
||||||
h={w}
|
h={w}
|
||||||
p={'1px'}
|
|
||||||
src={src || LOGO_ICON}
|
src={src || LOGO_ICON}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, BoxProps, Flex } from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
GetResourceFolderListProps,
|
GetResourceFolderListProps,
|
||||||
GetResourceListItemResponse,
|
GetResourceListItemResponse,
|
||||||
@@ -10,24 +10,43 @@ import Loading from '@fastgpt/web/components/common/MyLoading';
|
|||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { FolderImgUrl } from '@fastgpt/global/common/file/image/constants';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
type ResourceItemType = GetResourceListItemResponse & {
|
type ResourceItemType = GetResourceListItemResponse & {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
children?: ResourceItemType[];
|
children?: ResourceItemType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rootId = 'root';
|
||||||
|
|
||||||
const SelectOneResource = ({
|
const SelectOneResource = ({
|
||||||
server,
|
server,
|
||||||
value,
|
value,
|
||||||
onSelect
|
onSelect,
|
||||||
|
maxH = ['80vh', '600px']
|
||||||
}: {
|
}: {
|
||||||
server: (e: GetResourceFolderListProps) => Promise<GetResourceListItemResponse[]>;
|
server: (e: GetResourceFolderListProps) => Promise<GetResourceListItemResponse[]>;
|
||||||
value?: ParentIdType;
|
value?: ParentIdType;
|
||||||
onSelect: (e?: string) => any;
|
onSelect: (e?: string) => any;
|
||||||
|
maxH?: BoxProps['maxH'];
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [dataList, setDataList] = useState<ResourceItemType[]>([]);
|
const [dataList, setDataList] = useState<ResourceItemType[]>([]);
|
||||||
const [requestingIdList, setRequestingIdList] = useState<ParentIdType[]>([]);
|
const [requestingIdList, setRequestingIdList] = useState<ParentIdType[]>([]);
|
||||||
|
|
||||||
|
const concatRoot = useMemo(() => {
|
||||||
|
const root: ResourceItemType = {
|
||||||
|
id: rootId,
|
||||||
|
open: true,
|
||||||
|
avatar: FolderImgUrl,
|
||||||
|
name: t('common.folder.Root Path'),
|
||||||
|
isFolder: true,
|
||||||
|
children: dataList
|
||||||
|
};
|
||||||
|
return [root];
|
||||||
|
}, [dataList, t]);
|
||||||
|
|
||||||
const { runAsync: requestServer } = useRequest2((e: GetResourceFolderListProps) => {
|
const { runAsync: requestServer } = useRequest2((e: GetResourceFolderListProps) => {
|
||||||
if (requestingIdList.includes(e.parentId)) return Promise.reject(null);
|
if (requestingIdList.includes(e.parentId)) return Promise.reject(null);
|
||||||
|
|
||||||
@@ -59,7 +78,7 @@ const SelectOneResource = ({
|
|||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
py={1}
|
py={1}
|
||||||
pl={`${1.25 * index + 0.5}rem`}
|
pl={index === 0 ? '0.5rem' : `${1.75 * (index - 1) + 0.5}rem`}
|
||||||
pr={2}
|
pr={2}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
_hover={{
|
_hover={{
|
||||||
@@ -72,6 +91,7 @@ const SelectOneResource = ({
|
|||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
|
if (item.id === rootId) return;
|
||||||
// folder => open(request children) or close
|
// folder => open(request children) or close
|
||||||
if (item.isFolder) {
|
if (item.isFolder) {
|
||||||
if (!item.children) {
|
if (!item.children) {
|
||||||
@@ -90,33 +110,31 @@ const SelectOneResource = ({
|
|||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Flex
|
{index !== 0 && (
|
||||||
alignItems={'center'}
|
<Flex
|
||||||
justifyContent={'center'}
|
alignItems={'center'}
|
||||||
visibility={
|
justifyContent={'center'}
|
||||||
item.isFolder && (!item.children || item.children.length > 0)
|
visibility={item.isFolder ? 'visible' : 'hidden'}
|
||||||
? 'visible'
|
w={'1.25rem'}
|
||||||
: 'hidden'
|
h={'1.25rem'}
|
||||||
}
|
cursor={'pointer'}
|
||||||
w={'1.25rem'}
|
borderRadius={'xs'}
|
||||||
h={'1.25rem'}
|
_hover={{
|
||||||
cursor={'pointer'}
|
bg: 'rgba(31, 35, 41, 0.08)'
|
||||||
borderRadius={'xs'}
|
}}
|
||||||
_hover={{
|
>
|
||||||
bg: 'rgba(31, 35, 41, 0.08)'
|
<MyIcon
|
||||||
}}
|
name={
|
||||||
>
|
requestingIdList.includes(item.id)
|
||||||
<MyIcon
|
? 'common/loading'
|
||||||
name={
|
: 'common/rightArrowFill'
|
||||||
requestingIdList.includes(item.id)
|
}
|
||||||
? 'common/loading'
|
w={'14px'}
|
||||||
: 'common/rightArrowFill'
|
color={'myGray.500'}
|
||||||
}
|
transform={item.open ? 'rotate(90deg)' : 'none'}
|
||||||
w={'14px'}
|
/>
|
||||||
color={'myGray.500'}
|
</Flex>
|
||||||
transform={item.open ? 'rotate(90deg)' : 'none'}
|
)}
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Avatar ml={index !== 0 ? '0.5rem' : 0} src={item.avatar} w={'1.25rem'} />
|
<Avatar ml={index !== 0 ? '0.5rem' : 0} src={item.avatar} w={'1.25rem'} />
|
||||||
<Box fontSize={'sm'} ml={2}>
|
<Box fontSize={'sm'} ml={2}>
|
||||||
{item.name}
|
{item.name}
|
||||||
@@ -134,7 +152,13 @@ const SelectOneResource = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return loading ? <Loading fixed={false} /> : <Render list={dataList} />;
|
return loading ? (
|
||||||
|
<Loading fixed={false} />
|
||||||
|
) : (
|
||||||
|
<Box maxH={maxH} overflow={'auto'}>
|
||||||
|
<Render list={concatRoot} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectOneResource;
|
export default SelectOneResource;
|
||||||
|
@@ -16,8 +16,9 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
|||||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||||
|
|
||||||
export type ListAppBody = {
|
export type ListAppBody = {
|
||||||
parentId: ParentIdType;
|
parentId?: ParentIdType;
|
||||||
type?: AppTypeEnum;
|
type?: AppTypeEnum;
|
||||||
|
getRecentlyChat?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handler(
|
async function handler(
|
||||||
@@ -35,14 +36,23 @@ async function handler(
|
|||||||
per: ReadPermissionVal
|
per: ReadPermissionVal
|
||||||
});
|
});
|
||||||
|
|
||||||
const { parentId, type } = req.body;
|
const { parentId, type, getRecentlyChat } = req.body;
|
||||||
|
|
||||||
|
const findAppsQuery = getRecentlyChat
|
||||||
|
? {
|
||||||
|
// get all chat app
|
||||||
|
teamId,
|
||||||
|
type: { $in: [AppTypeEnum.advanced, AppTypeEnum.simple] }
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
teamId,
|
||||||
|
...(type && { type }),
|
||||||
|
...parseParentIdInMongo(parentId)
|
||||||
|
};
|
||||||
|
|
||||||
/* temp: get all apps and per */
|
/* temp: get all apps and per */
|
||||||
const [myApps, rpList] = await Promise.all([
|
const [myApps, rpList] = await Promise.all([
|
||||||
MongoApp.find(
|
MongoApp.find(findAppsQuery, '_id avatar type name intro tmbId defaultPermission')
|
||||||
{ teamId, ...(type && { type }), ...parseParentIdInMongo(parentId) },
|
|
||||||
'_id avatar type name intro tmbId defaultPermission'
|
|
||||||
)
|
|
||||||
.sort({
|
.sort({
|
||||||
updateTime: -1
|
updateTime: -1
|
||||||
})
|
})
|
||||||
@@ -69,7 +79,9 @@ async function handler(
|
|||||||
})
|
})
|
||||||
.filter((app) => app.permission.hasReadPer);
|
.filter((app) => app.permission.hasReadPer);
|
||||||
|
|
||||||
return filterApps.map((app) => ({
|
const sliceApps = getRecentlyChat ? filterApps.slice(0, 15) : filterApps;
|
||||||
|
|
||||||
|
return sliceApps.map((app) => ({
|
||||||
_id: app._id,
|
_id: app._id,
|
||||||
avatar: app.avatar,
|
avatar: app.avatar,
|
||||||
type: app.type,
|
type: app.type,
|
||||||
|
@@ -1,15 +1,5 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import {
|
import { Box, Button, Flex, useTheme, IconButton } from '@chakra-ui/react';
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
useTheme,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuList,
|
|
||||||
MenuItem,
|
|
||||||
IconButton
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
|
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@@ -21,10 +11,15 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
|||||||
import Tabs from '@/components/Tabs';
|
import Tabs from '@/components/Tabs';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||||
|
import SelectOneResource from '@/components/common/folder/SelectOneResource';
|
||||||
|
import {
|
||||||
|
GetResourceFolderListProps,
|
||||||
|
GetResourceListItemResponse
|
||||||
|
} from '@fastgpt/global/common/parentFolder/type';
|
||||||
|
import { getMyApps } from '@/web/core/app/api';
|
||||||
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
|
|
||||||
type HistoryItemType = {
|
type HistoryItemType = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -34,6 +29,7 @@ type HistoryItemType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum TabEnum {
|
enum TabEnum {
|
||||||
|
recently = 'recently',
|
||||||
'app' = 'app',
|
'app' = 'app',
|
||||||
'history' = 'history'
|
'history' = 'history'
|
||||||
}
|
}
|
||||||
@@ -69,6 +65,8 @@ const ChatHistorySlider = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const isTeamChat = router.pathname === '/chat/team';
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { appT } = useI18n();
|
const { appT } = useI18n();
|
||||||
|
|
||||||
@@ -78,6 +76,7 @@ const ChatHistorySlider = ({
|
|||||||
const [currentTab, setCurrentTab] = useState<`${TabEnum}`>(TabEnum.history);
|
const [currentTab, setCurrentTab] = useState<`${TabEnum}`>(TabEnum.history);
|
||||||
|
|
||||||
const showApps = apps?.length > 0;
|
const showApps = apps?.length > 0;
|
||||||
|
|
||||||
// custom title edit
|
// custom title edit
|
||||||
const { onOpenModal, EditModal: EditTitleModal } = useEditTitle({
|
const { onOpenModal, EditModal: EditTitleModal } = useEditTitle({
|
||||||
title: t('core.chat.Custom History Title'),
|
title: t('core.chat.Custom History Title'),
|
||||||
@@ -96,17 +95,33 @@ const ChatHistorySlider = ({
|
|||||||
[activeChatId, history, t]
|
[activeChatId, history, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
useQuery(['init'], () => {
|
|
||||||
if (!showApps) {
|
|
||||||
setCurrentTab(TabEnum.history);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
const canRouteToDetail = useMemo(
|
const canRouteToDetail = useMemo(
|
||||||
() => appId && userInfo?.team.role !== TeamMemberRoleEnum.visitor,
|
() => appId && userInfo?.team.permission.hasWritePer,
|
||||||
[appId, userInfo?.team.role]
|
[appId, userInfo?.team.permission.hasWritePer]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => {
|
||||||
|
return getMyApps({ parentId }).then((res) =>
|
||||||
|
res.map<GetResourceListItemResponse>((item) => ({
|
||||||
|
id: item._id,
|
||||||
|
name: item.name,
|
||||||
|
avatar: item.avatar,
|
||||||
|
isFolder: item.type === AppTypeEnum.folder
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onChangeApp = useCallback(
|
||||||
|
(appId: string) => {
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
chatId: '',
|
||||||
|
appId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[router]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -148,10 +163,11 @@ const ChatHistorySlider = ({
|
|||||||
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
|
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
|
||||||
{!isPc && appId && (
|
{!isPc && appId && (
|
||||||
<Tabs
|
<Tabs
|
||||||
w={'120px'}
|
w={'180px'}
|
||||||
mr={2}
|
mr={2}
|
||||||
list={[
|
list={[
|
||||||
{ label: 'App', id: TabEnum.app },
|
{ label: t('core.chat.Recent use'), id: TabEnum.recently },
|
||||||
|
{ label: t('App'), id: TabEnum.app },
|
||||||
{ label: t('core.chat.History'), id: TabEnum.history }
|
{ label: t('core.chat.History'), id: TabEnum.history }
|
||||||
]}
|
]}
|
||||||
activeId={currentTab}
|
activeId={currentTab}
|
||||||
@@ -291,7 +307,7 @@ const ChatHistorySlider = ({
|
|||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{currentTab === TabEnum.app && !isPc && (
|
{currentTab === TabEnum.recently && !isPc && (
|
||||||
<>
|
<>
|
||||||
{Array.isArray(apps) &&
|
{Array.isArray(apps) &&
|
||||||
apps.map((item) => (
|
apps.map((item) => (
|
||||||
@@ -309,12 +325,7 @@ const ChatHistorySlider = ({
|
|||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
router.replace({
|
onChangeApp(item._id);
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
appId: item._id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
@@ -327,9 +338,23 @@ const ChatHistorySlider = ({
|
|||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{currentTab === TabEnum.app && !isPc && (
|
||||||
|
<>
|
||||||
|
<SelectOneResource
|
||||||
|
value={appId}
|
||||||
|
onSelect={(id) => {
|
||||||
|
if (!id) return;
|
||||||
|
onChangeApp(id);
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
server={getAppList}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{!isPc && appId && (
|
{/* exec */}
|
||||||
|
{!isPc && appId && !isTeamChat && (
|
||||||
<Flex
|
<Flex
|
||||||
mt={2}
|
mt={2}
|
||||||
borderTop={theme.borders.base}
|
borderTop={theme.borders.base}
|
||||||
|
@@ -1,27 +1,53 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Flex, Box, IconButton } from '@chakra-ui/react';
|
import { Flex, Box, IconButton, HStack } from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||||
|
import MyDivider from '@fastgpt/web/components/common/MyDivider';
|
||||||
|
import MyPopover from '@fastgpt/web/components/common/MyPopover/index';
|
||||||
|
import SelectOneResource from '@/components/common/folder/SelectOneResource';
|
||||||
|
import { getMyApps } from '@/web/core/app/api';
|
||||||
|
import {
|
||||||
|
GetResourceFolderListProps,
|
||||||
|
GetResourceListItemResponse
|
||||||
|
} from '@fastgpt/global/common/parentFolder/type';
|
||||||
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
|
|
||||||
const SliderApps = ({
|
const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppId: string }) => {
|
||||||
showExist = true,
|
|
||||||
apps,
|
|
||||||
activeAppId
|
|
||||||
}: {
|
|
||||||
showExist?: boolean;
|
|
||||||
apps: AppListItemType[];
|
|
||||||
activeAppId: string;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const isTeamChat = router.pathname === '/chat/team';
|
||||||
|
|
||||||
|
const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => {
|
||||||
|
return getMyApps({ parentId }).then((res) =>
|
||||||
|
res.map<GetResourceListItemResponse>((item) => ({
|
||||||
|
id: item._id,
|
||||||
|
name: item.name,
|
||||||
|
avatar: item.avatar,
|
||||||
|
isFolder: item.type === AppTypeEnum.folder
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onChangeApp = useCallback(
|
||||||
|
(appId: string) => {
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
chatId: '',
|
||||||
|
appId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection={'column'} h={'100%'}>
|
<Flex flexDirection={'column'} h={'100%'}>
|
||||||
<Box px={5} py={4}>
|
<Box mt={4} px={4}>
|
||||||
{showExist && (
|
{!isTeamChat && (
|
||||||
<Flex
|
<Flex
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
@@ -33,7 +59,7 @@ const SliderApps = ({
|
|||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
mr={3}
|
mr={3}
|
||||||
icon={<MyIcon name={'common/backFill'} w={'18px'} color={'primary.500'} />}
|
icon={<MyIcon name={'common/backFill'} w={'1rem'} color={'primary.500'} />}
|
||||||
bg={'white'}
|
bg={'white'}
|
||||||
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
|
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
|
||||||
size={'smSquare'}
|
size={'smSquare'}
|
||||||
@@ -45,7 +71,58 @@ const SliderApps = ({
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box flex={'1 0 0'} h={0} px={5} overflow={'overlay'}>
|
{!isTeamChat && (
|
||||||
|
<>
|
||||||
|
<MyDivider h={2} my={1} />
|
||||||
|
<MyPopover
|
||||||
|
placement="right-start"
|
||||||
|
offset={[30, -65]}
|
||||||
|
trigger="hover"
|
||||||
|
Trigger={
|
||||||
|
<HStack
|
||||||
|
px={4}
|
||||||
|
my={2}
|
||||||
|
color={'myGray.500'}
|
||||||
|
fontSize={'sm'}
|
||||||
|
justifyContent={'space-between'}
|
||||||
|
>
|
||||||
|
<Box>{t('core.chat.Recent use')}</Box>
|
||||||
|
<HStack
|
||||||
|
spacing={0.5}
|
||||||
|
cursor={'pointer'}
|
||||||
|
px={2}
|
||||||
|
py={'0.5'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
mr={-2}
|
||||||
|
userSelect={'none'}
|
||||||
|
_hover={{
|
||||||
|
bg: 'myGray.200'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box>{t('common.More')}</Box>
|
||||||
|
<MyIcon name={'common/select'} w={'1rem'} />
|
||||||
|
</HStack>
|
||||||
|
</HStack>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{({ onClose }) => (
|
||||||
|
<Box minH={'200px'}>
|
||||||
|
<SelectOneResource
|
||||||
|
value={activeAppId}
|
||||||
|
onSelect={(id) => {
|
||||||
|
if (!id) return;
|
||||||
|
onChangeApp(id);
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
server={getAppList}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</MyPopover>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box flex={'1 0 0'} px={4} h={0} overflow={'overlay'}>
|
||||||
{apps.map((item) => (
|
{apps.map((item) => (
|
||||||
<Flex
|
<Flex
|
||||||
key={item._id}
|
key={item._id}
|
||||||
@@ -59,21 +136,14 @@ const SliderApps = ({
|
|||||||
{...(item._id === activeAppId
|
{...(item._id === activeAppId
|
||||||
? {
|
? {
|
||||||
bg: 'white',
|
bg: 'white',
|
||||||
boxShadow: 'md'
|
boxShadow: 'md',
|
||||||
|
color: 'primary.600'
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
_hover: {
|
_hover: {
|
||||||
bg: 'myGray.200'
|
bg: 'myGray.200'
|
||||||
},
|
},
|
||||||
onClick: () => {
|
onClick: () => onChangeApp(item._id)
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: '',
|
|
||||||
appId: item._id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Avatar src={item.avatar} w={'24px'} />
|
<Avatar src={item.avatar} w={'24px'} />
|
||||||
|
@@ -32,11 +32,12 @@ import ChatHeader from './components/ChatHeader';
|
|||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
|
||||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||||
|
import { getMyApps } from '@/web/core/app/api';
|
||||||
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
|
||||||
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -62,7 +63,6 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
setChatData,
|
setChatData,
|
||||||
delOneHistoryItem
|
delOneHistoryItem
|
||||||
} = useChatStore();
|
} = useChatStore();
|
||||||
const { myApps, loadMyApps } = useAppStore();
|
|
||||||
const { userInfo } = useUserStore();
|
const { userInfo } = useUserStore();
|
||||||
|
|
||||||
const { isPc } = useSystemStore();
|
const { isPc } = useSystemStore();
|
||||||
@@ -128,7 +128,12 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
||||||
);
|
);
|
||||||
|
|
||||||
useQuery(['loadModels'], () => loadMyApps());
|
const { data: myApps = [], runAsync: loadMyApps } = useRequest2(
|
||||||
|
() => getMyApps({ getRecentlyChat: true }),
|
||||||
|
{
|
||||||
|
manual: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// get chat app info
|
// get chat app info
|
||||||
const loadChatInfo = useCallback(
|
const loadChatInfo = useCallback(
|
||||||
@@ -272,7 +277,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
onClose={onCloseSlider}
|
onClose={onCloseSlider}
|
||||||
>
|
>
|
||||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||||
<DrawerContent maxWidth={'250px'}>{children}</DrawerContent>
|
<DrawerContent maxWidth={'75vw'}>{children}</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
})(
|
})(
|
||||||
|
@@ -274,7 +274,7 @@ const OutLink = () => {
|
|||||||
{/* pc show myself apps */}
|
{/* pc show myself apps */}
|
||||||
{isPc && (
|
{isPc && (
|
||||||
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||||
<SliderApps showExist={false} apps={myApps} activeAppId={appId} />
|
<SliderApps apps={myApps} activeAppId={appId} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
import { create } from 'zustand';
|
|
||||||
import { devtools } from 'zustand/middleware';
|
|
||||||
import { immer } from 'zustand/middleware/immer';
|
|
||||||
import { getMyApps } from '@/web/core/app/api';
|
|
||||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
|
||||||
|
|
||||||
export type State = {
|
|
||||||
myApps: AppListItemType[];
|
|
||||||
loadMyApps: (...arg: Parameters<typeof getMyApps>) => Promise<AppListItemType[]>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useAppStore = create<State>()(
|
|
||||||
devtools(
|
|
||||||
immer((set, get) => ({
|
|
||||||
myApps: [],
|
|
||||||
async loadMyApps(data) {
|
|
||||||
const res = await getMyApps(data);
|
|
||||||
set((state) => {
|
|
||||||
state.myApps = res;
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
);
|
|
@@ -6,7 +6,12 @@ import { HttpExceptionFilter } from './http-exception.filter';
|
|||||||
import { ResponseInterceptor } from './response';
|
import { ResponseInterceptor } from './response';
|
||||||
|
|
||||||
async function bootstrap(port: number) {
|
async function bootstrap(port: number) {
|
||||||
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());
|
const app = await NestFactory.create<NestFastifyApplication>(
|
||||||
|
AppModule,
|
||||||
|
new FastifyAdapter({
|
||||||
|
bodyLimit: 50 * 1048576 // 50MB
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// 使用全局异常过滤器
|
// 使用全局异常过滤器
|
||||||
app.useGlobalFilters(new HttpExceptionFilter());
|
app.useGlobalFilters(new HttpExceptionFilter());
|
||||||
|
Reference in New Issue
Block a user