style(api): 重构api架构
This commit is contained in:
@@ -9,7 +9,7 @@ from pretor.core.individual.supervisory_node.supervisory_node import Supervisory
|
|||||||
from pretor.core.individual.consciousness_node.consciousness_node import ConsciousnessNode
|
from pretor.core.individual.consciousness_node.consciousness_node import ConsciousnessNode
|
||||||
from pretor.core.individual.control_node.control_node import ControlNode
|
from pretor.core.individual.control_node.control_node import ControlNode
|
||||||
from pretor.core.workflow.workflow_runner import WorkflowRunningEngine
|
from pretor.core.workflow.workflow_runner import WorkflowRunningEngine
|
||||||
from pretor.core.api import PretorGateway
|
from pretor.api import PretorGateway
|
||||||
from ray import serve
|
from ray import serve
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
|
|||||||
@@ -12,3 +12,118 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from fastapi import FastAPI, WebSocket, Request
|
||||||
|
from fastapi.responses import FileResponse, JSONResponse
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from ray import serve
|
||||||
|
|
||||||
|
from .agent import agent_router
|
||||||
|
from .auth import auth_router
|
||||||
|
from .cluster import cluster_router
|
||||||
|
from .platform.frontend import client_router
|
||||||
|
from .provider import provider_router
|
||||||
|
from .resource import resource_router
|
||||||
|
from .workflow import workflow_router
|
||||||
|
from pretor.utils.error import (
|
||||||
|
DemandError, ModelNotExistError, UserError,
|
||||||
|
UserNotExistError, UserPasswordError, ProviderError,
|
||||||
|
ProviderNotExistError, WorkflowError, WorkflowExit
|
||||||
|
)
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
app.include_router(client_router) # 客户端路径
|
||||||
|
app.include_router(auth_router) # 用户路径
|
||||||
|
app.include_router(provider_router) # 供应商路径
|
||||||
|
app.include_router(resource_router) # 资源路径
|
||||||
|
app.include_router(cluster_router) # 集群信息路径
|
||||||
|
app.include_router(agent_router) # agent路径
|
||||||
|
app.include_router(workflow_router) # workflow路径
|
||||||
|
|
||||||
|
@app.exception_handler(UserNotExistError)
|
||||||
|
async def user_not_exist_handler(request: Request, exc: UserNotExistError):
|
||||||
|
return JSONResponse(status_code=404, content={"message": "用户不存在"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(UserPasswordError)
|
||||||
|
async def user_password_handler(request: Request, exc: UserPasswordError):
|
||||||
|
return JSONResponse(status_code=401, content={"message": "密码错误"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(UserError)
|
||||||
|
async def user_error_handler(request: Request, exc: UserError):
|
||||||
|
return JSONResponse(status_code=400, content={"message": "用户相关错误"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(ProviderNotExistError)
|
||||||
|
async def provider_not_exist_handler(request: Request, exc: ProviderNotExistError):
|
||||||
|
return JSONResponse(status_code=404, content={"message": "服务提供商不存在"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(ProviderError)
|
||||||
|
async def provider_error_handler(request: Request, exc: ProviderError):
|
||||||
|
return JSONResponse(status_code=400, content={"message": "服务提供商错误"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(ModelNotExistError)
|
||||||
|
async def model_not_exist_handler(request: Request, exc: ModelNotExistError):
|
||||||
|
return JSONResponse(status_code=404, content={"message": "模型不存在"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(DemandError)
|
||||||
|
async def demand_error_handler(request: Request, exc: DemandError):
|
||||||
|
return JSONResponse(status_code=400, content={"message": "需求格式错误或不满足"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(WorkflowExit)
|
||||||
|
async def workflow_exit_handler(request: Request, exc: WorkflowExit):
|
||||||
|
return JSONResponse(status_code=400, content={"message": "工作流已退出"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(WorkflowError)
|
||||||
|
async def workflow_error_handler(request: Request, exc: WorkflowError):
|
||||||
|
return JSONResponse(status_code=500, content={"message": "工作流执行错误"})
|
||||||
|
|
||||||
|
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
frontend_dir = os.path.join(base_dir, "frontend", "dist")
|
||||||
|
|
||||||
|
if os.path.exists(frontend_dir):
|
||||||
|
app.mount("/assets", StaticFiles(directory=os.path.join(frontend_dir, "assets")), name="assets")
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/favicon.svg", include_in_schema=False)
|
||||||
|
async def serve_favicon():
|
||||||
|
return FileResponse(os.path.join(frontend_dir, "favicon.svg"))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/icons.svg", include_in_schema=False)
|
||||||
|
async def serve_icons():
|
||||||
|
return FileResponse(os.path.join(frontend_dir, "icons.svg"))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/{full_path:path}", include_in_schema=False)
|
||||||
|
async def serve_frontend(full_path: str):
|
||||||
|
# 【重要安全修复】避免拦截不存在的 API 路由。如果是调用了不存在的 /api/ 接口,直接返回 404,不返回前端页面
|
||||||
|
if full_path.startswith("api/"):
|
||||||
|
return JSONResponse(status_code=404, content={"detail": "API endpoint not found"})
|
||||||
|
|
||||||
|
index_path = os.path.join(frontend_dir, "index.html")
|
||||||
|
if os.path.exists(index_path):
|
||||||
|
return FileResponse(index_path)
|
||||||
|
return JSONResponse(status_code=404, content={"detail": "Frontend build not found"})
|
||||||
|
else:
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.getLogger("pretor").warning(f"Frontend dist folder not found at {frontend_dir}. Skipping frontend mount.")
|
||||||
|
|
||||||
|
|
||||||
|
@serve.deployment
|
||||||
|
@serve.ingress(app)
|
||||||
|
class PretorGateway:
|
||||||
|
gateway: Dict[str, WebSocket]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.gateway = {}
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
# Copyright 2026 zhaoxi826
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
from typing import Dict
|
|
||||||
from fastapi import FastAPI, WebSocket, Request
|
|
||||||
from fastapi.staticfiles import StaticFiles
|
|
||||||
from fastapi.responses import FileResponse, JSONResponse
|
|
||||||
|
|
||||||
from pretor.api.platform.frontend import client_router
|
|
||||||
from pretor.api.auth import auth_router
|
|
||||||
from pretor.api.provider import provider_router
|
|
||||||
from pretor.api.resource import resource_router
|
|
||||||
from pretor.api.cluster import cluster_router
|
|
||||||
from pretor.api.agent import agent_router
|
|
||||||
from pretor.api.workflow import workflow_router
|
|
||||||
from pretor.utils.error import (
|
|
||||||
DemandError, ModelNotExistError, UserError,
|
|
||||||
UserNotExistError, UserPasswordError, ProviderError,
|
|
||||||
ProviderNotExistError, WorkflowError, WorkflowExit
|
|
||||||
)
|
|
||||||
|
|
||||||
from ray import serve
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
app.include_router(client_router) # 客户端路径
|
|
||||||
app.include_router(auth_router) # 用户路径
|
|
||||||
app.include_router(provider_router) # 供应商路径
|
|
||||||
app.include_router(resource_router) # 资源路径
|
|
||||||
app.include_router(cluster_router) # 集群信息路径
|
|
||||||
app.include_router(agent_router) # agent路径
|
|
||||||
app.include_router(workflow_router) # workflow路径
|
|
||||||
|
|
||||||
@app.exception_handler(UserNotExistError)
|
|
||||||
async def user_not_exist_handler(request: Request, exc: UserNotExistError):
|
|
||||||
return JSONResponse(status_code=404, content={"message": "用户不存在"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(UserPasswordError)
|
|
||||||
async def user_password_handler(request: Request, exc: UserPasswordError):
|
|
||||||
return JSONResponse(status_code=401, content={"message": "密码错误"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(UserError)
|
|
||||||
async def user_error_handler(request: Request, exc: UserError):
|
|
||||||
return JSONResponse(status_code=400, content={"message": "用户相关错误"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(ProviderNotExistError)
|
|
||||||
async def provider_not_exist_handler(request: Request, exc: ProviderNotExistError):
|
|
||||||
return JSONResponse(status_code=404, content={"message": "服务提供商不存在"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(ProviderError)
|
|
||||||
async def provider_error_handler(request: Request, exc: ProviderError):
|
|
||||||
return JSONResponse(status_code=400, content={"message": "服务提供商错误"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(ModelNotExistError)
|
|
||||||
async def model_not_exist_handler(request: Request, exc: ModelNotExistError):
|
|
||||||
return JSONResponse(status_code=404, content={"message": "模型不存在"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(DemandError)
|
|
||||||
async def demand_error_handler(request: Request, exc: DemandError):
|
|
||||||
return JSONResponse(status_code=400, content={"message": "需求格式错误或不满足"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(WorkflowExit)
|
|
||||||
async def workflow_exit_handler(request: Request, exc: WorkflowExit):
|
|
||||||
return JSONResponse(status_code=400, content={"message": "工作流已退出"})
|
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(WorkflowError)
|
|
||||||
async def workflow_error_handler(request: Request, exc: WorkflowError):
|
|
||||||
return JSONResponse(status_code=500, content={"message": "工作流执行错误"})
|
|
||||||
|
|
||||||
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
||||||
frontend_dir = os.path.join(base_dir, "frontend", "dist")
|
|
||||||
|
|
||||||
if os.path.exists(frontend_dir):
|
|
||||||
app.mount("/assets", StaticFiles(directory=os.path.join(frontend_dir, "assets")), name="assets")
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/favicon.svg", include_in_schema=False)
|
|
||||||
async def serve_favicon():
|
|
||||||
return FileResponse(os.path.join(frontend_dir, "favicon.svg"))
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/icons.svg", include_in_schema=False)
|
|
||||||
async def serve_icons():
|
|
||||||
return FileResponse(os.path.join(frontend_dir, "icons.svg"))
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/{full_path:path}", include_in_schema=False)
|
|
||||||
async def serve_frontend(full_path: str):
|
|
||||||
# 【重要安全修复】避免拦截不存在的 API 路由。如果是调用了不存在的 /api/ 接口,直接返回 404,不返回前端页面
|
|
||||||
if full_path.startswith("api/"):
|
|
||||||
return JSONResponse(status_code=404, content={"detail": "API endpoint not found"})
|
|
||||||
|
|
||||||
index_path = os.path.join(frontend_dir, "index.html")
|
|
||||||
if os.path.exists(index_path):
|
|
||||||
return FileResponse(index_path)
|
|
||||||
return JSONResponse(status_code=404, content={"detail": "Frontend build not found"})
|
|
||||||
else:
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logging.getLogger("pretor").warning(f"Frontend dist folder not found at {frontend_dir}. Skipping frontend mount.")
|
|
||||||
|
|
||||||
|
|
||||||
@serve.deployment
|
|
||||||
@serve.ingress(app)
|
|
||||||
class PretorGateway:
|
|
||||||
gateway: Dict[str, WebSocket]
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.gateway = {}
|
|
||||||
Reference in New Issue
Block a user