From 7d6db30662f472a379b5e008bc29f75bdb883892 Mon Sep 17 00:00:00 2001 From: jingrow Date: Fri, 21 Nov 2025 18:05:38 +0800 Subject: [PATCH] replace JSON file metadata reading with database queries --- .../frontend/src/views/dev/ToolDetail.vue | 1 + .../src/views/dev/ToolMarketplace.vue | 3 +- apps/jingrow/jingrow/api/tools.py | 91 ++++++------------- 3 files changed, 31 insertions(+), 64 deletions(-) diff --git a/apps/jingrow/frontend/src/views/dev/ToolDetail.vue b/apps/jingrow/frontend/src/views/dev/ToolDetail.vue index 31023bf..04dc024 100644 --- a/apps/jingrow/frontend/src/views/dev/ToolDetail.vue +++ b/apps/jingrow/frontend/src/views/dev/ToolDetail.vue @@ -255,6 +255,7 @@ async function performInstall() { installMessage.value = t('正在安装工具...') response = await axios.post('/jingrow/install-tool-from-url', new URLSearchParams({ + tool_name: tool.value.name || tool.value.tool_name, url: tool.value.file_url }), { headers: { diff --git a/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue b/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue index 72b57f9..027d923 100644 --- a/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue +++ b/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue @@ -275,7 +275,8 @@ async function performInstall(tool: any) { installMessage.value = t('正在安装工具...') response = await axios.post('/jingrow/install-tool-from-url', new URLSearchParams({ - url: tool.file_url + url: tool.file_url, + tool_name: tool.name || tool.tool_name }), { headers: { ...get_session_api_headers(), diff --git a/apps/jingrow/jingrow/api/tools.py b/apps/jingrow/jingrow/api/tools.py index 6f51fb4..fc2a8ce 100644 --- a/apps/jingrow/jingrow/api/tools.py +++ b/apps/jingrow/jingrow/api/tools.py @@ -129,9 +129,14 @@ async def get_tool_detail(name: str): @router.post("/jingrow/install-tool-from-file") -async def install_tool_from_file(file: UploadFile = File(...)): +async def install_tool_from_file(file: UploadFile = File(...), tool_name: str = Form(...)): """从上传的文件安装工具(支持ZIP,每个工具包独立)""" try: + # 从数据库获取工具元数据 + tool_data = await get_tool_detail(tool_name) + if not tool_data: + raise HTTPException(status_code=404, detail=f"工具 {tool_name} 不存在") + root = get_root_path() tmp_dir = root / "tmp" tmp_dir.mkdir(parents=True, exist_ok=True) @@ -144,8 +149,8 @@ async def install_tool_from_file(file: UploadFile = File(...)): with open(temp_file_path, 'wb') as f: shutil.copyfileobj(file.file, f) - # 安装工具 - result = _install_tool_from_file(str(temp_file_path)) + # 安装工具(传递工具元数据) + result = _install_tool_from_file(str(temp_file_path), tool_data) # 清理临时文件 if temp_file_path.exists(): @@ -153,6 +158,8 @@ async def install_tool_from_file(file: UploadFile = File(...)): return result + except HTTPException: + raise except Exception as e: logger.error(f"从文件安装工具失败: {str(e)}") logger.error(f"Traceback: {traceback.format_exc()}") @@ -160,9 +167,14 @@ async def install_tool_from_file(file: UploadFile = File(...)): @router.post("/jingrow/install-tool-from-url") -async def install_tool_from_url(url: str = Form(...)): +async def install_tool_from_url(url: str = Form(...), tool_name: str = Form(...)): """从URL安装工具""" try: + # 从数据库获取工具元数据 + tool_data = await get_tool_detail(tool_name) + if not tool_data: + raise HTTPException(status_code=404, detail=f"工具 {tool_name} 不存在") + root = get_root_path() tmp_dir = root / "tmp" tmp_dir.mkdir(parents=True, exist_ok=True) @@ -179,8 +191,8 @@ async def install_tool_from_url(url: str = Form(...)): for chunk in response.iter_content(chunk_size=8192): f.write(chunk) - # 安装工具 - result = _install_tool_from_file(str(temp_file_path)) + # 安装工具(传递工具元数据) + result = _install_tool_from_file(str(temp_file_path), tool_data) # 清理临时文件 if temp_file_path.exists(): @@ -188,13 +200,15 @@ async def install_tool_from_url(url: str = Form(...)): return result + except HTTPException: + raise except Exception as e: logger.error(f"从URL安装工具失败: {str(e)}") logger.error(f"Traceback: {traceback.format_exc()}") raise HTTPException(status_code=500, detail=f"安装工具失败: {str(e)}") -def _install_tool_from_file(file_path: str) -> Dict[str, Any]: +def _install_tool_from_file(file_path: str, tool_data: Dict[str, Any]) -> Dict[str, Any]: """从文件安装工具(支持ZIP,每个工具包独立)""" try: from jingrow.utils.app_installer import extract_package, cleanup_temp_dir @@ -207,28 +221,9 @@ def _install_tool_from_file(file_path: str) -> Dict[str, Any]: temp_dir = extract_result['temp_dir'] try: - # 查找工具定义文件 {tool_id}.json - # 工具包结构应该是: - # - {tool_id}.json(必需,在根目录,文件名与工具ID一致) - # - frontend/{tool_id}/{tool_id}.vue(前端组件) - # - backend/{tool_id}/{tool_id}.py(后端文件,可选) - - tool_json_path = None - - # 查找根目录下的 {tool_id}.json 文件 - # 遍历根目录,找到第一个 .json 文件(应该是工具ID命名的) - json_files = [f for f in Path(temp_dir).iterdir() - if f.is_file() and f.suffix == '.json' and not f.name.startswith('.')] - - if json_files: - # 使用第一个找到的 JSON 文件 - tool_json_path = json_files[0] - else: - return {'success': False, 'error': '压缩包中没有找到工具定义 JSON 文件(应为 {tool_id}.json)'} - - # 安装工具(每个包只包含一个工具) - tool_dir = tool_json_path.parent - result = _install_single_tool_directory(str(tool_dir)) + # 安装工具(传递工具元数据) + tool_dir = Path(temp_dir) + result = _install_single_tool_directory(str(tool_dir), tool_data) if result.get('success'): return { @@ -249,45 +244,15 @@ def _install_tool_from_file(file_path: str) -> Dict[str, Any]: return {'success': False, 'error': str(e)} -def _install_single_tool_directory(tool_dir: str) -> Dict[str, Any]: +def _install_single_tool_directory(tool_dir: str, tool_data: Dict[str, Any]) -> Dict[str, Any]: """安装单个工具目录(每个工具独立)""" try: tool_dir_path = Path(tool_dir) - # 读取工具定义文件 {tool_name}.json - # 查找 {tool_name}.json 文件 - json_files = [f for f in tool_dir_path.iterdir() - if f.is_file() and f.suffix == '.json' and not f.name.startswith('.')] - if not json_files: - return {'success': False, 'error': '找不到工具定义文件: 应为 {tool_name}.json'} - - tool_json = json_files[0] - - with open(tool_json, 'r', encoding='utf-8') as f: - tool_data = json.load(f) - - if not isinstance(tool_data, dict): - return {'success': False, 'error': '工具定义文件格式错误'} - - tool_name = tool_data.get('tool_name') + # 从数据库获取的元数据中提取 tool_name + tool_name = tool_data.get('tool_name') or tool_data.get('name') if not tool_name: - return {'success': False, 'error': '工具定义中缺少 tool_name'} - - # 验证 JSON 文件名必须与工具名称一致 - json_filename = tool_json.stem # 获取文件名(不含扩展名) - if json_filename != tool_name: - return {'success': False, 'error': f'工具定义文件名 {json_filename}.json 与工具名称 {tool_name} 不一致,必须使用 {tool_name}.json'} - - # 自动生成 routeName 和 routePath(如果未提供) - if tool_data.get('type') == 'route': - if not tool_data.get('routeName'): - tool_data['routeName'] = generate_route_name(tool_name) - if not tool_data.get('routePath'): - tool_data['routePath'] = generate_route_path(tool_name) - - # 将更新后的数据写回 JSON 文件 - with open(tool_json, 'w', encoding='utf-8') as f: - json.dump(tool_data, f, ensure_ascii=False, indent=2) + return {'success': False, 'error': '工具元数据中缺少 tool_name'} # 确定目标目录:apps/jingrow/frontend/src/views/tools/{tool_name} jingrow_root = get_jingrow_root()