replace JSON file metadata reading with database queries

This commit is contained in:
jingrow 2025-11-21 18:05:38 +08:00
parent c01bdf9a41
commit 7d6db30662
3 changed files with 31 additions and 64 deletions

View File

@ -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: {

View File

@ -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(),

View File

@ -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()