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)}")
+