feat: 添加CORS支持,优化API响应,重构工作流管理逻辑,支持本地存储设置

This commit is contained in:
iHeyTang
2025-08-11 15:24:42 +08:00
parent e3dfffad7d
commit 745319962c
5 changed files with 338 additions and 277 deletions

View File

@@ -13,7 +13,6 @@ CONFIG_FILE = os.path.join(NODE_DIR, "config.json")
# 默认配置
DEFAULT_CONFIG = {
"api_url": "",
"host": ""
}
@@ -75,7 +74,11 @@ class WorkflowPublisherNode:
async def get_publisher_settings(request):
"""获取发布器设置"""
config = load_config()
return web.json_response(config)
response = web.json_response(config)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
@PromptServer.instance.routes.post("/publisher/settings")
@@ -83,15 +86,21 @@ async def save_publisher_settings(request):
"""保存发布器设置"""
try:
data = await request.json()
api_url = data.get("api_url", "")
host = data.get("host", "")
config = load_config()
config["api_url"] = api_url
config["host"] = host
save_config(config)
return web.json_response({"status": "success", "message": "Settings saved"})
response = web.json_response({"status": "success", "message": "Settings saved"})
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
except Exception as e:
return web.json_response({"status": "error", "message": str(e)}, status=500)
response = web.json_response({"status": "error", "message": str(e)}, status=500)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
@PromptServer.instance.routes.post("/publisher/publish")
@@ -103,22 +112,24 @@ async def publish_workflow_handler(request):
workflow_name = data.get("name")
if not workflow or not workflow_name:
return web.json_response({"status": "error", "message": "Missing workflow data or name"}, status=400)
response = web.json_response({"status": "error", "message": "Missing workflow data or name"}, status=400)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
config = load_config()
target_api_url = config.get("api_url")
host = config.get("host", "")
if not target_api_url:
return web.json_response({"status": "error", "message": "API URL not configured"}, status=400)
if not host:
response = web.json_response({"status": "error", "message": "Host not configured"}, status=400)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
# 构建完整的URL
if host and not target_api_url.startswith(('http://', 'https://')):
# 如果提供了host且api_url不是完整URL则组合它们
full_url = host.rstrip('/') + '/' + target_api_url.lstrip('/')
else:
# 否则直接使用api_url
full_url = target_api_url
# 构建完整的URL - 直接使用 host + /api/workflow
full_url = host.rstrip('/') + '/api/workflow'
# 准备要发送到目标API的数据
payload = {
@@ -133,19 +144,31 @@ async def publish_workflow_handler(request):
async with session.post(full_url, json=payload, headers=headers) as response:
response_text = await response.text()
if response.status == 200 or response.status == 201:
return web.json_response(
result_response = web.json_response(
{"status": "success", "message": "Workflow published successfully!", "details": response_text})
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
else:
return web.json_response({
result_response = web.json_response({
"status": "error",
"message": f"Failed to publish workflow. Target API returned status {response.status}",
"details": response_text
}, status=500)
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
except Exception as e:
import traceback
traceback.print_exc()
return web.json_response({"status": "error", "message": str(e)}, status=500)
result_response = web.json_response({"status": "error", "message": str(e)}, status=500)
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
@PromptServer.instance.routes.get("/publisher/workflows")
@@ -153,29 +176,31 @@ async def get_workflows_from_server(request):
"""从目标服务器获取工作流列表,并按基础名称进行分组"""
try:
config = load_config()
target_api_url = config.get("api_url")
host = config.get("host", "")
if not target_api_url:
return web.json_response({"status": "error", "message": "API URL not configured"}, status=400)
if not host:
response = web.json_response({"status": "error", "message": "Host not configured"}, status=400)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
# 构建完整的URL
if host and not target_api_url.startswith(('http://', 'https://')):
# 如果提供了host且api_url不是完整URL则组合它们
full_url = host.rstrip('/') + '/' + target_api_url.lstrip('/')
else:
# 否则直接使用api_url
full_url = target_api_url
# 构建完整的URL - 直接使用 host + /api/workflow
full_url = host.rstrip('/') + '/api/workflow'
async with aiohttp.ClientSession() as session:
async with session.get(full_url) as response:
if response.status != 200:
response_text = await response.text()
return web.json_response({
result_response = web.json_response({
"status": "error",
"message": f"Failed to fetch workflows. Target API returned status {response.status}",
"details": response_text
}, status=500)
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
workflows = await response.json()
@@ -206,12 +231,20 @@ async def get_workflows_from_server(request):
for base_name in grouped_workflows:
grouped_workflows[base_name].sort(key=lambda x: x['version'], reverse=True)
return web.json_response(grouped_workflows)
result_response = web.json_response(grouped_workflows)
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
except Exception as e:
import traceback
traceback.print_exc()
return web.json_response({"status": "error", "message": str(e)}, status=500)
result_response = web.json_response({"status": "error", "message": str(e)}, status=500)
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
@PromptServer.instance.routes.post("/publisher/workflow/delete")
@@ -221,22 +254,24 @@ async def delete_workflow_version(request):
data = await request.json()
workflow_name = data.get("name")
if not workflow_name:
return web.json_response({"status": "error", "message": "Missing workflow name"}, status=400)
response = web.json_response({"status": "error", "message": "Missing workflow name"}, status=400)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
config = load_config()
target_api_url = config.get("api_url")
host = config.get("host", "")
if not target_api_url:
return web.json_response({"status": "error", "message": "API URL not configured"}, status=400)
if not host:
response = web.json_response({"status": "error", "message": "Host not configured"}, status=400)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
# 构建完整的URL
if host and not target_api_url.startswith(('http://', 'https://')):
# 如果提供了host且api_url不是完整URL则组合它们
base_url = host.rstrip('/') + '/' + target_api_url.lstrip('/')
else:
# 否则直接使用api_url
base_url = target_api_url
# 构建完整的URL - 直接使用 host + /api/workflow
base_url = host.rstrip('/') + '/api/workflow'
# 构建目标URL需要对工作流名称进行URL编码
delete_url = f"{base_url}/{urllib.parse.quote(workflow_name)}"
@@ -244,16 +279,40 @@ async def delete_workflow_version(request):
async with aiohttp.ClientSession() as session:
async with session.delete(delete_url) as response:
if response.status == 200:
return web.json_response({"status": "success", "message": "Workflow version deleted"})
result_response = web.json_response({"status": "success", "message": "Workflow version deleted"})
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
else:
details = await response.text()
return web.json_response({
result_response = web.json_response({
"status": "error",
"message": f"Target API failed to delete. Status: {response.status}",
"details": details
}, status=response.status)
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
except Exception as e:
return web.json_response({"status": "error", "message": str(e)}, status=500)
result_response = web.json_response({"status": "error", "message": str(e)}, status=500)
result_response.headers['Access-Control-Allow-Origin'] = '*'
result_response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
result_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return result_response
# 添加OPTIONS请求处理用于CORS预检请求
@PromptServer.instance.routes.options("/publisher/{path:.*}")
async def handle_options(request):
"""处理CORS预检请求"""
response = web.Response()
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
response.headers['Access-Control-Max-Age'] = '86400' # 24小时
return response
# -----------------
# ComfyUI 注册