From 896a3f1472eb415589ede4688dcb84814c4590ad Mon Sep 17 00:00:00 2001 From: heheer Date: Mon, 10 Feb 2025 15:20:49 +0800 Subject: [PATCH] add plugin unexist error tips (#3717) * add plugin unexist error tips * throw error when run plugin * check workflow * plugin data avoid request twice * auth owner tmbId * fix --- packages/global/core/app/utils.ts | 20 ++- packages/global/core/workflow/type/node.d.ts | 11 ++ .../common/Icon/icons/common/errorFill.svg | 11 +- packages/web/i18n/en/app.json | 4 + packages/web/i18n/en/common.json | 3 +- packages/web/i18n/zh-CN/app.json | 4 + packages/web/i18n/zh-CN/common.json | 3 +- packages/web/i18n/zh-Hant/app.json | 4 + packages/web/i18n/zh-Hant/common.json | 3 +- .../app/detail/Plugin/Header.tsx | 2 +- .../app/detail/SimpleApp/Header.tsx | 28 +++- .../SimpleApp/components/ToolSelect.tsx | 130 +++++++++++------- .../app/detail/Workflow/Header.tsx | 2 +- .../detail/Workflow/components/SaveButton.tsx | 9 +- .../Flow/nodes/render/NodeCard.tsx | 35 ++++- .../src/pageComponents/app/detail/context.tsx | 23 +++- projects/app/src/pages/api/core/app/detail.ts | 17 ++- .../src/pages/api/core/app/version/detail.ts | 6 +- projects/app/src/pages/api/core/chat/init.ts | 1 - .../src/pages/api/core/chat/outLink/init.ts | 2 - .../app/src/pages/api/v1/chat/completions.ts | 1 - projects/app/src/service/core/app/utils.ts | 51 +++++++ projects/app/src/web/core/app/utils.ts | 1 + projects/app/src/web/core/workflow/utils.ts | 4 + 24 files changed, 284 insertions(+), 91 deletions(-) diff --git a/packages/global/core/app/utils.ts b/packages/global/core/app/utils.ts index 708964b38..e6ef98a68 100644 --- a/packages/global/core/app/utils.ts +++ b/packages/global/core/app/utils.ts @@ -7,6 +7,8 @@ import { StoreNodeItemType } from '../workflow/type/node'; import { DatasetSearchModeEnum } from '../dataset/constants'; import { WorkflowTemplateBasicType } from '../workflow/type'; import { AppTypeEnum } from './constants'; +import { AppErrEnum } from '../../common/error/code/app'; +import { PluginErrEnum } from '../../common/error/code/plugin'; export const getDefaultAppForm = (): AppSimpleEditFormType => { return { @@ -117,7 +119,8 @@ export const appWorkflow2Form = ({ version: node.version, inputs: node.inputs, outputs: node.outputs, - templateType: FlowNodeTemplateTypeEnum.other + templateType: FlowNodeTemplateTypeEnum.other, + pluginData: node.pluginData }); } else if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) { defaultAppForm.chatConfig = getAppChatConfig({ @@ -147,3 +150,18 @@ export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFor } return ''; }; + +export const checkAppUnExistError = (error?: string) => { + const unExistError: Array = [ + AppErrEnum.unAuthApp, + AppErrEnum.unExist, + PluginErrEnum.unAuth, + PluginErrEnum.unExist + ]; + + if (!!error && unExistError.includes(error)) { + return error; + } else { + return undefined; + } +}; diff --git a/packages/global/core/workflow/type/node.d.ts b/packages/global/core/workflow/type/node.d.ts index 691fc96c9..deb5b24f2 100644 --- a/packages/global/core/workflow/type/node.d.ts +++ b/packages/global/core/workflow/type/node.d.ts @@ -43,6 +43,17 @@ export type FlowNodeCommonType = { pluginId?: string; isFolder?: boolean; // pluginType?: AppTypeEnum; + pluginData?: PluginDataType; +}; + +export type PluginDataType = { + version: string; + diagram?: string; + userGuide?: string; + courseUrl?: string; + name?: string; + avatar?: string; + error?: string; }; type HandleType = { diff --git a/packages/web/components/common/Icon/icons/common/errorFill.svg b/packages/web/components/common/Icon/icons/common/errorFill.svg index c73b125d9..e09539427 100644 --- a/packages/web/components/common/Icon/icons/common/errorFill.svg +++ b/packages/web/components/common/Icon/icons/common/errorFill.svg @@ -1,6 +1,5 @@ - - - \ No newline at end of file + + + + + diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index e70ae1170..cacd5aba8 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -7,8 +7,12 @@ "ai_settings": "AI Configuration", "all_apps": "All Applications", "app.Version name": "Version Name", + "app.error.publish_unExist_app": "Release failed, please check whether the tool call is normal", + "app.error.unExist_app": "Some components are missing, please delete them", "app.modules.click to update": "Click to Refresh", "app.modules.has new version": "New Version Available", + "app.modules.not_found": "Not Found", + "app.modules.not_found_tips": "This component cannot be found in the system, please delete it, otherwise the process will not run normally", "app.version_current": "Current Version", "app.version_initial": "Initial Version", "app.version_name_tips": "Version name cannot be empty", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 5b1fbbd32..eb79c9104 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -448,6 +448,7 @@ "core.chat.markdown.Edit Question": "Edit Question", "core.chat.markdown.Quick Question": "Click to Ask Immediately", "core.chat.markdown.Send Question": "Send Question", + "core.chat.module_unexist": "Running failed: Application missing components", "core.chat.quote.Quote Tip": "Only the actual quoted content is displayed here. If the data is updated, it will not be updated in real-time here.", "core.chat.quote.Read Quote": "View Quote", "core.chat.response.Complete Response": "Complete Response", @@ -787,7 +788,7 @@ "core.view_chat_detail": "View Chat Details", "core.workflow.Can not delete node": "This Node Cannot Be Deleted", "core.workflow.Change input type tip": "Changing the input type will clear the filled values, please confirm!", - "core.workflow.Check Failed": "Workflow Validation Failed, Please Check If the Nodes Are Correctly Filled and the Connections Are Normal", + "core.workflow.Check Failed": "Workflow verification failed, please check whether the value is missing, and whether the connection is normal.", "core.workflow.Confirm stop debug": "Confirm to Stop Debugging? Debug Information Will Not Be Retained.", "core.workflow.Copy node": "Node Copied", "core.workflow.Custom inputs": "Custom Inputs", diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index b1f41b3aa..f11584642 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -7,8 +7,12 @@ "ai_settings": "AI 配置", "all_apps": "全部应用", "app.Version name": "版本名称", + "app.error.publish_unExist_app": "发布失败,请检查工具调用是否正常", + "app.error.unExist_app": "部分组件缺失,请删除", "app.modules.click to update": "点击更新", "app.modules.has new version": "有新版本", + "app.modules.not_found": "组件缺失", + "app.modules.not_found_tips": "系统内无法查找到该组件,请删除,否则流程无法正常运行", "app.version_current": "当前版本", "app.version_initial": "初始版本", "app.version_name_tips": "版本名称不能为空", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index c79e0fa0f..3b187d741 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -451,6 +451,7 @@ "core.chat.markdown.Edit Question": "编辑问题", "core.chat.markdown.Quick Question": "点我立即提问", "core.chat.markdown.Send Question": "发送问题", + "core.chat.module_unexist": "运行失败:应用缺失组件", "core.chat.quote.Quote Tip": "此处仅显示实际引用内容,若数据有更新,此处不会实时更新", "core.chat.quote.Read Quote": "查看引用", "core.chat.response.Complete Response": "完整响应", @@ -790,7 +791,7 @@ "core.view_chat_detail": "查看对话详情", "core.workflow.Can not delete node": "该节点不允许删除", "core.workflow.Change input type tip": "修改输入类型会清空已填写的值,请确认!", - "core.workflow.Check Failed": "工作流校验失败,请检查节点是否正确填值,以及连线是否正常", + "core.workflow.Check Failed": "工作流校验失败,请检查是否缺失、缺值,连线是否正常", "core.workflow.Confirm stop debug": "确认终止调试?调试信息将会不保留。", "core.workflow.Copy node": "已复制节点", "core.workflow.Custom inputs": "自定义输入", diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index e5221b588..5e9abcfbb 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -7,8 +7,12 @@ "ai_settings": "AI 設定", "all_apps": "所有應用程式", "app.Version name": "版本名稱", + "app.error.publish_unExist_app": "發布失敗,請檢查工具調用是否正常", + "app.error.unExist_app": "部分組件缺失,請刪除", "app.modules.click to update": "點選更新", "app.modules.has new version": "有新版本", + "app.modules.not_found": "組件缺失", + "app.modules.not_found_tips": "系統內無法查找到該組件,請刪除,否則流程無法正常運行", "app.version_current": "目前版本", "app.version_initial": "初始版本", "app.version_name_tips": "版本名稱不能空白", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index bab85faa0..52a814e42 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -447,6 +447,7 @@ "core.chat.markdown.Edit Question": "編輯問題", "core.chat.markdown.Quick Question": "點我立即發問", "core.chat.markdown.Send Question": "傳送問題", + "core.chat.module_unexist": "運行失敗:應用缺失組件", "core.chat.quote.Quote Tip": "此處僅顯示實際引用內容,若資料有更新,此處不會即時更新", "core.chat.quote.Read Quote": "檢視引用", "core.chat.response.Complete Response": "完整回應", @@ -786,7 +787,7 @@ "core.view_chat_detail": "檢視對話詳細資料", "core.workflow.Can not delete node": "此節點不允許刪除", "core.workflow.Change input type tip": "修改輸入類型將清空已填寫的值,請確認!", - "core.workflow.Check Failed": "工作流程驗證失敗,請檢查節點是否正確填值,以及連線是否正常", + "core.workflow.Check Failed": "工作流校驗失敗,請檢查是否缺失、缺值,連線是否正常", "core.workflow.Confirm stop debug": "確認停止除錯?除錯資訊將不會保留。", "core.workflow.Copy node": "已複製節點", "core.workflow.Custom inputs": "自訂輸入", diff --git a/projects/app/src/pageComponents/app/detail/Plugin/Header.tsx b/projects/app/src/pageComponents/app/detail/Plugin/Header.tsx index 83ceef6fe..b9cc86898 100644 --- a/projects/app/src/pageComponents/app/detail/Plugin/Header.tsx +++ b/projects/app/src/pageComponents/app/detail/Plugin/Header.tsx @@ -196,7 +196,7 @@ const Header = () => { !!flowData2StoreDataAndCheck()} /> )} diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/Header.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/Header.tsx index c4681af46..f863f2c39 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/Header.tsx +++ b/projects/app/src/pageComponents/app/detail/SimpleApp/Header.tsx @@ -30,6 +30,12 @@ import PublishHistories from '../PublishHistoriesSlider'; import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload'; import { isProduction } from '@fastgpt/global/common/system/constants'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { + checkWorkflowNodeAndConnection, + storeEdgesRenderEdge, + storeNode2FlowNode +} from '@/web/core/workflow/utils'; const Header = ({ forbiddenSaveSnapshot, @@ -48,6 +54,7 @@ const Header = ({ }) => { const { t } = useTranslation(); const { isPc } = useSystem(); + const { toast } = useToast(); const router = useRouter(); const appId = useContextSelector(AppContext, (v) => v.appId); const onSaveApp = useContextSelector(AppContext, (v) => v.onSaveApp); @@ -231,7 +238,26 @@ const Header = ({ variant={'whitePrimary'} onClick={setIsShowHistories} /> - + { + const { nodes: storeNodes, edges: storeEdges } = form2AppWorkflow(appForm, t); + + const nodes = storeNodes.map((item) => storeNode2FlowNode({ item, t })); + const edges = storeEdges.map((item) => storeEdgesRenderEdge({ edge: item })); + + const checkResults = checkWorkflowNodeAndConnection({ nodes, edges }); + + if (checkResults) { + toast({ + title: t('app:app.error.publish_unExist_app'), + status: 'warning' + }); + } + return !checkResults; + }} + /> )} diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx index effd419c4..3ce6633c2 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx +++ b/projects/app/src/pageComponents/app/detail/SimpleApp/components/ToolSelect.tsx @@ -14,6 +14,7 @@ import Avatar from '@fastgpt/web/components/common/Avatar'; import ConfigToolModal from './ConfigToolModal'; import { getWebLLMModel } from '@/web/common/system/utils'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; +import { checkAppUnExistError } from '@fastgpt/global/core/app/utils'; const ToolSelect = ({ appForm, @@ -60,60 +61,83 @@ const ToolSelect = ({ gridTemplateColumns={'repeat(2, minmax(0, 1fr))'} gridGap={[2, 4]} > - {appForm.selectedTools.map((item) => ( - - { - if ( - item.inputs - .filter((input) => !childAppSystemKey.includes(input.key)) - .every( - (input) => - input.toolDescription || - input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) || - input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) - ) - ) { - return; - } - setConfigTool(item); - }} - > - - - {item.name} - - { - e.stopPropagation(); - setAppForm((state: AppSimpleEditFormType) => ({ - ...state, - selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id) - })); + {appForm.selectedTools.map((item) => { + const hasError = checkAppUnExistError(item.pluginData?.error); + + return ( + + - - - ))} + cursor={'pointer'} + onClick={() => { + if ( + item.inputs + .filter((input) => !childAppSystemKey.includes(input.key)) + .every( + (input) => + input.toolDescription || + input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) || + input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) + ) || + hasError + ) { + return; + } + setConfigTool(item); + }} + > + + + {item.name} + + {hasError && ( + + + + {t('app:app.modules.not_found')} + + + )} + { + e.stopPropagation(); + setAppForm((state: AppSimpleEditFormType) => ({ + ...state, + selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id) + })); + }} + /> + + + ); + })} {isOpenToolsSelect && ( diff --git a/projects/app/src/pageComponents/app/detail/Workflow/Header.tsx b/projects/app/src/pageComponents/app/detail/Workflow/Header.tsx index 3e1d7c9d8..0615b085f 100644 --- a/projects/app/src/pageComponents/app/detail/Workflow/Header.tsx +++ b/projects/app/src/pageComponents/app/detail/Workflow/Header.tsx @@ -200,7 +200,7 @@ const Header = () => { !!flowData2StoreDataAndCheck()} /> )} diff --git a/projects/app/src/pageComponents/app/detail/Workflow/components/SaveButton.tsx b/projects/app/src/pageComponents/app/detail/Workflow/components/SaveButton.tsx index 61b50c0e3..b03e63c58 100644 --- a/projects/app/src/pageComponents/app/detail/Workflow/components/SaveButton.tsx +++ b/projects/app/src/pageComponents/app/detail/Workflow/components/SaveButton.tsx @@ -6,8 +6,6 @@ import { useTranslation } from 'next-i18next'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { useToast } from '@fastgpt/web/hooks/useToast'; import SaveAndPublishModal from '../../WorkflowComponents/Flow/components/SaveAndPublish'; -import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; -import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; const SaveButton = ({ isLoading, @@ -16,12 +14,7 @@ const SaveButton = ({ }: { isLoading: boolean; onClickSave: (options: { isPublish?: boolean; versionName?: string }) => Promise; - checkData?: (hideTip?: boolean) => - | { - nodes: StoreNodeItemType[]; - edges: StoreEdgeItemType[]; - } - | undefined; + checkData?: () => boolean | undefined; }) => { const { t } = useTranslation(); const [isSave, setIsSave] = useState(false); diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx index 313f2c456..deb2d56d8 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Box, Button, Card, Flex, FlexProps } from '@chakra-ui/react'; import MyIcon from '@fastgpt/web/components/common/Icon'; import Avatar from '@fastgpt/web/components/common/Avatar'; @@ -99,14 +99,15 @@ const NodeCard = (props: Props) => { const { data: nodeTemplate } = useRequest2( async () => { + if (node?.pluginData?.error) { + return undefined; + } + if ( node?.flowNodeType === FlowNodeTypeEnum.pluginModule || node?.flowNodeType === FlowNodeTypeEnum.appModule ) { - if (!node?.pluginId) return; - const template = await getPreviewPluginNode({ appId: node.pluginId }); - - return template; + return { ...node, ...node.pluginData }; } else { const template = moduleTemplatesFlat.find( (item) => item.flowNodeType === node?.flowNodeType @@ -141,10 +142,13 @@ const NodeCard = (props: Props) => { const { runAsync: onClickSyncVersion } = useRequest2( async () => { - if (!!nodeTemplate) { + if (!node?.pluginId) return; + const template = await getPreviewPluginNode({ appId: node.pluginId }); + + if (!!template) { onResetNode({ id: nodeId, - node: nodeTemplate + node: template }); } onCloseConfirmSync(); @@ -286,6 +290,22 @@ const NodeCard = (props: Props) => { )} )} + {!!node?.pluginData?.error && ( + + + + {t('app:app.modules.not_found')} + + + )} @@ -297,6 +317,7 @@ const NodeCard = (props: Props) => { }, [ node?.flowNodeType, node?.courseUrl, + node?.pluginData?.error, showToolHandle, nodeId, isFolded, diff --git a/projects/app/src/pageComponents/app/detail/context.tsx b/projects/app/src/pageComponents/app/detail/context.tsx index 26ed53f30..797cbd5ae 100644 --- a/projects/app/src/pageComponents/app/detail/context.tsx +++ b/projects/app/src/pageComponents/app/detail/context.tsx @@ -1,4 +1,12 @@ -import { Dispatch, ReactNode, SetStateAction, useCallback, useMemo, useState } from 'react'; +import { + Dispatch, + ReactNode, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState +} from 'react'; import { createContext } from 'use-context-selector'; import { defaultApp } from '@/web/core/app/constants'; import { delAppById, getAppDetailById, putAppById } from '@/web/core/app/api'; @@ -14,6 +22,8 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { AppErrEnum } from '@fastgpt/global/common/error/code/app'; +import { checkAppUnExistError } from '@fastgpt/global/core/app/utils'; +import { useToast } from '@fastgpt/web/hooks/useToast'; const InfoModal = dynamic(() => import('./InfoModal')); const TagsEditModal = dynamic(() => import('./TagsEditModal')); @@ -84,6 +94,7 @@ export const AppContext = createContext({ const AppContextProvider = ({ children }: { children: ReactNode }) => { const { t } = useTranslation(); + const { toast } = useToast(); const router = useRouter(); const { appId, currentTab = TabEnum.appEdit } = router.query as { appId: string; @@ -194,6 +205,16 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => { [appDetail.name, deleteApp, openConfirmDel, t] ); + // check app unExist error + useEffect(() => { + if (appDetail.modules.some((module) => checkAppUnExistError(module.pluginData?.error))) { + toast({ + title: t('app:app.error.unExist_app'), + status: 'error' + }); + } + }, [appDetail.modules, t, toast]); + const contextValue: AppContextType = useMemo( () => ({ appId, diff --git a/projects/app/src/pages/api/core/app/detail.ts b/projects/app/src/pages/api/core/app/detail.ts index c9d3f0ac9..e57b5b4a5 100644 --- a/projects/app/src/pages/api/core/app/detail.ts +++ b/projects/app/src/pages/api/core/app/detail.ts @@ -3,8 +3,9 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { NextAPI } from '@/service/middleware/entry'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; +import { checkNode } from '@/service/core/app/utils'; -/* 获取我的模型 */ +/* 获取应用详情 */ async function handler(req: NextApiRequest, res: NextApiResponse) { const { appId } = req.query as { appId: string }; @@ -15,11 +16,19 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const { app } = await authApp({ req, authToken: true, appId, per: ReadPermissionVal }); if (!app.permission.hasWritePer) { - app.modules = []; - app.edges = []; + return { + ...app, + modules: [], + edges: [] + }; } - return app; + return { + ...app, + modules: await Promise.all( + app.modules.map((node) => checkNode({ node, ownerTmbId: app.tmbId })) + ) + }; } export default NextAPI(handler); diff --git a/projects/app/src/pages/api/core/app/version/detail.ts b/projects/app/src/pages/api/core/app/version/detail.ts index a11fa7b8c..41721fd42 100644 --- a/projects/app/src/pages/api/core/app/version/detail.ts +++ b/projects/app/src/pages/api/core/app/version/detail.ts @@ -5,6 +5,7 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; +import { checkNode } from '@/service/core/app/utils'; type Props = { versionId: string; @@ -17,7 +18,7 @@ async function handler( ): Promise { const { versionId, appId } = req.query as Props; - await authApp({ req, authToken: true, appId, per: WritePermissionVal }); + const { app } = await authApp({ req, authToken: true, appId, per: WritePermissionVal }); const result = await MongoAppVersion.findById(versionId).lean(); if (!result) { @@ -26,6 +27,9 @@ async function handler( return { ...result, + nodes: await Promise.all( + result.nodes.map((n) => checkNode({ node: n, ownerTmbId: app.tmbId })) + ), versionName: result?.versionName || formatTime2YMDHM(result?.time) }; } diff --git a/projects/app/src/pages/api/core/chat/init.ts b/projects/app/src/pages/api/core/chat/init.ts index e4c73569b..596b70060 100644 --- a/projects/app/src/pages/api/core/chat/init.ts +++ b/projects/app/src/pages/api/core/chat/init.ts @@ -43,7 +43,6 @@ async function handler( // get app and history const { nodes, chatConfig } = await getAppLatestVersion(app._id, app); - const pluginInputs = chat?.pluginInputs ?? nodes?.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs ?? diff --git a/projects/app/src/pages/api/core/chat/outLink/init.ts b/projects/app/src/pages/api/core/chat/outLink/init.ts index 8a5baf335..a758a3411 100644 --- a/projects/app/src/pages/api/core/chat/outLink/init.ts +++ b/projects/app/src/pages/api/core/chat/outLink/init.ts @@ -2,7 +2,6 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import type { InitChatResponse, InitOutLinkChatProps } from '@/global/core/chat/api.d'; import { getGuideModule, getAppChatConfig } from '@fastgpt/global/core/workflow/utils'; -import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; import { authOutLink } from '@/service/support/permission/auth/outLink'; import { MongoApp } from '@fastgpt/service/core/app/schema'; import { AppErrEnum } from '@fastgpt/global/common/error/code/app'; @@ -11,7 +10,6 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { NextAPI } from '@/service/middleware/entry'; -import { UserModelSchema } from '@fastgpt/global/support/user/type'; import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils'; async function handler(req: NextApiRequest, res: NextApiResponse) { diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index 52d483418..f75e9a3b1 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -36,7 +36,6 @@ import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team'; import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant'; import { MongoApp } from '@fastgpt/service/core/app/schema'; -import { UserModelSchema } from '@fastgpt/global/support/user/type'; import { AppSchema } from '@fastgpt/global/core/app/type'; import { AuthOutLinkChatProps } from '@fastgpt/global/support/outLink/api'; import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; diff --git a/projects/app/src/service/core/app/utils.ts b/projects/app/src/service/core/app/utils.ts index 7804c1d1c..8e7e8e285 100644 --- a/projects/app/src/service/core/app/utils.ts +++ b/projects/app/src/service/core/app/utils.ts @@ -22,6 +22,14 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti import { UserChatItemValueItemType } from '@fastgpt/global/core/chat/type'; import { saveChat } from '@fastgpt/service/core/chat/saveChat'; import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller'; +import { + getChildAppPreviewNode, + splitCombinePluginId +} from '@fastgpt/service/core/app/plugin/controller'; +import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants'; +import { authAppByTmbId } from '@fastgpt/service/support/permission/app/auth'; +import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; +import { PluginDataType, StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; export const getScheduleTriggerApp = async () => { // 1. Find all the app @@ -125,3 +133,46 @@ export const getScheduleTriggerApp = async () => { }) ); }; + +export const checkNode = async ({ + node, + ownerTmbId +}: { + node: StoreNodeItemType; + ownerTmbId: string; +}) => { + const { pluginId } = node; + if (!pluginId) return node; + + try { + const { source } = await splitCombinePluginId(pluginId); + if (source === PluginSourceEnum.personal) { + await authAppByTmbId({ + tmbId: ownerTmbId, + appId: pluginId, + per: ReadPermissionVal + }); + } + + const preview = await getChildAppPreviewNode({ id: pluginId }); + return { + ...node, + pluginData: { + version: preview.version, + diagram: preview.diagram, + userGuide: preview.userGuide, + courseUrl: preview.courseUrl, + name: preview.name, + avatar: preview.avatar + } + }; + } catch (error: any) { + return { + ...node, + isError: true, + pluginData: { + error + } as PluginDataType + }; + } +}; diff --git a/projects/app/src/web/core/app/utils.ts b/projects/app/src/web/core/app/utils.ts index 1d68bdc8b..016a6bcef 100644 --- a/projects/app/src/web/core/app/utils.ts +++ b/projects/app/src/web/core/app/utils.ts @@ -388,6 +388,7 @@ export function form2AppWorkflow( }, // 这里不需要固定版本,给一个不存在的版本,每次都会用最新版 version: defaultNodeVersion, + pluginData: tool.pluginData, inputs: tool.inputs.map((input) => { // Special key value if (input.key === NodeInputKeyEnum.forbidStream) { diff --git a/projects/app/src/web/core/workflow/utils.ts b/projects/app/src/web/core/workflow/utils.ts index 209b284f8..6311b951f 100644 --- a/projects/app/src/web/core/workflow/utils.ts +++ b/projects/app/src/web/core/workflow/utils.ts @@ -349,6 +349,10 @@ export const checkWorkflowNodeAndConnection = ({ edge.targetHandle === NodeOutputKeyEnum.selectedTools && edge.target === node.data.nodeId ); + if (data.pluginData?.error) { + return [data.nodeId]; + } + if ( data.flowNodeType === FlowNodeTypeEnum.systemConfig || data.flowNodeType === FlowNodeTypeEnum.pluginConfig ||