实现一键上传安装扩展包到jingrow数据库的功能
This commit is contained in:
parent
d3465d8210
commit
d26c797624
@ -19,7 +19,7 @@
|
||||
ref="uploadRef"
|
||||
:file-list="fileList"
|
||||
:max="1"
|
||||
accept=".zip"
|
||||
accept=".zip,.tar.gz,.tgz,.gz"
|
||||
:show-file-list="false"
|
||||
:on-before-upload="beforeUpload"
|
||||
:on-change="handleFileChange"
|
||||
@ -32,17 +32,17 @@
|
||||
<Icon icon="tabler:cloud-upload" />
|
||||
</n-icon>
|
||||
<n-text style="font-size: 16px">
|
||||
{{ uploading ? t('Uploading...') : t('Click or drag ZIP file to this area to upload') }}
|
||||
{{ uploading ? t('Uploading...') : t('Click or drag package file to this area to upload') }}
|
||||
</n-text>
|
||||
<n-p depth="3" style="margin: 8px 0 0 0">
|
||||
{{ t('Support for ZIP format only') }}
|
||||
{{ t('Support for ZIP, TAR.GZ, and GZ format') }}
|
||||
</n-p>
|
||||
</div>
|
||||
</n-upload-dragger>
|
||||
</n-upload>
|
||||
|
||||
<!-- 应用名称输入 -->
|
||||
<div class="app-name-input" v-if="fileList.length > 0">
|
||||
<!-- 应用名称输入 - 仅普通应用包需要 -->
|
||||
<div class="app-name-input" v-if="fileList.length > 0 && !isExtensionFile">
|
||||
<n-form-item :label="t('App Name (Optional)')">
|
||||
<n-input
|
||||
v-model:value="appName"
|
||||
@ -128,6 +128,7 @@ const installProgress = ref(0)
|
||||
const installMessage = ref('')
|
||||
const installStatus = ref<'success' | 'error' | 'info'>('info')
|
||||
const showProgressModal = ref(false)
|
||||
const isExtensionFile = ref(false) // 是否是扩展包
|
||||
|
||||
// 本地App相关
|
||||
const localApps = ref<any[]>([])
|
||||
@ -180,8 +181,12 @@ const beforeUpload = (data: { file: UploadFileInfo }) => {
|
||||
const file = data.file.file
|
||||
if (!file) return false
|
||||
|
||||
if (!file.name.toLowerCase().endsWith('.zip')) {
|
||||
message.error(t('Only ZIP files are supported'))
|
||||
const fileName = file.name.toLowerCase()
|
||||
const allowedExtensions = ['.zip', '.tar.gz', '.tgz', '.gz']
|
||||
const isValidFile = allowedExtensions.some(ext => fileName.endsWith(ext))
|
||||
|
||||
if (!isValidFile) {
|
||||
message.error(t('Only ZIP and TAR.GZ files are supported'))
|
||||
return false
|
||||
}
|
||||
|
||||
@ -190,6 +195,19 @@ const beforeUpload = (data: { file: UploadFileInfo }) => {
|
||||
|
||||
const handleFileChange = (options: { fileList: UploadFileInfo[] }) => {
|
||||
fileList.value = options.fileList
|
||||
|
||||
// 检测是否是扩展包
|
||||
if (fileList.value.length > 0) {
|
||||
const fileName = fileList.value[0].file?.name || ''
|
||||
isExtensionFile.value = fileName.toLowerCase().endsWith('.tar.gz') ||
|
||||
fileName.toLowerCase().endsWith('.tgz') ||
|
||||
fileName.toLowerCase().endsWith('.gz')
|
||||
|
||||
// 如果是扩展包,清空应用名称(因为不需要)
|
||||
if (isExtensionFile.value) {
|
||||
appName.value = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const customUpload = async (_options: any) => {
|
||||
@ -209,6 +227,92 @@ const startUpload = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
// 判断文件类型,选择对应的 API endpoint
|
||||
const fileName = file.name.toLowerCase()
|
||||
const isExtensionPackage = fileName.endsWith('.tar.gz') || fileName.endsWith('.tgz') || fileName.endsWith('.gz')
|
||||
|
||||
// 如果是一键安装的扩展包,直接调用 install_package
|
||||
if (isExtensionPackage) {
|
||||
try {
|
||||
uploading.value = true
|
||||
installing.value = true
|
||||
showProgressModal.value = true
|
||||
installProgress.value = 0
|
||||
installMessage.value = t('Uploading file...')
|
||||
installStatus.value = 'info'
|
||||
|
||||
// 先上传文件保存到 public/files
|
||||
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 // 60秒超时
|
||||
})
|
||||
|
||||
console.log('Save response:', saveResponse.data)
|
||||
|
||||
if (!saveResponse.data.success) {
|
||||
throw new Error(saveResponse.data.error || t('Failed to save package'))
|
||||
}
|
||||
|
||||
const fileUrl = saveResponse.data.file_url
|
||||
console.log('File URL:', fileUrl)
|
||||
|
||||
installProgress.value = 50
|
||||
installMessage.value = t('Installing package...')
|
||||
|
||||
// 第二步:调用 install_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'
|
||||
|
||||
console.error('Upload error:', error)
|
||||
console.error('Error response data:', error.response?.data)
|
||||
|
||||
const errorMsg = error.response?.data?.detail ||
|
||||
error.response?.data?.error ||
|
||||
error.message ||
|
||||
t('Upload failed')
|
||||
|
||||
console.error('Final error message:', errorMsg)
|
||||
message.error(errorMsg)
|
||||
} finally {
|
||||
uploading.value = false
|
||||
installing.value = false
|
||||
}
|
||||
|
||||
return // 扩展包使用特殊流程,提前返回
|
||||
}
|
||||
|
||||
const apiEndpoint = '/jingrow/install/upload'
|
||||
|
||||
try {
|
||||
uploading.value = true
|
||||
installing.value = true
|
||||
@ -220,15 +324,16 @@ const startUpload = async () => {
|
||||
// 创建FormData
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
if (appName.value.trim()) {
|
||||
if (appName.value.trim() && !isExtensionPackage) {
|
||||
// 只有普通应用包才需要 app_name 参数
|
||||
formData.append('app_name', appName.value.trim())
|
||||
}
|
||||
|
||||
installProgress.value = 30
|
||||
installMessage.value = t('Uploading file...')
|
||||
installMessage.value = isExtensionPackage ? t('Installing extension package...') : t('Uploading file...')
|
||||
|
||||
// 上传文件
|
||||
const response = await axios.post('/jingrow/install/upload', formData, {
|
||||
const response = await axios.post(apiEndpoint, formData, {
|
||||
headers: {
|
||||
...get_session_api_headers(),
|
||||
'Content-Type': 'multipart/form-data'
|
||||
@ -249,7 +354,8 @@ const startUpload = async () => {
|
||||
// 清空文件列表
|
||||
clearFiles()
|
||||
|
||||
message.success(t('App \'{0}\' installed successfully').replace('{0}', response.data.app_name))
|
||||
const resultAppName = isExtensionPackage ? response.data.package_name : response.data.app_name
|
||||
message.success(t('Package \'{0}\' installed successfully').replace('{0}', resultAppName))
|
||||
} else {
|
||||
throw new Error(response.data.error || t('Installation failed'))
|
||||
}
|
||||
@ -260,7 +366,17 @@ const startUpload = async () => {
|
||||
installStatus.value = 'error'
|
||||
|
||||
console.error('Upload error:', error)
|
||||
message.error(error.response?.data?.detail || error.message || t('Upload failed'))
|
||||
console.error('Error response:', error.response)
|
||||
console.error('Error details:', error.response?.data)
|
||||
|
||||
// 更详细的错误信息
|
||||
const errorDetail = error.response?.data?.detail ||
|
||||
error.response?.data?.message ||
|
||||
error.message ||
|
||||
t('Upload failed')
|
||||
|
||||
console.error('Final error detail:', errorDetail)
|
||||
message.error(errorDetail)
|
||||
} finally {
|
||||
uploading.value = false
|
||||
installing.value = false
|
||||
@ -270,6 +386,7 @@ const startUpload = async () => {
|
||||
const clearFiles = () => {
|
||||
fileList.value = []
|
||||
appName.value = ''
|
||||
isExtensionFile.value = false
|
||||
uploadRef.value?.clear()
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import json
|
||||
import shutil
|
||||
import re
|
||||
|
||||
from jingrow.utils.app_installer import install_app, get_installed_apps as get_apps, get_app_directories
|
||||
from jingrow.utils.app_installer import install_app, get_installed_apps as get_apps, get_app_directories, install_extension_package
|
||||
from jingrow.utils.jingrow_api import log_info, log_error
|
||||
from jingrow.utils.auth import get_jingrow_cloud_url, get_jingrow_cloud_api_headers, get_jingrow_cloud_api_url, get_jingrow_api_headers
|
||||
from jingrow.config import Config
|
||||
@ -419,6 +419,92 @@ async def get_app_info(request: Request, app_name: str):
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
async def _install_extension_package_in_api(package_path: str, original_filename: str) -> Dict[str, Any]:
|
||||
"""保存扩展包并立即安装"""
|
||||
import shutil
|
||||
import requests
|
||||
|
||||
try:
|
||||
log_info(f"保存扩展包文件: {original_filename}")
|
||||
|
||||
# 复制文件到 public/files 目录
|
||||
# 从 jingrow-framework/apps/jingrow/jingrow/api/local_app_installer.py
|
||||
# 到 /home/jingrow/jingrow-bench/sites/test001/public/files/
|
||||
current = Path(__file__).resolve()
|
||||
|
||||
# current: /home/dev/jingrow-framework/apps/jingrow/jingrow/api/local_app_installer.py
|
||||
# 需要回到 /home/jingrow/jingrow-bench
|
||||
|
||||
# 如果当前在 framework 目录,则使用相对路径找到 jingrow-bench
|
||||
if 'jingrow-framework' in str(current):
|
||||
# 从 framework 目录回到 jingrow-bench
|
||||
jingrow_bench_path = Path('/home/jingrow/jingrow-bench')
|
||||
else:
|
||||
# 从 apps/jingrow/... 到 jingrow-bench
|
||||
project_root = current.parents[6] if current.parts.count('apps') > 1 else current.parents[5]
|
||||
jingrow_bench_path = project_root
|
||||
|
||||
target_dir = jingrow_bench_path / "sites" / "test001" / "public" / "files"
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
target_path = target_dir / original_filename
|
||||
|
||||
# 复制文件
|
||||
shutil.copy2(package_path, target_path)
|
||||
|
||||
log_info(f"文件已保存到: {target_path}")
|
||||
|
||||
# 立即调用 jlocal API 安装扩展包
|
||||
try:
|
||||
from jingrow.utils.jingrow_api import get_jingrow_api_headers
|
||||
headers = get_jingrow_api_headers()
|
||||
|
||||
# 调用 jlocal.install_package
|
||||
api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.install_package"
|
||||
|
||||
file_url = f"/files/{original_filename}"
|
||||
response = requests.post(
|
||||
api_url,
|
||||
json={'package_file_url': file_url},
|
||||
headers=headers,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('message', {}).get('success'):
|
||||
log_info(f"扩展包安装成功: {result.get('message', {}).get('package_name')}")
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'扩展包安装成功',
|
||||
'package_name': result.get('message', {}).get('package_name'),
|
||||
'file_url': file_url,
|
||||
'imported_files': result.get('message', {}).get('imported_files', []),
|
||||
'file_count': result.get('message', {}).get('file_count', 0)
|
||||
}
|
||||
else:
|
||||
error_msg = result.get('message', {}).get('error', '未知错误')
|
||||
log_error(f"安装失败: {error_msg}")
|
||||
return {'success': False, 'error': error_msg}
|
||||
else:
|
||||
log_error(f"API调用失败: HTTP {response.status_code}")
|
||||
return {'success': False, 'error': f'API调用失败: HTTP {response.status_code}'}
|
||||
|
||||
except Exception as api_error:
|
||||
log_error(f"调用安装API失败: {str(api_error)}")
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'扩展包已保存到 public/files 目录',
|
||||
'file_url': f'/files/{original_filename}',
|
||||
'file_path': str(target_path),
|
||||
'note': '文件已上传,请手动在 jingrow 应用中使用 Package Import 功能导入'
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
log_error(f"保存扩展包失败: {str(e)}")
|
||||
return {'success': False, 'error': f'保存文件失败: {str(e)}'}
|
||||
|
||||
|
||||
async def _remove_from_database(app_name: str) -> Dict[str, Any]:
|
||||
"""从数据库中删除应用记录"""
|
||||
try:
|
||||
@ -555,6 +641,105 @@ async def get_app_meta():
|
||||
raise HTTPException(status_code=500, detail=f"获取元数据失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/jingrow/install-extension")
|
||||
async def install_extension_package_api(request: Request, file: UploadFile = File(...)):
|
||||
"""安装扩展包到数据库"""
|
||||
temp_file_path = None
|
||||
try:
|
||||
log_info(f"开始处理上传的扩展包: {file.filename}")
|
||||
|
||||
# 验证文件类型
|
||||
if not file.filename:
|
||||
raise HTTPException(status_code=400, detail="文件名不能为空")
|
||||
|
||||
filename_lower = file.filename.lower()
|
||||
if not filename_lower.endswith(('.tar.gz', '.tgz', '.gz')):
|
||||
raise HTTPException(status_code=400, detail=f"只支持TAR.GZ格式的扩展包,当前文件: {filename_lower}")
|
||||
|
||||
# 保存上传文件到临时目录
|
||||
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.tar.gz')
|
||||
temp_file_path = temp_file.name
|
||||
try:
|
||||
content = await file.read()
|
||||
if not content:
|
||||
raise HTTPException(status_code=400, detail="文件内容为空")
|
||||
|
||||
temp_file.write(content)
|
||||
temp_file.close()
|
||||
|
||||
log_info(f"文件已保存到临时位置: {temp_file_path}, 大小: {len(content)} 字节")
|
||||
|
||||
# 保存文件到 jingrow-bench/sites/test001/public/files/
|
||||
target_dir = Path('/home/jingrow/jingrow-bench/sites/test001/public/files')
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
target_path = target_dir / file.filename
|
||||
|
||||
# 复制文件
|
||||
shutil.copy2(temp_file_path, target_path)
|
||||
|
||||
# 修复文件权限和所有者
|
||||
import pwd
|
||||
try:
|
||||
jingrow_user = pwd.getpwnam('jingrow')
|
||||
os.chown(target_path, jingrow_user.pw_uid, jingrow_user.pw_gid)
|
||||
os.chmod(target_path, 0o644)
|
||||
log_info(f"文件权限已修复: owner=jingrow, permissions=644")
|
||||
except Exception as perm_error:
|
||||
log_error(f"修复文件权限失败: {perm_error}")
|
||||
|
||||
log_info(f"文件已保存到: {target_path}")
|
||||
|
||||
# 调用 install_package API 安装
|
||||
from jingrow.utils.jingrow_api import get_jingrow_api_headers
|
||||
import requests
|
||||
|
||||
api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.install_package"
|
||||
headers = get_jingrow_api_headers()
|
||||
|
||||
file_url = f"/files/{file.filename}"
|
||||
response = requests.post(
|
||||
api_url,
|
||||
json={'package_file_url': file_url},
|
||||
headers=headers,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result_data = response.json()
|
||||
if result_data.get('message', {}).get('success'):
|
||||
log_info(f"扩展包安装成功: {result_data['message'].get('package_name')}")
|
||||
return {
|
||||
'success': True,
|
||||
'package_name': result_data['message']['package_name'],
|
||||
'file_url': file_url,
|
||||
'file_count': result_data['message'].get('file_count', 0)
|
||||
}
|
||||
else:
|
||||
error_msg = result_data.get('message', {}).get('error', '未知错误')
|
||||
log_error(f"安装失败: {error_msg}")
|
||||
raise HTTPException(status_code=400, detail=error_msg)
|
||||
else:
|
||||
log_error(f"API调用失败: HTTP {response.status_code}")
|
||||
raise HTTPException(status_code=500, detail=f'安装API调用失败: HTTP {response.status_code}')
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
if temp_file_path:
|
||||
try:
|
||||
os.unlink(temp_file_path)
|
||||
log_info(f"临时文件已删除: {temp_file_path}")
|
||||
except Exception as cleanup_error:
|
||||
log_error(f"清理临时文件失败: {cleanup_error}")
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_detail = traceback.format_exc()
|
||||
log_error(f"安装扩展包失败: {str(e)}\n{error_detail}")
|
||||
raise HTTPException(status_code=500, detail=f"安装扩展包失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/jingrow/upload-image")
|
||||
async def upload_image(file: UploadFile = File(...)):
|
||||
"""上传应用图片"""
|
||||
|
||||
@ -7,6 +7,7 @@ import json
|
||||
import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any
|
||||
from datetime import datetime
|
||||
@ -43,11 +44,18 @@ def get_app_directories():
|
||||
|
||||
@handle_errors
|
||||
def extract_package(zip_path: str) -> Dict[str, Any]:
|
||||
"""解压安装包"""
|
||||
"""解压安装包 - 支持 ZIP 和 TAR.GZ"""
|
||||
temp_dir = tempfile.mkdtemp(prefix="jingrow_app_install_")
|
||||
|
||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||||
zip_ref.extractall(temp_dir)
|
||||
# 判断文件类型
|
||||
if zip_path.endswith('.tar.gz') or zip_path.endswith('.tgz') or zip_path.endswith('.gz'):
|
||||
with tarfile.open(zip_path, 'r:gz') as tar_ref:
|
||||
tar_ref.extractall(temp_dir)
|
||||
elif zip_path.endswith('.zip'):
|
||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||||
zip_ref.extractall(temp_dir)
|
||||
else:
|
||||
return {'success': False, 'error': '不支持的文件格式'}
|
||||
|
||||
return {'success': True, 'temp_dir': temp_dir}
|
||||
|
||||
@ -303,3 +311,125 @@ def install_app(uploaded_file_path: str, app_name: str = None) -> Dict[str, Any]
|
||||
except Exception as e:
|
||||
log_error(f"安装应用失败: {str(e)}")
|
||||
return {'success': False, 'error': str(e)}
|
||||
|
||||
|
||||
def install_extension_package(package_path: str) -> Dict[str, Any]:
|
||||
"""
|
||||
直接安装扩展包到数据库(不走文件系统复制)
|
||||
|
||||
Args:
|
||||
package_path: 扩展包文件路径(tar.gz)
|
||||
|
||||
Returns:
|
||||
Dict: 安装结果
|
||||
"""
|
||||
try:
|
||||
log_info(f"开始安装扩展包: {package_path}")
|
||||
|
||||
# 验证文件
|
||||
if not os.path.exists(package_path):
|
||||
return {'success': False, 'error': '文件不存在'}
|
||||
|
||||
# 解压文件
|
||||
extract_result = extract_package(package_path)
|
||||
if not extract_result.get('success'):
|
||||
return extract_result
|
||||
|
||||
temp_dir = extract_result['temp_dir']
|
||||
|
||||
# 查找根目录(通常是包名)
|
||||
root_items = os.listdir(temp_dir)
|
||||
if not root_items:
|
||||
cleanup_temp_dir(temp_dir)
|
||||
return {'success': False, 'error': '压缩包为空'}
|
||||
|
||||
# 找到顶层目录
|
||||
root_dir = os.path.join(temp_dir, root_items[0]) if len(root_items) == 1 and os.path.isdir(os.path.join(temp_dir, root_items[0])) else temp_dir
|
||||
package_name = os.path.basename(root_dir)
|
||||
|
||||
# 查找 package.json 文件
|
||||
package_json_path = os.path.join(root_dir, f"{package_name}.json")
|
||||
if not os.path.exists(package_json_path):
|
||||
# 尝试查找所有json文件,找到pagetype为Package的
|
||||
for root, dirs, files in os.walk(root_dir):
|
||||
for file in files:
|
||||
if file.endswith('.json'):
|
||||
try:
|
||||
with open(os.path.join(root, file), 'r') as f:
|
||||
data = json.load(f)
|
||||
if data.get('pagetype') == 'Package':
|
||||
package_json_path = os.path.join(root, file)
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if os.path.exists(package_json_path):
|
||||
break
|
||||
|
||||
if not os.path.exists(package_json_path):
|
||||
cleanup_temp_dir(temp_dir)
|
||||
return {'success': False, 'error': '找不到 Package.json 文件'}
|
||||
|
||||
# 导入 Package
|
||||
try:
|
||||
import jingrow
|
||||
from jingrow.modules.import_file import import_pg, import_file_by_path
|
||||
from jingrow.model.sync import get_pg_files
|
||||
|
||||
# 确保 jingrow 环境已初始化
|
||||
if not hasattr(jingrow, 'db') or jingrow.db is None:
|
||||
cleanup_temp_dir(temp_dir)
|
||||
return {'success': False, 'error': 'jingrow 环境未初始化,请在 API 层调用'}
|
||||
|
||||
with open(package_json_path, 'r', encoding='utf-8') as f:
|
||||
pg_dict = json.load(f)
|
||||
|
||||
# 验证 Package 数据
|
||||
if not pg_dict.get('pagetype') == 'Package':
|
||||
cleanup_temp_dir(temp_dir)
|
||||
return {'success': False, 'error': f'无效的 Package 文件,pagetype 为: {pg_dict.get("pagetype")}'}
|
||||
|
||||
# 导入 Package 到数据库
|
||||
package_doc = import_pg(pg_dict, ignore_version=True)
|
||||
jingrow.flags.package = package_doc
|
||||
|
||||
# 收集所有 pagetype 文件
|
||||
files = []
|
||||
for module in os.listdir(root_dir):
|
||||
module_path = os.path.join(root_dir, module)
|
||||
if os.path.isdir(module_path):
|
||||
files = get_pg_files(files, module_path)
|
||||
|
||||
log_info(f"找到 {len(files)} 个 pagetype 文件待导入")
|
||||
|
||||
# 导入所有文件
|
||||
imported_files = []
|
||||
for file in files:
|
||||
try:
|
||||
import_file_by_path(file, force=True, ignore_version=True)
|
||||
imported_files.append(file)
|
||||
except Exception as e:
|
||||
log_error(f"导入文件失败 {file}: {str(e)}")
|
||||
# 继续导入其他文件,不中断
|
||||
|
||||
# 清理临时文件
|
||||
cleanup_temp_dir(temp_dir)
|
||||
|
||||
log_info(f"扩展包 {package_name} 安装成功,导入了 {len(imported_files)} 个文件")
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'扩展包 {package_name} 安装成功',
|
||||
'package_name': package_name,
|
||||
'imported_files': imported_files,
|
||||
'file_count': len(imported_files)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
cleanup_temp_dir(temp_dir)
|
||||
import traceback
|
||||
error_detail = traceback.format_exc()
|
||||
log_error(f"导入失败: {str(e)}")
|
||||
return {'success': False, 'error': f'导入失败: {str(e)}', 'detail': error_detail}
|
||||
|
||||
except Exception as e:
|
||||
log_error(f"安装扩展包失败: {str(e)}")
|
||||
return {'success': False, 'error': str(e)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user