mirror of
https://github.com/langgenius/dify.git
synced 2026-03-30 18:40:17 +08:00
feat: support import zip
This commit is contained in:
@ -1,23 +1,84 @@
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { DSLImportStatus } from '@/models/app'
|
||||
import { importAppBundle } from '@/service/apps'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
|
||||
type DSLConfirmModalProps = {
|
||||
file?: File
|
||||
versions?: {
|
||||
importedVersion: string
|
||||
systemVersion: string
|
||||
}
|
||||
onCancel: () => void
|
||||
onConfirm: () => void
|
||||
onSuccess?: () => void
|
||||
confirmDisabled?: boolean
|
||||
}
|
||||
const DSLConfirmModal = ({
|
||||
file,
|
||||
versions = { importedVersion: '', systemVersion: '' },
|
||||
onCancel,
|
||||
onConfirm,
|
||||
onSuccess,
|
||||
confirmDisabled = false,
|
||||
}: DSLConfirmModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const { push } = useRouter()
|
||||
const { isCurrentWorkspaceEditor } = useAppContext()
|
||||
const { handleCheckPluginDependencies } = usePluginDependencies()
|
||||
const [isImporting, setIsImporting] = useState(false)
|
||||
const isZipFile = !!file && file.name.toLowerCase().endsWith('.zip')
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (confirmDisabled || isImporting)
|
||||
return
|
||||
if (!isZipFile) {
|
||||
onConfirm()
|
||||
return
|
||||
}
|
||||
if (!file)
|
||||
return
|
||||
|
||||
setIsImporting(true)
|
||||
try {
|
||||
const response = await importAppBundle({ file })
|
||||
const { status, app_id, app_mode } = response
|
||||
|
||||
if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) {
|
||||
notify({
|
||||
type: status === DSLImportStatus.COMPLETED ? 'success' : 'warning',
|
||||
message: t(status === DSLImportStatus.COMPLETED ? 'newApp.appCreated' : 'newApp.caution', { ns: 'app' }),
|
||||
children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('newApp.appCreateDSLWarning', { ns: 'app' }),
|
||||
})
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
if (app_id)
|
||||
await handleCheckPluginDependencies(app_id)
|
||||
if (app_id)
|
||||
getRedirection(isCurrentWorkspaceEditor, { id: app_id, mode: app_mode }, push)
|
||||
onSuccess?.()
|
||||
onCancel()
|
||||
}
|
||||
else {
|
||||
notify({ type: 'error', message: t('importBundleFailed', { ns: 'app' }) })
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
const error = e as Error
|
||||
notify({ type: 'error', message: error.message || t('importBundleFailed', { ns: 'app' }) })
|
||||
}
|
||||
finally {
|
||||
setIsImporting(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -43,7 +104,15 @@ const DSLConfirmModal = ({
|
||||
</div>
|
||||
<div className="flex items-start justify-end gap-2 self-stretch pt-6">
|
||||
<Button variant="secondary" onClick={() => onCancel()}>{t('newApp.Cancel', { ns: 'app' })}</Button>
|
||||
<Button variant="primary" destructive onClick={onConfirm} disabled={confirmDisabled}>{t('newApp.Confirm', { ns: 'app' })}</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
destructive
|
||||
onClick={handleConfirm}
|
||||
disabled={confirmDisabled || isImporting}
|
||||
loading={isImporting}
|
||||
>
|
||||
{t('newApp.Confirm', { ns: 'app' })}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { MouseEventHandler } from 'react'
|
||||
import { RiCloseLine, RiExternalLinkLine } from '@remixicon/react'
|
||||
import { useDebounceFn, useKeyPress } from 'ahooks'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
@ -24,11 +23,13 @@ import {
|
||||
DSLImportStatus,
|
||||
} from '@/models/app'
|
||||
import {
|
||||
importAppBundle,
|
||||
importDSL,
|
||||
importDSLConfirm,
|
||||
} from '@/service/apps'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import DSLConfirmModal from './dsl-confirm-modal'
|
||||
import Uploader from './uploader'
|
||||
|
||||
type CreateFromDSLModalProps = {
|
||||
@ -59,6 +60,8 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
const [importId, setImportId] = useState<string>()
|
||||
const { handleCheckPluginDependencies } = usePluginDependencies()
|
||||
|
||||
const isZipFile = (file?: File) => !!file && file.name.toLowerCase().endsWith('.zip')
|
||||
|
||||
const readFile = (file: File) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (event) {
|
||||
@ -70,9 +73,9 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
|
||||
const handleFile = (file?: File) => {
|
||||
setDSLFile(file)
|
||||
if (file)
|
||||
if (file && !isZipFile(file))
|
||||
readFile(file)
|
||||
if (!file)
|
||||
if (!file || isZipFile(file))
|
||||
setFileContent('')
|
||||
}
|
||||
|
||||
@ -99,10 +102,15 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
let response
|
||||
|
||||
if (currentTab === CreateFromDSLModalTab.FROM_FILE) {
|
||||
response = await importDSL({
|
||||
mode: DSLImportMode.YAML_CONTENT,
|
||||
yaml_content: fileContent || '',
|
||||
})
|
||||
if (isZipFile(currentFile)) {
|
||||
response = await importAppBundle({ file: currentFile! })
|
||||
}
|
||||
else {
|
||||
response = await importDSL({
|
||||
mode: DSLImportMode.YAML_CONTENT,
|
||||
yaml_content: fileContent || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
if (currentTab === CreateFromDSLModalTab.FROM_URL) {
|
||||
response = await importDSL({
|
||||
@ -170,7 +178,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
onClose()
|
||||
})
|
||||
|
||||
const onDSLConfirm: MouseEventHandler = async () => {
|
||||
const onDSLConfirm = async () => {
|
||||
try {
|
||||
if (!importId)
|
||||
return
|
||||
@ -205,6 +213,12 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirmSuccess = () => {
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
onClose()
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
key: CreateFromDSLModalTab.FROM_FILE,
|
||||
@ -273,6 +287,8 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
className="mt-0"
|
||||
file={currentFile}
|
||||
updateFile={handleFile}
|
||||
accept=".yaml,.yml,.zip"
|
||||
displayName={isZipFile(currentFile) ? 'ZIP' : 'YAML'}
|
||||
/>
|
||||
)}
|
||||
{currentTab === CreateFromDSLModalTab.FROM_URL && (
|
||||
@ -317,32 +333,15 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
isShow={showErrorModal}
|
||||
onClose={() => setShowErrorModal(false)}
|
||||
className="w-[480px]"
|
||||
>
|
||||
<div className="flex flex-col items-start gap-2 self-stretch pb-4">
|
||||
<div className="title-2xl-semi-bold text-text-primary">{t('newApp.appCreateDSLErrorTitle', { ns: 'app' })}</div>
|
||||
<div className="system-md-regular flex grow flex-col text-text-secondary">
|
||||
<div>{t('newApp.appCreateDSLErrorPart1', { ns: 'app' })}</div>
|
||||
<div>{t('newApp.appCreateDSLErrorPart2', { ns: 'app' })}</div>
|
||||
<br />
|
||||
<div>
|
||||
{t('newApp.appCreateDSLErrorPart3', { ns: 'app' })}
|
||||
<span className="system-md-medium">{versions?.importedVersion}</span>
|
||||
</div>
|
||||
<div>
|
||||
{t('newApp.appCreateDSLErrorPart4', { ns: 'app' })}
|
||||
<span className="system-md-medium">{versions?.systemVersion}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start justify-end gap-2 self-stretch pt-6">
|
||||
<Button variant="secondary" onClick={() => setShowErrorModal(false)}>{t('newApp.Cancel', { ns: 'app' })}</Button>
|
||||
<Button variant="primary" destructive onClick={onDSLConfirm}>{t('newApp.Confirm', { ns: 'app' })}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
{showErrorModal && (
|
||||
<DSLConfirmModal
|
||||
file={currentFile}
|
||||
versions={versions}
|
||||
onCancel={() => setShowErrorModal(false)}
|
||||
onConfirm={onDSLConfirm}
|
||||
onSuccess={handleConfirmSuccess}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user