mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
Fix share page whisper auth (#1161)
Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
### FastGPT V4.7
|
||||
### FastGPT V4.7.1
|
||||
|
||||
1. 新增 - 语音输入完整配置。支持选择是否打开语音输入(包括分享页面),支持语音输入后自动发送,支持语音输入后自动语音播放(流式)。
|
||||
2. 新增 - Pptx 和 xlsx 文件读取。但所有文件读取都放服务端,会消耗更多的服务器资源,以及无法在上传时预览更多内容。
|
||||
|
@@ -33,7 +33,7 @@ function embedChatbot() {
|
||||
ChatBtnDiv.draggable = false;
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.allow = 'fullscreen;microphone';
|
||||
iframe.allow = '*';
|
||||
iframe.referrerPolicy = 'no-referrer';
|
||||
iframe.title = 'FastGPT Chat Window';
|
||||
iframe.id = chatWindowId;
|
||||
|
@@ -230,7 +230,8 @@
|
||||
"Amount": "{{amount}}{{unit}}"
|
||||
},
|
||||
"speech": {
|
||||
"error tip": "Speech Failed"
|
||||
"error tip": "Speech Failed",
|
||||
"not support": "Your browser does not support voice input"
|
||||
},
|
||||
"system": {
|
||||
"Commercial version function": "Commercial version special function",
|
||||
@@ -250,7 +251,7 @@
|
||||
},
|
||||
"core": {
|
||||
"Chat": "Chat",
|
||||
"Chat test": "Chat test",
|
||||
"Chat test": "Test",
|
||||
"Max Token": "MaxTokens",
|
||||
"Start chat": "Start chat",
|
||||
"Total chars": "Total chars: {{total}}",
|
||||
@@ -423,6 +424,7 @@
|
||||
"Converting to text": "Converting to text...",
|
||||
"Custom History Title": "Custom history title",
|
||||
"Custom History Title Description": "If set to empty, chat history will be followed automatically.",
|
||||
"Debug test": "Test",
|
||||
"Exit Chat": "Exit",
|
||||
"Failed to initialize chat": "Failed to initialize chat",
|
||||
"Feedback Failed": "Feedback Failed",
|
||||
|
@@ -230,7 +230,8 @@
|
||||
"Amount": "{{amount}}{{unit}}"
|
||||
},
|
||||
"speech": {
|
||||
"error tip": "语音转文字失败"
|
||||
"error tip": "语音转文字失败",
|
||||
"not support": "您的浏览器不支持语音输入"
|
||||
},
|
||||
"system": {
|
||||
"Commercial version function": "商业版特有功能",
|
||||
@@ -250,7 +251,7 @@
|
||||
},
|
||||
"core": {
|
||||
"Chat": "对话",
|
||||
"Chat test": "测试对话",
|
||||
"Chat test": "测试",
|
||||
"Max Token": "单条数据上限",
|
||||
"Start chat": "立即对话",
|
||||
"Total chars": "总字数: {{total}}",
|
||||
@@ -423,6 +424,7 @@
|
||||
"Converting to text": "正在转换为文本...",
|
||||
"Custom History Title": "自定义历史记录标题",
|
||||
"Custom History Title Description": "如果设置为空,会自动跟随聊天记录。",
|
||||
"Debug test": "调试预览",
|
||||
"Exit Chat": "退出聊天",
|
||||
"Failed to initialize chat": "初始化聊天失败",
|
||||
"Feedback Failed": "提交反馈异常",
|
||||
@@ -608,8 +610,7 @@
|
||||
"success": "开始同步"
|
||||
}
|
||||
},
|
||||
"training": {
|
||||
}
|
||||
"training": {}
|
||||
},
|
||||
"data": {
|
||||
"Auxiliary Data": "辅助数据",
|
||||
@@ -996,6 +997,7 @@
|
||||
"Tool module": "工具",
|
||||
"UnKnow Module": "未知模块",
|
||||
"User guide": "用户引导",
|
||||
"App system setting": "系统配置",
|
||||
"http body placeholder": "与APIFox相同的语法",
|
||||
"textEditor": "文本加工",
|
||||
"textEditor intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。"
|
||||
|
@@ -94,7 +94,7 @@ const ChatItem = ({
|
||||
|
||||
/* AI */
|
||||
return (
|
||||
<Flex flexDirection={'column'} gap={2}>
|
||||
<Flex flexDirection={'column'} key={chat.dataId} gap={2}>
|
||||
{chat.value.map((value, i) => {
|
||||
const key = `${chat.dataId}-ai-${i}`;
|
||||
if (value.text) {
|
||||
|
@@ -8,6 +8,7 @@ import React, {
|
||||
useImperativeHandle,
|
||||
ForwardedRef
|
||||
} from 'react';
|
||||
import { SmallCloseIcon } from '@chakra-ui/icons';
|
||||
import { Box, Flex, IconButton } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
@@ -18,6 +19,7 @@ import type { ComponentRef, StartChatFnProps } from '@/components/ChatBox/type.d
|
||||
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { checkChatSupportSelectFileByModules } from '@/web/core/chat/utils';
|
||||
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export type ChatTestComponentRef = {
|
||||
resetChatTest: () => void;
|
||||
@@ -35,6 +37,7 @@ const ChatTest = (
|
||||
},
|
||||
ref: ForwardedRef<ChatTestComponentRef>
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||
const { userInfo } = useUserStore();
|
||||
const isOpen = useMemo(() => modules && modules.length > 0, [modules]);
|
||||
@@ -100,9 +103,9 @@ const ChatTest = (
|
||||
>
|
||||
<Flex py={4} px={5} whiteSpace={'nowrap'}>
|
||||
<Box fontSize={'xl'} fontWeight={'bold'} flex={1}>
|
||||
调试预览
|
||||
{t('core.chat.Debug test')}
|
||||
</Box>
|
||||
<MyTooltip label={'重置'}>
|
||||
<MyTooltip label={t('core.chat.Restart')}>
|
||||
<IconButton
|
||||
className="chat"
|
||||
size={'smSquare'}
|
||||
@@ -117,6 +120,16 @@ const ChatTest = (
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyTooltip label={t('common.Close')}>
|
||||
<IconButton
|
||||
ml={[3, 6]}
|
||||
icon={<SmallCloseIcon fontSize={'22px'} />}
|
||||
variant={'grayBase'}
|
||||
size={'smSquare'}
|
||||
aria-label={''}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<Box flex={1}>
|
||||
<ChatBox
|
||||
@@ -132,7 +145,7 @@ const ChatTest = (
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box
|
||||
{/* <Box
|
||||
zIndex={2}
|
||||
display={isOpen ? 'block' : 'none'}
|
||||
position={'fixed'}
|
||||
@@ -141,7 +154,7 @@ const ChatTest = (
|
||||
bottom={0}
|
||||
right={0}
|
||||
onClick={onClose}
|
||||
/>
|
||||
/> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -166,7 +166,7 @@ export const FlowProvider = ({
|
||||
}, [nodes]);
|
||||
|
||||
const onFixView = useCallback(() => {
|
||||
const btn = document.querySelector('.react-flow__controls-fitview') as HTMLButtonElement;
|
||||
const btn = document.querySelector('.custom-workflow-fix_view') as HTMLButtonElement;
|
||||
|
||||
setTimeout(() => {
|
||||
btn && btn.click();
|
||||
|
@@ -15,8 +15,10 @@ import NodeCard from '../render/NodeCard';
|
||||
import type { VariableItemType } from '@fastgpt/global/core/app/type.d';
|
||||
import QGSwitch from '@/components/core/app/QGSwitch';
|
||||
import TTSSelect from '@/components/core/app/TTSSelect';
|
||||
import WhisperConfig from '@/components/core/app/WhisperConfig';
|
||||
import { splitGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { TTSTypeEnum } from '@/constants/app';
|
||||
|
||||
const NodeUserGuide = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
|
||||
const theme = useTheme();
|
||||
@@ -31,6 +33,9 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
|
||||
<Box pt={3} borderTop={theme.borders.base}>
|
||||
<TTSGuide data={data} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={theme.borders.base}>
|
||||
<WhisperGuide data={data} />
|
||||
</Box>
|
||||
<Box mt={3} pt={3} borderTop={theme.borders.base}>
|
||||
<QuestionGuide data={data} />
|
||||
</Box>
|
||||
@@ -164,3 +169,26 @@ function TTSGuide({ data }: { data: FlowModuleItemType }) {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function WhisperGuide({ data }: { data: FlowModuleItemType }) {
|
||||
const { inputs, moduleId } = data;
|
||||
const { ttsConfig, whisperConfig } = splitGuideModule({ inputs } as ModuleItemType);
|
||||
|
||||
return (
|
||||
<WhisperConfig
|
||||
isOpenAudio={ttsConfig.type !== TTSTypeEnum.none}
|
||||
value={whisperConfig}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
key: ModuleInputKeyEnum.whisper,
|
||||
type: 'updateInput',
|
||||
value: {
|
||||
...inputs.find((item) => item.key === ModuleInputKeyEnum.whisper),
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -261,7 +261,7 @@ const NodeCard = (props: Props) => {
|
||||
}}
|
||||
>
|
||||
{Header}
|
||||
{children}
|
||||
<Box className="nowheel">{children}</Box>
|
||||
{RenderModal}
|
||||
</Box>
|
||||
);
|
||||
|
@@ -3,8 +3,11 @@ import ReactFlow, {
|
||||
Background,
|
||||
Connection,
|
||||
Controls,
|
||||
ControlButton,
|
||||
MiniMap,
|
||||
NodeProps,
|
||||
ReactFlowProvider
|
||||
ReactFlowProvider,
|
||||
useReactFlow
|
||||
} from 'reactflow';
|
||||
import { Box, Flex, IconButton, useDisclosure } from '@chakra-ui/react';
|
||||
import { SmallCloseIcon } from '@chakra-ui/icons';
|
||||
@@ -20,6 +23,8 @@ import 'reactflow/dist/style.css';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { FlowModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
|
||||
const NodeSimple = dynamic(() => import('./components/nodes/NodeSimple'));
|
||||
const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
|
||||
@@ -54,19 +59,10 @@ const edgeTypes = {
|
||||
const Container = React.memo(function Container() {
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { reactFlowWrapper, nodes, onNodesChange, edges, onEdgesChange, onConnect } =
|
||||
useFlowProviderStore();
|
||||
|
||||
const memoRenderTools = useMemo(
|
||||
() => (
|
||||
<>
|
||||
<Background />
|
||||
<Controls position={'bottom-right'} style={{ display: 'flex' }} showInteractive={false} />
|
||||
</>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const customOnConnect = useCallback(
|
||||
(connect: Connection) => {
|
||||
if (!connect.sourceHandle || !connect.targetHandle) {
|
||||
@@ -105,7 +101,7 @@ const Container = React.memo(function Container() {
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={customOnConnect}
|
||||
>
|
||||
{memoRenderTools}
|
||||
<FlowController />
|
||||
</ReactFlow>
|
||||
);
|
||||
});
|
||||
@@ -168,3 +164,40 @@ const Flow = ({ Header, ...data }: { Header: React.ReactNode }) => {
|
||||
};
|
||||
|
||||
export default React.memo(Flow);
|
||||
|
||||
const FlowController = React.memo(function FlowController() {
|
||||
const { fitView } = useReactFlow();
|
||||
return (
|
||||
<>
|
||||
<MiniMap
|
||||
style={{
|
||||
height: 78,
|
||||
width: 126,
|
||||
marginBottom: 35
|
||||
}}
|
||||
pannable
|
||||
/>
|
||||
<Controls
|
||||
position={'bottom-right'}
|
||||
style={{
|
||||
display: 'flex',
|
||||
marginBottom: 5,
|
||||
background: 'white',
|
||||
borderRadius: '6px',
|
||||
overflow: 'hidden',
|
||||
boxShadow:
|
||||
'0px 0px 1px 0px rgba(19, 51, 107, 0.20), 0px 12px 16px -4px rgba(19, 51, 107, 0.20)'
|
||||
}}
|
||||
showInteractive={false}
|
||||
showFitView={false}
|
||||
>
|
||||
<MyTooltip label={'页面居中'}>
|
||||
<ControlButton className="custom-workflow-fix_view" onClick={() => fitView()}>
|
||||
<MyIcon name={'core/modules/fixview'} w={'14px'} />
|
||||
</ControlButton>
|
||||
</MyTooltip>
|
||||
</Controls>
|
||||
<Background />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
@@ -18,7 +18,8 @@ import { getDocPath } from '@/web/common/system/doc';
|
||||
const LafAccountModal = ({
|
||||
defaultData = {
|
||||
token: '',
|
||||
appid: ''
|
||||
appid: '',
|
||||
pat: ''
|
||||
},
|
||||
onClose
|
||||
}: {
|
||||
@@ -140,7 +141,7 @@ const LafAccountModal = ({
|
||||
onResetForm();
|
||||
putUpdateTeam({
|
||||
teamId: userInfo?.team.teamId || '',
|
||||
lafAccount: { token: '', appid: '' }
|
||||
lafAccount: { token: '', appid: '', pat: '' }
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
29
projects/app/src/pages/api/admin/initv471.ts
Normal file
29
projects/app/src/pages/api/admin/initv471.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { PgClient } from '@fastgpt/service/common/vectorStore/pg';
|
||||
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authCert({ req, authRoot: true });
|
||||
|
||||
// 删除索引
|
||||
await PgClient.query(`DROP INDEX IF EXISTS team_dataset_index;`);
|
||||
await PgClient.query(`DROP INDEX IF EXISTS team_collection_index;`);
|
||||
await PgClient.query(`DROP INDEX IF EXISTS team_id_index;`);
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
@@ -29,7 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
teamId,
|
||||
datasetId: collection.datasetId._id,
|
||||
collectionId,
|
||||
fields: '_id teamId fileId metadata'
|
||||
fields: '_id teamId datasetId fileId metadata'
|
||||
});
|
||||
|
||||
// delete
|
||||
|
@@ -70,6 +70,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
// Duplicate data check
|
||||
await hasSameValue({
|
||||
teamId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
q: formatQ,
|
||||
a: formatA
|
||||
|
@@ -6,6 +6,7 @@ import type { GetDatasetDataListProps } from '@/global/core/api/datasetReq';
|
||||
import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { PagingData } from '@/types';
|
||||
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -28,7 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
per: 'r'
|
||||
});
|
||||
|
||||
searchText = searchText.replace(/'/g, '');
|
||||
searchText = replaceRegChars(searchText).replace(/'/g, '');
|
||||
|
||||
const match = {
|
||||
teamId,
|
||||
|
@@ -9,6 +9,7 @@ import { pushWhisperUsage } from '@/service/support/wallet/usage/push';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
|
||||
const upload = getUploadModel({
|
||||
maxSize: 2
|
||||
@@ -20,15 +21,16 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
try {
|
||||
const {
|
||||
file,
|
||||
data: { appId, duration, teamId: spaceTeamId, teamToken }
|
||||
} = await upload.doUpload<{
|
||||
appId: string;
|
||||
duration: number;
|
||||
shareId?: string;
|
||||
teamId?: string;
|
||||
teamToken?: string;
|
||||
}>(req, res);
|
||||
data: { appId, duration, shareId, outLinkUid, teamId: spaceTeamId, teamToken }
|
||||
} = await upload.doUpload<
|
||||
OutLinkChatAuthProps & {
|
||||
appId: string;
|
||||
duration: number;
|
||||
}
|
||||
>(req, res);
|
||||
|
||||
req.body.shareId = shareId;
|
||||
req.body.outLinkUid = outLinkUid;
|
||||
req.body.teamId = spaceTeamId;
|
||||
req.body.teamToken = teamToken;
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react';
|
||||
import { SmallCloseIcon } from '@chakra-ui/icons';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure, Button } from '@chakra-ui/react';
|
||||
import { ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||
@@ -18,6 +17,7 @@ import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import MyMenu from '@/components/MyMenu';
|
||||
|
||||
const ImportSettings = dynamic(() => import('@/components/core/module/Flow/ImportSettings'));
|
||||
|
||||
@@ -143,73 +143,61 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
{app.name}
|
||||
</Box>
|
||||
|
||||
<MyTooltip label={t('app.Import Configs')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
size={'smSquare'}
|
||||
icon={<MyIcon name={'common/importLight'} w={['14px', '16px']} />}
|
||||
variant={'whitePrimary'}
|
||||
aria-label={'save'}
|
||||
onClick={onOpenImport}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyTooltip label={t('app.Export Configs')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'export'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
variant={'whitePrimary'}
|
||||
aria-label={'save'}
|
||||
onClick={async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
copyData(filterExportModules(modules), t('app.Export Config Successful'));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
|
||||
{testModules ? (
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<SmallCloseIcon fontSize={'25px'} />}
|
||||
variant={'whitePrimary'}
|
||||
size={'smSquare'}
|
||||
aria-label={''}
|
||||
onClick={() => setTestModules(undefined)}
|
||||
/>
|
||||
) : (
|
||||
<MyTooltip label={t('core.Chat test')}>
|
||||
<MyMenu
|
||||
Button={
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'core/chat/chatLight'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
aria-label={'save'}
|
||||
mr={[3, 5]}
|
||||
icon={<MyIcon name={'more'} w={'14px'} p={2} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
variant={'whitePrimary'}
|
||||
onClick={async () => {
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{ label: t('app.Import Configs'), icon: 'common/importLight', onClick: onOpenImport },
|
||||
{
|
||||
label: t('app.Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
setTestModules(modules);
|
||||
copyData(filterExportModules(modules), t('app.Export Config Successful'));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
)}
|
||||
}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<MyTooltip label={t('common.Save')}>
|
||||
<IconButton
|
||||
icon={<MyIcon name={'common/saveFill'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
isLoading={isSaving}
|
||||
aria-label={'save'}
|
||||
{!testModules && (
|
||||
<Button
|
||||
mr={[3, 5]}
|
||||
size={'sm'}
|
||||
leftIcon={<MyIcon name={'core/chat/chatLight'} w={['14px', '16px']} />}
|
||||
variant={'whitePrimary'}
|
||||
onClick={async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
onclickSave(modules);
|
||||
setTestModules(modules);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
>
|
||||
{t('core.Chat test')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
size={'sm'}
|
||||
isLoading={isSaving}
|
||||
leftIcon={<MyIcon name={'common/saveFill'} w={['14px', '16px']} />}
|
||||
onClick={async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
onclickSave(modules);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('common.Save')}
|
||||
</Button>
|
||||
</Flex>
|
||||
{isOpenImport && <ImportSettings onClose={onCloseImport} />}
|
||||
<ConfirmModal
|
||||
|
@@ -88,7 +88,7 @@ const SelectUsingWayModal = ({ share, onClose }: { share: OutLinkSchema; onClose
|
||||
src="${linkUrl}"
|
||||
style="width: 100%; height: 100%;"
|
||||
frameborder="0"
|
||||
allow="microphone"
|
||||
allow="*"
|
||||
/>`
|
||||
},
|
||||
[UsingWayEnum.script]: {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useChatBox } from '@/components/ChatBox/hooks/useChatBox';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { Menu, MenuButton, MenuList, MenuItem, Box, IconButton } from '@chakra-ui/react';
|
||||
import { Box, IconButton } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useRouter } from 'next/router';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure, Button } from '@chakra-ui/react';
|
||||
import { PluginItemSchema } from '@fastgpt/global/core/plugin/type';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
@@ -14,6 +14,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import MyMenu from '@/components/MyMenu';
|
||||
|
||||
const ImportSettings = dynamic(() => import('@/components/core/module/Flow/ImportSettings'));
|
||||
const PreviewPlugin = dynamic(() => import('./Preview'));
|
||||
@@ -137,38 +138,37 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box ml={[3, 6]} fontSize={['md', '2xl']} flex={1}>
|
||||
<Box ml={[3, 5]} fontSize={['md', '2xl']} flex={1}>
|
||||
{plugin.name}
|
||||
</Box>
|
||||
|
||||
<MyTooltip label={t('app.Import Configs')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'common/importLight'} w={['14px', '16px']} />}
|
||||
variant={'whitePrimary'}
|
||||
size={'smSquare'}
|
||||
aria-label={'save'}
|
||||
onClick={onOpenImport}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyTooltip label={t('app.Export Configs')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'export'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
variant={'whitePrimary'}
|
||||
aria-label={'save'}
|
||||
onClick={async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
copyData(filterExportModules(modules), t('app.Export Config Successful'));
|
||||
<MyMenu
|
||||
Button={
|
||||
<IconButton
|
||||
mr={[3, 5]}
|
||||
icon={<MyIcon name={'more'} w={'14px'} p={2} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
variant={'whitePrimary'}
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{ label: t('app.Import Configs'), icon: 'common/importLight', onClick: onOpenImport },
|
||||
{
|
||||
label: t('app.Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
copyData(filterExportModules(modules), t('app.Export Config Successful'));
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<MyTooltip label={t('module.Preview Plugin')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
mr={[3, 5]}
|
||||
icon={<MyIcon name={'core/modules/previewLight'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
aria-label={'save'}
|
||||
@@ -181,20 +181,19 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyTooltip label={t('module.Save Config')}>
|
||||
<IconButton
|
||||
icon={<MyIcon name={'save'} w={['14px', '16px']} />}
|
||||
size={'smSquare'}
|
||||
isLoading={isLoading}
|
||||
aria-label={'save'}
|
||||
onClick={async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
onclickSave(modules);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Button
|
||||
size={'sm'}
|
||||
isLoading={isLoading}
|
||||
leftIcon={<MyIcon name={'common/saveFill'} w={['14px', '16px']} />}
|
||||
onClick={async () => {
|
||||
const modules = await flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
onclickSave(modules);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('common.Save')}
|
||||
</Button>
|
||||
</Flex>
|
||||
{isOpenImport && <ImportSettings onClose={onCloseImport} />}
|
||||
{!!previewModules && (
|
||||
|
@@ -2,6 +2,7 @@ import {
|
||||
delFileByFileIdList,
|
||||
getGFSCollection
|
||||
} from '@fastgpt/service/common/file/gridfs/controller';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import {
|
||||
deleteDatasetDataVector,
|
||||
@@ -72,15 +73,19 @@ export async function checkInvalidDatasetData(start: Date, end: Date) {
|
||||
$lte: end
|
||||
}
|
||||
},
|
||||
'_id teamId collectionId'
|
||||
'_id teamId datasetId collectionId'
|
||||
).lean();
|
||||
|
||||
// 2. 合并所有的collectionId
|
||||
const map = new Map<string, { teamId: string; collectionId: string }>();
|
||||
const map = new Map<string, { teamId: string; datasetId: string; collectionId: string }>();
|
||||
for (const item of rows) {
|
||||
const collectionId = String(item.collectionId);
|
||||
if (!map.has(collectionId)) {
|
||||
map.set(collectionId, { teamId: item.teamId, collectionId });
|
||||
map.set(collectionId, {
|
||||
teamId: item.teamId,
|
||||
datasetId: item.datasetId,
|
||||
collectionId
|
||||
});
|
||||
}
|
||||
}
|
||||
const list = Array.from(map.values());
|
||||
@@ -92,21 +97,28 @@ export async function checkInvalidDatasetData(start: Date, end: Date) {
|
||||
// 3. 查看该collection是否存在,不存在,则删除对应的数据
|
||||
const collection = await MongoDatasetCollection.findOne({ _id: item.collectionId });
|
||||
if (!collection) {
|
||||
const result = await Promise.all([
|
||||
MongoDatasetTraining.deleteMany({
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoDatasetTraining.deleteMany(
|
||||
{
|
||||
teamId: item.teamId,
|
||||
collectionId: item.collectionId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
await MongoDatasetData.deleteMany(
|
||||
{
|
||||
teamId: item.teamId,
|
||||
collectionId: item.collectionId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
await deleteDatasetDataVector({
|
||||
teamId: item.teamId,
|
||||
collectionId: item.collectionId
|
||||
}),
|
||||
MongoDatasetData.deleteMany({
|
||||
teamId: item.teamId,
|
||||
collectionId: item.collectionId
|
||||
}),
|
||||
deleteDatasetDataVector({
|
||||
teamId: item.teamId,
|
||||
collectionIds: [String(item.collectionId)]
|
||||
})
|
||||
]);
|
||||
console.log(result);
|
||||
datasetIds: [item.datasetId],
|
||||
collectionIds: [item.collectionId]
|
||||
});
|
||||
});
|
||||
|
||||
console.log('collection is not found', item);
|
||||
continue;
|
||||
}
|
||||
|
@@ -5,17 +5,20 @@ import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
*/
|
||||
export async function hasSameValue({
|
||||
teamId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
q,
|
||||
a = ''
|
||||
}: {
|
||||
teamId: string;
|
||||
datasetId: string;
|
||||
collectionId: string;
|
||||
q: string;
|
||||
a?: string;
|
||||
}) {
|
||||
const count = await MongoDatasetData.countDocuments({
|
||||
teamId,
|
||||
datasetId,
|
||||
collectionId,
|
||||
q,
|
||||
a
|
||||
|
@@ -5,6 +5,8 @@ import axios, {
|
||||
AxiosProgressEvent
|
||||
} from 'axios';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { putUpdateTeam } from '@/web/support/user/team/api';
|
||||
import { LafAccountType } from '@fastgpt/global/support/user/team/type';
|
||||
|
||||
interface ConfigType {
|
||||
headers?: { [key: string]: string };
|
||||
@@ -72,21 +74,33 @@ function responseSuccess(response: AxiosResponse<ResponseDataType>) {
|
||||
/**
|
||||
* 响应数据检查
|
||||
*/
|
||||
function checkRes(data: ResponseDataType) {
|
||||
if (data === undefined) {
|
||||
console.log('error->', data, 'data is empty');
|
||||
function checkRes(
|
||||
res: ResponseDataType,
|
||||
url: string,
|
||||
data: any,
|
||||
requestConfig: ConfigType,
|
||||
method: Method
|
||||
) {
|
||||
if (res === undefined) {
|
||||
console.log('error->', res, 'res is empty');
|
||||
return Promise.reject('服务器异常');
|
||||
} else if (data.error) {
|
||||
return responseError(data.error);
|
||||
} else if (res.error) {
|
||||
return responseError(data.error, url, data, requestConfig, method);
|
||||
}
|
||||
|
||||
return data.data;
|
||||
return res.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应错误
|
||||
*/
|
||||
function responseError(err: any) {
|
||||
function responseError(
|
||||
err: any,
|
||||
url: string,
|
||||
data: any,
|
||||
requestConfig: ConfigType,
|
||||
method: Method
|
||||
) {
|
||||
console.log('error->', '请求错误', err);
|
||||
|
||||
if (!err) {
|
||||
@@ -97,6 +111,25 @@ function responseError(err: any) {
|
||||
}
|
||||
|
||||
if (err?.response?.data) {
|
||||
const code = err?.response?.data?.statusCode;
|
||||
if (code === 401) {
|
||||
return POST<string>(`/v1/auth/pat2token`, {
|
||||
pat: useUserStore.getState().userInfo?.team?.lafAccount?.pat
|
||||
})
|
||||
.then((res) => {
|
||||
putUpdateTeam({
|
||||
teamId: useUserStore.getState().userInfo?.team.teamId || '',
|
||||
lafAccount: {
|
||||
...useUserStore.getState().userInfo?.team?.lafAccount,
|
||||
token: res
|
||||
} as LafAccountType
|
||||
});
|
||||
return request(url, data, requestConfig, method);
|
||||
})
|
||||
.catch((err) => {
|
||||
return Promise.reject({ message: '登录凭证过期' });
|
||||
});
|
||||
}
|
||||
return Promise.reject(err?.response?.data);
|
||||
}
|
||||
return Promise.reject(err);
|
||||
@@ -115,12 +148,9 @@ instance.interceptors.request.use(startInterceptors, (err) => Promise.reject(err
|
||||
/* 响应拦截 */
|
||||
instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err));
|
||||
|
||||
function request(
|
||||
url: string,
|
||||
data: any,
|
||||
{ cancelToken, maxQuantity, ...config }: ConfigType,
|
||||
method: Method
|
||||
): any {
|
||||
function request(url: string, data: any, requestConfig: ConfigType, method: Method): any {
|
||||
const { cancelToken, maxQuantity, ...config } = requestConfig;
|
||||
|
||||
/* 去空 */
|
||||
for (const key in data) {
|
||||
if (data[key] === null || data[key] === undefined) {
|
||||
@@ -140,8 +170,8 @@ function request(
|
||||
signal: cancelToken?.signal,
|
||||
...config // 用户自定义配置,可以覆盖前面的配置
|
||||
})
|
||||
.then((res) => checkRes(res.data))
|
||||
.catch((err) => responseError(err))
|
||||
.then((res) => checkRes(res.data, url, data, requestConfig, method))
|
||||
.catch((err) => responseError(err, url, data, requestConfig, method))
|
||||
.finally(() => requestFinish({ url }));
|
||||
}
|
||||
|
||||
|
@@ -51,6 +51,12 @@ export const useSpeech = (props?: OutLinkChatAuthProps & { appId?: string }) =>
|
||||
}, []);
|
||||
|
||||
const startSpeak = async (onFinish: (text: string) => void) => {
|
||||
if (!navigator.mediaDevices.getUserMedia) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('common.speech.not support')
|
||||
});
|
||||
}
|
||||
try {
|
||||
cancelWhisperSignal.current = false;
|
||||
|
||||
|
@@ -17,7 +17,7 @@ export const appTemplates: (AppItemType & {
|
||||
modules: [
|
||||
{
|
||||
moduleId: 'userGuide',
|
||||
name: 'core.module.template.User guide',
|
||||
name: 'core.module.template.App system setting',
|
||||
avatar: '/imgs/module/userGuide.png',
|
||||
flowType: 'userGuide',
|
||||
position: {
|
||||
@@ -300,7 +300,7 @@ export const appTemplates: (AppItemType & {
|
||||
modules: [
|
||||
{
|
||||
moduleId: 'userGuide',
|
||||
name: 'core.module.template.User guide',
|
||||
name: 'core.module.template.App system setting',
|
||||
avatar: '/imgs/module/userGuide.png',
|
||||
flowType: 'userGuide',
|
||||
position: {
|
||||
@@ -616,7 +616,7 @@ export const appTemplates: (AppItemType & {
|
||||
modules: [
|
||||
{
|
||||
moduleId: 'userGuide',
|
||||
name: 'core.module.template.User guide',
|
||||
name: 'core.module.template.App system setting',
|
||||
intro: 'core.app.tip.userGuideTip',
|
||||
avatar: '/imgs/module/userGuide.png',
|
||||
flowType: 'userGuide',
|
||||
@@ -1651,7 +1651,7 @@ export const appTemplates: (AppItemType & {
|
||||
},
|
||||
{
|
||||
moduleId: 'q9equb',
|
||||
name: 'core.module.template.User guide',
|
||||
name: 'core.module.template.App system setting',
|
||||
flowType: 'userGuide',
|
||||
position: {
|
||||
x: -272.66416216517086,
|
||||
|
@@ -14,7 +14,7 @@ export async function postForm2Modules(data: AppSimpleEditFormType) {
|
||||
function userGuideTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
||||
return [
|
||||
{
|
||||
name: 'core.module.template.User guide',
|
||||
name: '系统配置',
|
||||
flowType: FlowNodeTypeEnum.userGuide,
|
||||
inputs: [
|
||||
{
|
||||
|
@@ -1,6 +1,4 @@
|
||||
.react-flow__panel {
|
||||
display: none;
|
||||
}
|
||||
.react-flow__panel.react-flow__attribution {
|
||||
display: none !important;
|
||||
z-index: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { GET, POST, PUT } from '@/web/common/api/lafRequest';
|
||||
import { GET, POST } from '@/web/common/api/lafRequest';
|
||||
|
||||
export const postLafPat2Token = (pat: string) => POST<string>(`/v1/auth/pat2token`, { pat });
|
||||
|
||||
|
Reference in New Issue
Block a user