diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx index c2bda8d8fc..f143c2fcef 100644 --- a/web/app/components/app-sidebar/app-info.tsx +++ b/web/app/components/app-sidebar/app-info.tsx @@ -239,7 +239,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx const secondaryOperations: Operation[] = [ // Import DSL (conditional) - ...(appDetail.mode !== AppModeEnum.AGENT_CHAT && (appDetail.mode === AppModeEnum.ADVANCED_CHAT || appDetail.mode === AppModeEnum.WORKFLOW)) ? [{ + ...(appDetail.mode === AppModeEnum.ADVANCED_CHAT || appDetail.mode === AppModeEnum.WORKFLOW) ? [{ id: 'import', title: t('workflow.common.importDSL'), icon: , @@ -271,7 +271,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx ] // Keep the switch operation separate as it's not part of the main operations - const switchOperation = (appDetail.mode !== AppModeEnum.AGENT_CHAT && (appDetail.mode === AppModeEnum.COMPLETION || appDetail.mode === AppModeEnum.CHAT)) ? { + const switchOperation = (appDetail.mode === AppModeEnum.COMPLETION || appDetail.mode === AppModeEnum.CHAT) ? { id: 'switch', title: t('app.switch'), icon: , diff --git a/web/app/components/workflow/update-dsl-modal.tsx b/web/app/components/workflow/update-dsl-modal.tsx index 00c36cce90..136c3d3455 100644 --- a/web/app/components/workflow/update-dsl-modal.tsx +++ b/web/app/components/workflow/update-dsl-modal.tsx @@ -9,6 +9,7 @@ import { } from 'react' import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' +import { load as yamlLoad } from 'js-yaml' import { RiAlertFill, RiCloseLine, @@ -16,8 +17,14 @@ import { } from '@remixicon/react' import { WORKFLOW_DATA_UPDATE } from './constants' import { + BlockEnum, SupportUploadFileTypes, } from './types' +import type { + CommonNodeType, + Node, +} from './types' +import { AppModeEnum } from '@/types/app' import { initialEdges, initialNodes, @@ -130,6 +137,33 @@ const UpdateDSLModal = ({ } as any) }, [eventEmitter]) + const validateDSLContent = (content: string): boolean => { + try { + const data = yamlLoad(content) as any + const nodes = data?.workflow?.graph?.nodes ?? [] + const invalidNodes = appDetail?.mode === AppModeEnum.ADVANCED_CHAT + ? [ + BlockEnum.End, + BlockEnum.TriggerWebhook, + BlockEnum.TriggerSchedule, + BlockEnum.TriggerPlugin, + ] + : [BlockEnum.Answer] + const hasInvalidNode = nodes.some((node: Node) => { + return invalidNodes.includes(node?.data?.type) + }) + if (hasInvalidNode) { + notify({ type: 'error', message: t('workflow.common.importFailure') }) + return false + } + return true + } + catch (err: any) { + notify({ type: 'error', message: t('workflow.common.importFailure') }) + return false + } + } + const isCreatingRef = useRef(false) const handleImport: MouseEventHandler = useCallback(async () => { if (isCreatingRef.current) @@ -138,7 +172,7 @@ const UpdateDSLModal = ({ if (!currentFile) return try { - if (appDetail && fileContent) { + if (appDetail && fileContent && validateDSLContent(fileContent)) { setLoading(true) const response = await importDSL({ mode: DSLImportMode.YAML_CONTENT, yaml_content: fileContent, app_id: appDetail.id }) const { id, status, app_id, imported_dsl_version, current_dsl_version } = response diff --git a/web/package.json b/web/package.json index d10359f25d..0d267d7ee8 100644 --- a/web/package.json +++ b/web/package.json @@ -88,6 +88,7 @@ "immer": "^10.1.3", "js-audio-recorder": "^1.0.7", "js-cookie": "^3.0.5", + "js-yaml": "^4.1.0", "jsonschema": "^1.5.0", "katex": "^0.16.25", "ky": "^1.12.0", @@ -163,6 +164,7 @@ "@testing-library/react": "^16.3.0", "@types/jest": "^29.5.14", "@types/js-cookie": "^3.0.6", + "@types/js-yaml": "^4.0.9", "@types/lodash-es": "^4.17.12", "@types/negotiator": "^0.6.4", "@types/node": "18.15.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 8e638ed2df..7ef519a291 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -192,6 +192,9 @@ importers: js-cookie: specifier: ^3.0.5 version: 3.0.5 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 jsonschema: specifier: ^1.5.0 version: 1.5.0 @@ -412,6 +415,9 @@ importers: '@types/js-cookie': specifier: ^3.0.6 version: 3.0.6 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -3240,6 +3246,9 @@ packages: '@types/js-cookie@3.0.6': resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -11632,6 +11641,8 @@ snapshots: '@types/js-cookie@3.0.6': {} + '@types/js-yaml@4.0.9': {} + '@types/json-schema@7.0.15': {} '@types/katex@0.16.7': {}