diff --git a/apps/jingrow/frontend/src/app/router/index.ts b/apps/jingrow/frontend/src/app/router/index.ts index de0e33f..652a06e 100644 --- a/apps/jingrow/frontend/src/app/router/index.ts +++ b/apps/jingrow/frontend/src/app/router/index.ts @@ -156,6 +156,12 @@ const router = createRouter({ name: 'ToolDetail', component: () => import('../../views/dev/ToolDetail.vue') }, + { + path: 'publish-tool', + name: 'PublishTool', + component: () => import('../../views/dev/PublishTool.vue'), + meta: { requiresAuth: true } + }, { path: 'app-marketplace/:name', name: 'AppDetail', diff --git a/apps/jingrow/frontend/src/locales/zh-CN.json b/apps/jingrow/frontend/src/locales/zh-CN.json index c1e71cf..6f5e1bc 100644 --- a/apps/jingrow/frontend/src/locales/zh-CN.json +++ b/apps/jingrow/frontend/src/locales/zh-CN.json @@ -952,6 +952,7 @@ "Environment restart request submitted. The system will restart shortly.": "环境重启请求已提交,系统将在稍后重启。", "Failed to restart environment": "重启环境失败", "Publish App": "发布应用", + "Publish Tool": "发布工具", "My Published Apps": "已发布应用", "Manage your published applications in the marketplace": "管理您在应用市场中发布的应用", "View in Marketplace": "在市场查看", @@ -1064,6 +1065,7 @@ "Loading tools...": "加载工具中...", "Edit Tool": "编辑工具", "Tool Name": "工具名称", + "Tool name": "工具名称", "Enter tool name": "请输入工具名称", "Enter tool description": "请输入工具描述", "Enter category": "请输入分类", diff --git a/apps/jingrow/frontend/src/views/dev/PublishTool.vue b/apps/jingrow/frontend/src/views/dev/PublishTool.vue new file mode 100644 index 0000000..669289a --- /dev/null +++ b/apps/jingrow/frontend/src/views/dev/PublishTool.vue @@ -0,0 +1,552 @@ + + + + + + diff --git a/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue b/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue index 47b259a..85cddea 100644 --- a/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue +++ b/apps/jingrow/frontend/src/views/dev/ToolMarketplace.vue @@ -6,6 +6,12 @@

{{ t('Tool Marketplace') }}

{{ t('Browse and install tools from Jingrow Tool Marketplace') }}

+ + + {{ t('Publish Tool') }} + @@ -236,6 +242,10 @@ function handlePageSizeChange(newPageSize: number) { loadTools() } +function publishTool() { + router.push('/publish-tool') +} + function viewToolDetail(tool: any) { // 跳转到工具详情页面,传递返回路径 router.push({ diff --git a/apps/jingrow/jingrow/api/tools.py b/apps/jingrow/jingrow/api/tools.py index e82088b..1f10c3b 100644 --- a/apps/jingrow/jingrow/api/tools.py +++ b/apps/jingrow/jingrow/api/tools.py @@ -383,3 +383,79 @@ async def uninstall_tool(tool_name: str): logger.error(f"Traceback: {traceback.format_exc()}") raise HTTPException(status_code=500, detail=f"卸载工具失败: {str(e)}") + +@router.post("/jingrow/tool/publish") +async def publish_tool_to_marketplace( + tool_name: str = Form(...), + title: str = Form(...), + subtitle: Optional[str] = Form(None), + description: Optional[str] = Form(None), + category: Optional[str] = Form(None), + file_url: Optional[str] = Form(None), + repository_url: Optional[str] = Form(None), + tool_image: Optional[str] = Form(None), + icon: Optional[str] = Form(None), + color: Optional[str] = Form(None) +): + """ + 发布工具到Jingrow Cloud工具市场 + """ + try: + # 如果file_url不是绝对地址,拼接完整的服务器地址 + if file_url and not file_url.startswith('http://') and not file_url.startswith('https://'): + from jingrow.config import Config + # 确保URL以 / 开头 + if not file_url.startswith('/'): + file_url = '/' + file_url + + # 使用Config中的服务器URL + server_url = Config.jingrow_server_url + # 确保服务器URL不以 / 结尾 + server_url = server_url.rstrip('/') + file_url = f"{server_url}{file_url}" + + url = f"{get_jingrow_cloud_url()}/api/action/jcloud.api.jlocal.create_local_tool" + + headers = get_jingrow_cloud_api_headers() + headers['Content-Type'] = 'application/json' + + response = requests.post(url, json={ + "tool_data": { + "tool_name": tool_name, + "title": title, + "subtitle": subtitle or "", + "description": description or "", + "category": category or "", + "file_url": file_url, + "repository_url": repository_url, + "tool_image": tool_image, + "icon": icon, + "color": color + } + }, headers=headers, timeout=20) + + if response.status_code != 200: + error_detail = response.json().get('detail', f"HTTP {response.status_code}") if response.headers.get('content-type', '').startswith('application/json') else f"HTTP {response.status_code}" + raise HTTPException(status_code=response.status_code, detail=error_detail) + + result = response.json() + + # 检查错误 + if isinstance(result, dict) and result.get('error'): + raise HTTPException(status_code=400, detail=result['error']) + + message = result.get('message', {}) + if isinstance(message, dict) and message.get('error'): + raise HTTPException(status_code=400, detail=message['error']) + + # 成功响应 + tool_name_result = message.get('name', 'unknown') if isinstance(message, dict) else result.get('message', 'unknown') + return {"success": True, "message": f"工具发布成功,工具名称: {tool_name_result}"} + + except HTTPException: + raise + except Exception as e: + logger.error(f"发布工具失败: {str(e)}") + logger.error(f"Traceback: {traceback.format_exc()}") + raise HTTPException(status_code=500, detail=f"发布工具失败: {str(e)}") +