修复应用安装界面无法通过上传安装包安装app的问题
This commit is contained in:
parent
51d9f9b0a1
commit
916ec1b69c
@ -223,15 +223,9 @@ const beforeUpload = (data: { file: UploadFileInfo }) => {
|
|||||||
const handleFileChange = (options: { fileList: UploadFileInfo[] }) => {
|
const handleFileChange = (options: { fileList: UploadFileInfo[] }) => {
|
||||||
fileList.value = options.fileList
|
fileList.value = options.fileList
|
||||||
|
|
||||||
|
// 移除对扩展包的特殊处理,统一使用应用安装流程
|
||||||
if (fileList.value.length > 0) {
|
if (fileList.value.length > 0) {
|
||||||
const fileName = fileList.value[0].file?.name || ''
|
isExtensionFile.value = false
|
||||||
isExtensionFile.value = fileName.toLowerCase().endsWith('.tar.gz') ||
|
|
||||||
fileName.toLowerCase().endsWith('.tgz') ||
|
|
||||||
fileName.toLowerCase().endsWith('.gz')
|
|
||||||
|
|
||||||
if (isExtensionFile.value) {
|
|
||||||
appName.value = ''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,77 +245,7 @@ const startUpload = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileName = file.name.toLowerCase()
|
// 统一使用应用安装流程,不再区分扩展包和应用包
|
||||||
const isExtensionPackage = fileName.endsWith('.tar.gz') || fileName.endsWith('.tgz') || fileName.endsWith('.gz')
|
|
||||||
|
|
||||||
if (isExtensionPackage) {
|
|
||||||
try {
|
|
||||||
uploading.value = true
|
|
||||||
installing.value = true
|
|
||||||
showProgressModal.value = true
|
|
||||||
installProgress.value = 0
|
|
||||||
installMessage.value = t('Uploading file...')
|
|
||||||
installStatus.value = 'info'
|
|
||||||
|
|
||||||
const formData = new FormData()
|
|
||||||
formData.append('file', file)
|
|
||||||
|
|
||||||
installProgress.value = 20
|
|
||||||
installMessage.value = t('Saving package...')
|
|
||||||
|
|
||||||
const saveResponse = await axios.post('/jingrow/install-extension', formData, {
|
|
||||||
headers: {
|
|
||||||
...get_session_api_headers(),
|
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
},
|
|
||||||
timeout: 60000
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!saveResponse.data.success) {
|
|
||||||
throw new Error(saveResponse.data.error || t('Failed to save package'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileUrl = saveResponse.data.file_url
|
|
||||||
|
|
||||||
installProgress.value = 50
|
|
||||||
installMessage.value = t('Installing package...')
|
|
||||||
const installResponse = await axios.post('/api/action/jingrow.ai.utils.jlocal.install_package', {
|
|
||||||
package_file_url: fileUrl
|
|
||||||
}, {
|
|
||||||
headers: get_session_api_headers()
|
|
||||||
})
|
|
||||||
|
|
||||||
installProgress.value = 100
|
|
||||||
installMessage.value = t('Installation completed!')
|
|
||||||
installStatus.value = 'success'
|
|
||||||
|
|
||||||
if (installResponse.data.message && installResponse.data.message.success) {
|
|
||||||
clearFiles()
|
|
||||||
const result = installResponse.data.message
|
|
||||||
message.success(t('Package \'{0}\' installed successfully').replace('{0}', result.package_name))
|
|
||||||
} else {
|
|
||||||
throw new Error(installResponse.data.message?.error || t('Installation failed'))
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error: any) {
|
|
||||||
installProgress.value = 100
|
|
||||||
installMessage.value = t('Installation failed!')
|
|
||||||
installStatus.value = 'error'
|
|
||||||
|
|
||||||
const errorMsg = error.response?.data?.detail ||
|
|
||||||
error.response?.data?.error ||
|
|
||||||
error.message ||
|
|
||||||
t('Upload failed')
|
|
||||||
|
|
||||||
message.error(errorMsg)
|
|
||||||
} finally {
|
|
||||||
uploading.value = false
|
|
||||||
installing.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiEndpoint = '/jingrow/install/upload'
|
const apiEndpoint = '/jingrow/install/upload'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -334,12 +258,12 @@ const startUpload = async () => {
|
|||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
if (appName.value.trim() && !isExtensionPackage) {
|
if (appName.value.trim()) {
|
||||||
formData.append('app_name', appName.value.trim())
|
formData.append('app_name', appName.value.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
installProgress.value = 30
|
installProgress.value = 30
|
||||||
installMessage.value = isExtensionPackage ? t('Installing extension package...') : t('Uploading file...')
|
installMessage.value = t('Uploading file...')
|
||||||
const response = await axios.post(apiEndpoint, formData, {
|
const response = await axios.post(apiEndpoint, formData, {
|
||||||
headers: {
|
headers: {
|
||||||
...get_session_api_headers(),
|
...get_session_api_headers(),
|
||||||
@ -360,7 +284,7 @@ const startUpload = async () => {
|
|||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
clearFiles()
|
clearFiles()
|
||||||
|
|
||||||
const resultAppName = isExtensionPackage ? response.data.package_name : response.data.app_name
|
const resultAppName = response.data.app_name
|
||||||
message.success(t('Package \'{0}\' installed successfully').replace('{0}', resultAppName))
|
message.success(t('Package \'{0}\' installed successfully').replace('{0}', resultAppName))
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.error || t('Installation failed'))
|
throw new Error(response.data.error || t('Installation failed'))
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from typing import Dict, Any, List, Optional
|
|||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
@ -82,34 +83,127 @@ async def install_app_from_upload(
|
|||||||
):
|
):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not file.filename.lower().endswith('.zip'):
|
filename_lower = file.filename.lower()
|
||||||
raise HTTPException(status_code=400, detail="只支持ZIP格式的安装包")
|
|
||||||
|
|
||||||
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
|
# 支持zip、tar.gz、tgz、gz格式
|
||||||
|
if not (filename_lower.endswith('.zip') or
|
||||||
|
filename_lower.endswith('.tar.gz') or
|
||||||
|
filename_lower.endswith('.tgz') or
|
||||||
|
filename_lower.endswith('.gz')):
|
||||||
|
raise HTTPException(status_code=400, detail="只支持ZIP、TAR.GZ、TGZ、GZ格式的安装包")
|
||||||
|
|
||||||
|
# 获取项目根目录
|
||||||
|
current = Path(__file__).resolve()
|
||||||
|
root = current.parents[4] # apps/jingrow/jingrow/api/ -> apps/ -> root
|
||||||
|
tmp_dir = root / "tmp"
|
||||||
|
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 创建唯一的文件名
|
||||||
|
temp_filename = f"upload_{uuid.uuid4().hex[:8]}{Path(filename_lower).suffix}"
|
||||||
|
temp_file_path = tmp_dir / temp_filename
|
||||||
|
|
||||||
|
# 保存上传的文件到项目tmp目录
|
||||||
|
content = await file.read()
|
||||||
|
log_info(f"文件大小: {len(content)} 字节")
|
||||||
|
|
||||||
|
with open(temp_file_path, 'wb') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
log_info(f"文件已保存到: {temp_file_path}, 存在: {temp_file_path.exists()}, 大小: {temp_file_path.stat().st_size if temp_file_path.exists() else 0}")
|
||||||
|
|
||||||
|
# 统一使用 install_app 函数处理所有格式
|
||||||
try:
|
try:
|
||||||
content = await file.read()
|
result = install_app(str(temp_file_path), app_name)
|
||||||
temp_file.write(content)
|
|
||||||
temp_file.close()
|
|
||||||
|
|
||||||
result = install_app(temp_file.name, app_name)
|
|
||||||
|
|
||||||
if result.get('success'):
|
if result.get('success'):
|
||||||
app_name_result = result.get('app_name')
|
app_name_result = result.get('app_name')
|
||||||
|
backend_result = result.get('backend_result', {})
|
||||||
|
app_dir = backend_result.get('app_dir')
|
||||||
|
|
||||||
|
# 对齐扫描安装的执行链
|
||||||
try:
|
try:
|
||||||
_import_app_package_and_pagetypes(app_name_result, result)
|
# 1. 添加到 Local Installed Apps PageType
|
||||||
except Exception:
|
from jingrow.utils.jingrow_api import get_single_pagetype
|
||||||
pass
|
pagetype_result = get_single_pagetype("Local Installed Apps")
|
||||||
|
if pagetype_result.get('success'):
|
||||||
|
config = pagetype_result.get('config', {})
|
||||||
|
apps_list = config.get('local_installed_apps', [])
|
||||||
|
else:
|
||||||
|
apps_list = []
|
||||||
|
|
||||||
|
# 检查是否已存在,如果存在则更新,否则添加
|
||||||
|
app_exists = False
|
||||||
|
for app in apps_list:
|
||||||
|
if app.get('app_name', '') == app_name_result:
|
||||||
|
app['app_version'] = '1.0.0'
|
||||||
|
app['git_branch'] = 'main'
|
||||||
|
app_exists = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not app_exists:
|
||||||
|
apps_list.append({
|
||||||
|
'app_name': app_name_result,
|
||||||
|
'app_version': '1.0.0',
|
||||||
|
'git_branch': 'main'
|
||||||
|
})
|
||||||
|
|
||||||
|
# 更新数据库
|
||||||
|
if await _update_local_installed_apps(apps_list):
|
||||||
|
log_info(f"已更新 Local Installed Apps: {app_name_result}")
|
||||||
|
|
||||||
|
# 2. 调用 sync_app_files 同步文件到数据库(pagetype, package, module)
|
||||||
|
log_info(f"准备同步应用文件: app_name={app_name_result}, app_dir={app_dir}")
|
||||||
|
|
||||||
|
if app_dir:
|
||||||
|
# 计算应用的后端目录
|
||||||
|
current = Path(__file__).resolve()
|
||||||
|
root = current.parents[4]
|
||||||
|
apps_dir = root / "apps"
|
||||||
|
backend_dir = apps_dir / app_name_result / app_name_result
|
||||||
|
if not backend_dir.exists():
|
||||||
|
backend_dir = apps_dir / app_name_result
|
||||||
|
|
||||||
|
log_info(f"后端目录: {backend_dir}, 存在: {backend_dir.exists()}")
|
||||||
|
|
||||||
|
if backend_dir.exists():
|
||||||
|
try:
|
||||||
|
api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.sync_app_files"
|
||||||
|
log_info(f"调用 sync_app_files API: {api_url}, 参数: app_name={app_name_result}, app_path={str(backend_dir)}")
|
||||||
|
response = requests.post(
|
||||||
|
api_url,
|
||||||
|
json={'app_name': app_name_result, 'app_path': str(backend_dir), 'force': True},
|
||||||
|
headers=get_jingrow_api_headers(),
|
||||||
|
timeout=60
|
||||||
|
)
|
||||||
|
log_info(f"sync_app_files 响应: status={response.status_code}, body={response.text}")
|
||||||
|
if response.status_code == 200:
|
||||||
|
log_info(f"已同步应用文件到数据库: {app_name_result}, 响应: {response.json()}")
|
||||||
|
else:
|
||||||
|
log_error(f"同步应用文件失败: HTTP {response.status_code}, {response.text}")
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
log_error(f"同步应用文件异常: {str(e)}, {traceback.format_exc()}")
|
||||||
|
else:
|
||||||
|
log_error(f"后端目录不存在: {backend_dir}")
|
||||||
|
else:
|
||||||
|
log_error(f"app_dir 为空,无法同步文件")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"更新数据库失败: {str(e)}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=400, detail=result.get('error'))
|
error_msg = result.get('error', '未知错误')
|
||||||
|
log_error(f"安装失败: {error_msg}")
|
||||||
|
raise HTTPException(status_code=400, detail=error_msg)
|
||||||
finally:
|
finally:
|
||||||
|
# 清理上传的文件
|
||||||
try:
|
try:
|
||||||
os.unlink(temp_file.name)
|
if temp_file_path.exists():
|
||||||
except:
|
os.unlink(temp_file_path)
|
||||||
pass
|
log_info(f"已删除临时文件: {temp_file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"删除临时文件失败: {e}")
|
||||||
|
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import shutil
|
|||||||
import tempfile
|
import tempfile
|
||||||
import zipfile
|
import zipfile
|
||||||
import tarfile
|
import tarfile
|
||||||
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Any
|
from typing import Dict, List, Any
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -36,7 +37,6 @@ def get_app_directories():
|
|||||||
"""获取应用目录路径"""
|
"""获取应用目录路径"""
|
||||||
project_root = Path(__file__).resolve().parents[4]
|
project_root = Path(__file__).resolve().parents[4]
|
||||||
apps_dir = project_root / "apps"
|
apps_dir = project_root / "apps"
|
||||||
|
|
||||||
apps_dir.mkdir(parents=True, exist_ok=True)
|
apps_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
return apps_dir, apps_dir
|
return apps_dir, apps_dir
|
||||||
@ -45,7 +45,15 @@ def get_app_directories():
|
|||||||
@handle_errors
|
@handle_errors
|
||||||
def extract_package(zip_path: str) -> Dict[str, Any]:
|
def extract_package(zip_path: str) -> Dict[str, Any]:
|
||||||
"""解压安装包 - 支持 ZIP 和 TAR.GZ"""
|
"""解压安装包 - 支持 ZIP 和 TAR.GZ"""
|
||||||
temp_dir = tempfile.mkdtemp(prefix="jingrow_app_install_")
|
# 获取项目根目录
|
||||||
|
project_root = Path(__file__).resolve().parents[4]
|
||||||
|
tmp_dir = project_root / "tmp"
|
||||||
|
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 创建唯一临时目录
|
||||||
|
temp_dir_name = f"app_install_{uuid.uuid4().hex[:8]}"
|
||||||
|
temp_dir = tmp_dir / temp_dir_name
|
||||||
|
temp_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# 判断文件类型
|
# 判断文件类型
|
||||||
if zip_path.endswith('.tar.gz') or zip_path.endswith('.tgz') or zip_path.endswith('.gz'):
|
if zip_path.endswith('.tar.gz') or zip_path.endswith('.tgz') or zip_path.endswith('.gz'):
|
||||||
@ -57,7 +65,7 @@ def extract_package(zip_path: str) -> Dict[str, Any]:
|
|||||||
else:
|
else:
|
||||||
return {'success': False, 'error': '不支持的文件格式'}
|
return {'success': False, 'error': '不支持的文件格式'}
|
||||||
|
|
||||||
return {'success': True, 'temp_dir': temp_dir}
|
return {'success': True, 'temp_dir': str(temp_dir)}
|
||||||
|
|
||||||
|
|
||||||
@handle_errors
|
@handle_errors
|
||||||
@ -74,7 +82,8 @@ def analyze_package(temp_dir: str) -> Dict[str, Any]:
|
|||||||
'has_backend': False,
|
'has_backend': False,
|
||||||
'has_frontend': False,
|
'has_frontend': False,
|
||||||
'backend_files': [],
|
'backend_files': [],
|
||||||
'frontend_files': []
|
'frontend_files': [],
|
||||||
|
'root_dir': root_dir # 保存根目录路径
|
||||||
}
|
}
|
||||||
|
|
||||||
# 检查配置文件
|
# 检查配置文件
|
||||||
@ -98,7 +107,8 @@ def analyze_package(temp_dir: str) -> Dict[str, Any]:
|
|||||||
for root, dirs, files in os.walk(root_dir):
|
for root, dirs, files in os.walk(root_dir):
|
||||||
for file in files:
|
for file in files:
|
||||||
rel_path = os.path.relpath(os.path.join(root, file), root_dir)
|
rel_path = os.path.relpath(os.path.join(root, file), root_dir)
|
||||||
if file.endswith(('.py', '.txt', '.cfg')):
|
|
||||||
|
if file.endswith(('.py', '.txt', '.cfg', '.json', '.md', '.LICENSE', '.yaml', '.yml')):
|
||||||
package_info['backend_files'].append(rel_path)
|
package_info['backend_files'].append(rel_path)
|
||||||
package_info['has_backend'] = True
|
package_info['has_backend'] = True
|
||||||
elif file.endswith(('.vue', '.ts', '.js', '.css')):
|
elif file.endswith(('.vue', '.ts', '.js', '.css')):
|
||||||
@ -125,27 +135,49 @@ def install_files(temp_dir: str, app_name: str, package_info: Dict[str, Any], is
|
|||||||
backend_dir.mkdir(parents=True, exist_ok=True)
|
backend_dir.mkdir(parents=True, exist_ok=True)
|
||||||
frontend_dir.mkdir(parents=True, exist_ok=True)
|
frontend_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 获取根目录
|
||||||
|
root_dir = package_info.get('root_dir', temp_dir)
|
||||||
copied_files = []
|
copied_files = []
|
||||||
|
|
||||||
# 复制后端文件
|
# 复制后端文件
|
||||||
if package_info.get('has_backend', False):
|
if package_info.get('has_backend', False):
|
||||||
for file_path in package_info.get('backend_files', []):
|
for file_path in package_info.get('backend_files', []):
|
||||||
src_path = os.path.join(temp_dir, file_path)
|
# file_path 是相对于 root_dir 的路径
|
||||||
dst_path = backend_dir / file_path
|
# 例如:jin/hooks.py
|
||||||
|
# root_dir 是 tmp/app_install_xxx/jin
|
||||||
|
# 所以 src_path = tmp/app_install_xxx/jin/jin/hooks.py
|
||||||
|
src_path = os.path.join(root_dir, file_path)
|
||||||
|
|
||||||
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
# dst_path 需要去掉 app_name 前缀
|
||||||
shutil.copy2(src_path, dst_path)
|
if file_path.startswith(f"{app_name}/"):
|
||||||
copied_files.append(str(dst_path))
|
dst_path = backend_dir / file_path[len(app_name) + 1:]
|
||||||
|
else:
|
||||||
|
dst_path = backend_dir / file_path
|
||||||
|
|
||||||
|
if os.path.exists(src_path):
|
||||||
|
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(src_path, dst_path)
|
||||||
|
copied_files.append(str(dst_path))
|
||||||
|
else:
|
||||||
|
log_error(f"源文件不存在: {src_path}, root_dir={root_dir}, file_path={file_path}")
|
||||||
|
|
||||||
# 复制前端文件
|
# 复制前端文件
|
||||||
if package_info.get('has_frontend', False):
|
if package_info.get('has_frontend', False):
|
||||||
for file_path in package_info.get('frontend_files', []):
|
for file_path in package_info.get('frontend_files', []):
|
||||||
src_path = os.path.join(temp_dir, file_path)
|
src_path = os.path.join(root_dir, file_path)
|
||||||
dst_path = frontend_dir / file_path
|
|
||||||
|
|
||||||
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
# dst_path 需要去掉 app_name 前缀
|
||||||
shutil.copy2(src_path, dst_path)
|
if file_path.startswith(f"{app_name}/"):
|
||||||
copied_files.append(str(dst_path))
|
dst_path = frontend_dir / file_path[len(app_name) + 1:]
|
||||||
|
else:
|
||||||
|
dst_path = frontend_dir / file_path
|
||||||
|
|
||||||
|
if os.path.exists(src_path):
|
||||||
|
dst_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(src_path, dst_path)
|
||||||
|
copied_files.append(str(dst_path))
|
||||||
|
else:
|
||||||
|
log_error(f"源文件不存在: {src_path}, root_dir={root_dir}, file_path={file_path}")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'success': True,
|
'success': True,
|
||||||
@ -245,8 +277,15 @@ def install_app(uploaded_file_path: str, app_name: str = None) -> Dict[str, Any]
|
|||||||
log_info(f"开始安装应用包: {uploaded_file_path}")
|
log_info(f"开始安装应用包: {uploaded_file_path}")
|
||||||
|
|
||||||
# 验证文件
|
# 验证文件
|
||||||
if not os.path.exists(uploaded_file_path) or not uploaded_file_path.lower().endswith('.zip'):
|
if not os.path.exists(uploaded_file_path):
|
||||||
return {'success': False, 'error': '无效的ZIP文件'}
|
return {'success': False, 'error': '文件不存在'}
|
||||||
|
|
||||||
|
filename_lower = uploaded_file_path.lower()
|
||||||
|
if not (filename_lower.endswith('.zip') or
|
||||||
|
filename_lower.endswith('.tar.gz') or
|
||||||
|
filename_lower.endswith('.tgz') or
|
||||||
|
filename_lower.endswith('.gz')):
|
||||||
|
return {'success': False, 'error': '不支持的文件格式,支持ZIP、TAR.GZ、TGZ、GZ格式'}
|
||||||
|
|
||||||
# 解压文件
|
# 解压文件
|
||||||
extract_result = extract_package(uploaded_file_path)
|
extract_result = extract_package(uploaded_file_path)
|
||||||
@ -259,21 +298,35 @@ def install_app(uploaded_file_path: str, app_name: str = None) -> Dict[str, Any]
|
|||||||
analyze_result = analyze_package(temp_dir)
|
analyze_result = analyze_package(temp_dir)
|
||||||
if not analyze_result.get('success'):
|
if not analyze_result.get('success'):
|
||||||
cleanup_temp_dir(temp_dir)
|
cleanup_temp_dir(temp_dir)
|
||||||
|
log_error(f"分析包失败: {analyze_result.get('error')}")
|
||||||
return analyze_result
|
return analyze_result
|
||||||
|
|
||||||
package_info = analyze_result['data']
|
package_info = analyze_result['data']
|
||||||
|
log_info(f"包信息: {package_info}")
|
||||||
|
|
||||||
# 确定应用名称
|
# 确定应用名称
|
||||||
if not app_name:
|
if not app_name:
|
||||||
app_name = package_info.get('app_name')
|
app_name = package_info.get('app_name')
|
||||||
if not app_name:
|
if not app_name:
|
||||||
cleanup_temp_dir(temp_dir)
|
cleanup_temp_dir(temp_dir)
|
||||||
|
log_error(f"无法识别应用名称,包信息: {package_info}")
|
||||||
return {'success': False, 'error': '无法识别应用名称'}
|
return {'success': False, 'error': '无法识别应用名称'}
|
||||||
|
|
||||||
# 检查是否已安装
|
log_info(f"应用名称: {app_name}")
|
||||||
|
|
||||||
|
# 如果应用已安装,先删除旧版本(允许覆盖安装)
|
||||||
if is_app_installed(app_name):
|
if is_app_installed(app_name):
|
||||||
cleanup_temp_dir(temp_dir)
|
log_info(f"应用 {app_name} 已存在,将覆盖安装")
|
||||||
return {'success': False, 'error': f'应用 {app_name} 已安装'}
|
apps_dir, _ = get_app_directories()
|
||||||
|
app_dir = apps_dir / app_name
|
||||||
|
try:
|
||||||
|
if app_dir.exists():
|
||||||
|
shutil.rmtree(app_dir)
|
||||||
|
log_info(f"已删除旧版本应用: {app_dir}")
|
||||||
|
except Exception as e:
|
||||||
|
cleanup_temp_dir(temp_dir)
|
||||||
|
log_error(f"删除旧应用失败: {e}")
|
||||||
|
return {'success': False, 'error': f'删除旧应用失败: {str(e)}'}
|
||||||
|
|
||||||
# 安装后端文件
|
# 安装后端文件
|
||||||
backend_result = None
|
backend_result = None
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user