mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 10:28:10 +08:00
feat: Refactor app export to support sandboxed bundle format
This commit is contained in:
@ -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<boolean>(false)
|
||||
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
|
||||
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 && (
|
||||
<DSLExportConfirmModal
|
||||
envList={secretEnvList}
|
||||
onConfirm={onExport}
|
||||
onConfirm={include => onExport(include, exportSandboxed)}
|
||||
onClose={() => setSecretEnvList([])}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -73,6 +73,7 @@ type WorkflowChildrenProps = {
|
||||
const WorkflowChildren = ({ headerLeftSlot }: WorkflowChildrenProps) => {
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
|
||||
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 && (
|
||||
<DSLExportConfirmModal
|
||||
envList={secretEnvList}
|
||||
onConfirm={handleExportDSL!}
|
||||
onConfirm={include => handleExportDSL!(include, undefined, exportSandboxed)}
|
||||
onClose={() => setSecretEnvList([])}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ export type CommonHooksFnMap = {
|
||||
availableNodesMetaData?: AvailableNodesMetaData
|
||||
getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string, traceUrl: string }
|
||||
exportCheck?: () => Promise<void>
|
||||
handleExportDSL?: (include?: boolean, flowId?: string) => Promise<void>
|
||||
handleExportDSL?: (include?: boolean, flowId?: string, sandboxed?: boolean) => Promise<void>
|
||||
fetchInspectVars: (params: { passInVars?: boolean, vars?: VarInInspect[], passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>, passedInSchemaTypeDefinitions?: SchemaTypeDefinition[] }) => Promise<void>
|
||||
hasNodeInspectVars: (nodeId: string) => boolean
|
||||
hasSetInspectVar: (nodeId: string, name: string, sysVars: VarInInspect[], conversationVars: VarInInspect[]) => boolean
|
||||
|
||||
@ -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])
|
||||
|
||||
Reference in New Issue
Block a user