diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx index ebfc75891f..6ab0da34d8 100644 --- a/web/app/components/app-sidebar/app-info.tsx +++ b/web/app/components/app-sidebar/app-info.tsx @@ -27,12 +27,13 @@ import { webSocketClient } from '@/app/components/workflow/collaboration/core/we import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' -import { copyApp, deleteApp, exportAppConfig, fetchAppDetail, updateAppInfo } from '@/service/apps' +import { copyApp, deleteApp, exportAppBundle, exportAppConfig, fetchAppDetail, updateAppInfo } from '@/service/apps' import { useInvalidateAppList } from '@/service/use-apps' import { fetchWorkflowDraft } from '@/service/workflow' import { AppModeEnum } from '@/types/app' import { getRedirection } from '@/utils/app-redirection' import { cn } from '@/utils/classnames' +import { downloadBlob } from '@/utils/download' import AppIcon from '../base/app-icon' import AppOperations from './app-operations' @@ -78,6 +79,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx const [showImportDSLModal, setShowImportDSLModal] = useState(false) const [secretEnvList, setSecretEnvList] = useState([]) const [showExportWarning, setShowExportWarning] = useState(false) + const [exportSandboxed, setExportSandboxed] = useState(false) const emitAppMetaUpdate = useCallback(() => { if (!appDetail?.id) @@ -153,21 +155,23 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx } } - const onExport = async (include = false) => { + const onExport = async (include = false, sandboxed = false) => { if (!appDetail) return try { + if (sandboxed) { + await exportAppBundle({ + appID: appDetail.id, + include, + }) + return + } const { data } = await exportAppConfig({ appID: appDetail.id, include, }) - const a = document.createElement('a') const file = new Blob([data], { type: 'application/yaml' }) - const url = URL.createObjectURL(file) - a.href = url - a.download = `${appDetail.name}.yml` - a.click() - URL.revokeObjectURL(url) + downloadBlob({ data: file, fileName: `${appDetail.name}.yaml` }) } catch { notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) }) @@ -178,7 +182,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx if (!appDetail) return if (appDetail.mode !== AppModeEnum.WORKFLOW && appDetail.mode !== AppModeEnum.ADVANCED_CHAT) { - onExport() + onExport(false, false) return } @@ -192,11 +196,13 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx try { const workflowDraft = await fetchWorkflowDraft(`/apps/${appDetail.id}/workflows/draft`) const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret') + const sandboxed = workflowDraft.features?.sandbox?.enabled === true if (list.length === 0) { - onExport() + onExport(false, sandboxed) return } setSecretEnvList(list) + setExportSandboxed(sandboxed) } catch { notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) }) @@ -490,7 +496,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx {secretEnvList.length > 0 && ( onExport(include, exportSandboxed)} onClose={() => setSecretEnvList([])} /> )} diff --git a/web/app/components/workflow-app/components/workflow-children.tsx b/web/app/components/workflow-app/components/workflow-children.tsx index c0efd5dc3c..ba0fe517ea 100644 --- a/web/app/components/workflow-app/components/workflow-children.tsx +++ b/web/app/components/workflow-app/components/workflow-children.tsx @@ -73,6 +73,7 @@ type WorkflowChildrenProps = { const WorkflowChildren = ({ headerLeftSlot }: WorkflowChildrenProps) => { const { eventEmitter } = useEventEmitterContextContext() const [secretEnvList, setSecretEnvList] = useState([]) + const [exportSandboxed, setExportSandboxed] = useState(false) const showFeaturesPanel = useStore(s => s.showFeaturesPanel) const showImportDSLModal = useStore(s => s.showImportDSLModal) const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal) @@ -93,8 +94,10 @@ const WorkflowChildren = ({ headerLeftSlot }: WorkflowChildrenProps) => { } = useDSL() eventEmitter?.useSubscription((v: any) => { - if (v.type === DSL_EXPORT_CHECK) + if (v.type === DSL_EXPORT_CHECK) { setSecretEnvList(v.payload.data as EnvironmentVariable[]) + setExportSandboxed(v.payload.sandboxed || false) + } }) const autoGenerateWebhookUrl = useAutoGenerateWebhookUrl() @@ -188,7 +191,7 @@ const WorkflowChildren = ({ headerLeftSlot }: WorkflowChildrenProps) => { secretEnvList.length > 0 && ( handleExportDSL!(include, undefined, exportSandboxed)} onClose={() => setSecretEnvList([])} /> ) diff --git a/web/app/components/workflow-app/hooks/use-DSL.ts b/web/app/components/workflow-app/hooks/use-DSL.ts index 6c01509bc5..ff727ec9c7 100644 --- a/web/app/components/workflow-app/hooks/use-DSL.ts +++ b/web/app/components/workflow-app/hooks/use-DSL.ts @@ -9,8 +9,9 @@ import { DSL_EXPORT_CHECK, } from '@/app/components/workflow/constants' import { useEventEmitterContextContext } from '@/context/event-emitter' -import { exportAppConfig } from '@/service/apps' +import { exportAppBundle, exportAppConfig } from '@/service/apps' import { fetchWorkflowDraft } from '@/service/workflow' +import { downloadBlob } from '@/utils/download' import { useNodesSyncDraft } from './use-nodes-sync-draft' export const useDSL = () => { @@ -22,7 +23,7 @@ export const useDSL = () => { const appDetail = useAppStore(s => s.appDetail) - const handleExportDSL = useCallback(async (include = false, workflowId?: string) => { + const handleExportDSL = useCallback(async (include = false, workflowId?: string, sandboxed = false) => { if (!appDetail) return @@ -32,18 +33,23 @@ export const useDSL = () => { try { setExporting(true) await doSyncWorkflowDraft() - const { data } = await exportAppConfig({ - appID: appDetail.id, - include, - workflowID: workflowId, - }) - const a = document.createElement('a') - const file = new Blob([data], { type: 'application/yaml' }) - const url = URL.createObjectURL(file) - a.href = url - a.download = `${appDetail.name}.yml` - a.click() - URL.revokeObjectURL(url) + + if (sandboxed) { + await exportAppBundle({ + appID: appDetail.id, + include, + workflowID: workflowId, + }) + } + else { + const { data } = await exportAppConfig({ + appID: appDetail.id, + include, + workflowID: workflowId, + }) + const file = new Blob([data], { type: 'application/yaml' }) + downloadBlob({ data: file, fileName: `${appDetail.name}.yaml` }) + } } catch { notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) }) @@ -58,15 +64,17 @@ export const useDSL = () => { return try { const workflowDraft = await fetchWorkflowDraft(`/apps/${appDetail?.id}/workflows/draft`) + const sandboxed = workflowDraft.features?.sandbox?.enabled === true const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret') if (list.length === 0) { - handleExportDSL() + handleExportDSL(false, undefined, sandboxed) return } eventEmitter?.emit({ type: DSL_EXPORT_CHECK, payload: { data: list, + sandboxed, }, } as any) } diff --git a/web/app/components/workflow/hooks-store/store.ts b/web/app/components/workflow/hooks-store/store.ts index be353c6760..975a541b23 100644 --- a/web/app/components/workflow/hooks-store/store.ts +++ b/web/app/components/workflow/hooks-store/store.ts @@ -61,7 +61,7 @@ export type CommonHooksFnMap = { availableNodesMetaData?: AvailableNodesMetaData getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string, traceUrl: string } exportCheck?: () => Promise - handleExportDSL?: (include?: boolean, flowId?: string) => Promise + handleExportDSL?: (include?: boolean, flowId?: string, sandboxed?: boolean) => Promise fetchInspectVars: (params: { passInVars?: boolean, vars?: VarInInspect[], passedInAllPluginInfoList?: Record, passedInSchemaTypeDefinitions?: SchemaTypeDefinition[] }) => Promise hasNodeInspectVars: (nodeId: string) => boolean hasSetInspectVar: (nodeId: string, name: string, sysVars: VarInInspect[], conversationVars: VarInInspect[]) => boolean diff --git a/web/app/components/workflow/panel/version-history-panel/index.tsx b/web/app/components/workflow/panel/version-history-panel/index.tsx index 04cfc4c3b8..1b71110c61 100644 --- a/web/app/components/workflow/panel/version-history-panel/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/index.tsx @@ -126,7 +126,7 @@ export const VersionHistoryPanel = ({ }) break case VersionHistoryContextMenuOptions.exportDSL: - handleExportDSL?.(false, item.id) + handleExportDSL?.(false, item.id, item.features?.sandbox?.enabled === true) break } }, [t, handleExportDSL])