mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-24 22:03:54 +00:00
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
This commit is contained in:
@@ -196,7 +196,7 @@ const Header = () => {
|
||||
<SaveButton
|
||||
isLoading={loading}
|
||||
onClickSave={onClickSave}
|
||||
checkData={flowData2StoreDataAndCheck}
|
||||
checkData={() => !!flowData2StoreDataAndCheck()}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
|
@@ -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}
|
||||
/>
|
||||
<SaveButton isLoading={loading} onClickSave={onClickSave} />
|
||||
<SaveButton
|
||||
isLoading={loading}
|
||||
onClickSave={onClickSave}
|
||||
checkData={() => {
|
||||
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;
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
|
@@ -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) => (
|
||||
<MyTooltip key={item.id} label={item.intro}>
|
||||
<Flex
|
||||
overflow={'hidden'}
|
||||
alignItems={'center'}
|
||||
p={2.5}
|
||||
bg={'white'}
|
||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||
borderRadius={'md'}
|
||||
border={theme.borders.base}
|
||||
_hover={{
|
||||
...hoverDeleteStyles,
|
||||
borderColor: 'primary.300'
|
||||
}}
|
||||
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)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setConfigTool(item);
|
||||
}}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'1.5rem'} h={'1.5rem'} borderRadius={'sm'} />
|
||||
<Box
|
||||
ml={2}
|
||||
flex={'1 0 0'}
|
||||
w={0}
|
||||
className={'textEllipsis'}
|
||||
fontSize={'sm'}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
<DeleteIcon
|
||||
onClick={(e) => {
|
||||
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 (
|
||||
<MyTooltip key={item.id} label={item.intro}>
|
||||
<Flex
|
||||
overflow={'hidden'}
|
||||
alignItems={'center'}
|
||||
p={2.5}
|
||||
bg={'white'}
|
||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||
borderRadius={'md'}
|
||||
border={theme.borders.base}
|
||||
borderColor={hasError ? 'red.600' : ''}
|
||||
_hover={{
|
||||
...hoverDeleteStyles,
|
||||
borderColor: hasError ? 'red.600' : 'primary.300'
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
))}
|
||||
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);
|
||||
}}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'1.5rem'} h={'1.5rem'} borderRadius={'sm'} />
|
||||
<Box
|
||||
flex={'1 0 0'}
|
||||
ml={2}
|
||||
gap={2}
|
||||
className={'textEllipsis'}
|
||||
fontSize={'sm'}
|
||||
color={'myGray.900'}
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
{hasError && (
|
||||
<MyTooltip label={t('app:app.modules.not_found_tips')}>
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={6}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>{t('app:app.modules.not_found')}</Box>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
)}
|
||||
<DeleteIcon
|
||||
ml={2}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setAppForm((state: AppSimpleEditFormType) => ({
|
||||
...state,
|
||||
selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id)
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
|
||||
{isOpenToolsSelect && (
|
||||
|
@@ -200,7 +200,7 @@ const Header = () => {
|
||||
<SaveButton
|
||||
isLoading={loading}
|
||||
onClickSave={onClickSave}
|
||||
checkData={flowData2StoreDataAndCheck}
|
||||
checkData={() => !!flowData2StoreDataAndCheck()}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
|
@@ -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<void>;
|
||||
checkData?: (hideTip?: boolean) =>
|
||||
| {
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
}
|
||||
| undefined;
|
||||
checkData?: () => boolean | undefined;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [isSave, setIsSave] = useState(false);
|
||||
|
@@ -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) => {
|
||||
)}
|
||||
</UseGuideModal>
|
||||
)}
|
||||
{!!node?.pluginData?.error && (
|
||||
<MyTooltip label={t('app:app.modules.not_found_tips')}>
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={8}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>{t('app:app.modules.not_found')}</Box>
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
)}
|
||||
</Flex>
|
||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||
</Box>
|
||||
@@ -297,6 +317,7 @@ const NodeCard = (props: Props) => {
|
||||
}, [
|
||||
node?.flowNodeType,
|
||||
node?.courseUrl,
|
||||
node?.pluginData?.error,
|
||||
showToolHandle,
|
||||
nodeId,
|
||||
isFolded,
|
||||
|
@@ -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<AppContextType>({
|
||||
|
||||
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,
|
||||
|
@@ -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<any>) {
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
@@ -15,11 +16,19 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
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);
|
||||
|
@@ -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<AppVersionSchemaType> {
|
||||
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)
|
||||
};
|
||||
}
|
||||
|
@@ -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 ??
|
||||
|
@@ -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) {
|
||||
|
@@ -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';
|
||||
|
@@ -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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@@ -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) {
|
||||
|
@@ -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 ||
|
||||
|
Reference in New Issue
Block a user