wip: 修复了serve的部分bug
This commit is contained in:
parent
27a71c9e49
commit
dcf53524b2
|
|
@ -10,8 +10,6 @@ services:
|
|||
POSTGRES_DB: pretor
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d pretor"]
|
||||
interval: 5s
|
||||
|
|
@ -34,9 +32,6 @@ services:
|
|||
- POSTGRES_PORT=5432
|
||||
- POSTGRES_DB=pretor
|
||||
- SECRET_KEY=changethiskey12345
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/frontend/dist # Prevent local uncompiled frontend from overriding the built one
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
|
|
|||
12
main.py
12
main.py
|
|
@ -10,20 +10,26 @@ from pretor.core.individual.control_node.control_node import ControlNode
|
|||
from pretor.core.workflow.workflow_runner import WorkflowRunningEngine
|
||||
from pretor.core.api import PretorGateway
|
||||
from ray import serve
|
||||
import os
|
||||
|
||||
|
||||
|
||||
async def start_system():
|
||||
# 1. 初始化 Ray
|
||||
|
||||
db_host = os.getenv("POSTGRES_HOST", "db")
|
||||
env_vars = {
|
||||
"POSTGRES_USER": "postgres",
|
||||
"POSTGRES_PASSWORD": "postgres",
|
||||
"POSTGRES_HOST": "127.0.0.1",
|
||||
"POSTGRES_HOST": db_host,
|
||||
"POSTGRES_PORT": "5432",
|
||||
"POSTGRES_DB": "postgres",
|
||||
"SECRET_KEY": "yoursecretkey"
|
||||
}
|
||||
ray.init(ignore_reinit_error=True, runtime_env={"env_vars": env_vars})
|
||||
|
||||
ray.init(ignore_reinit_error=True,
|
||||
dashboard_host="0.0.0.0",
|
||||
dashboard_port=8265,
|
||||
runtime_env={"env_vars": env_vars})
|
||||
|
||||
|
||||
# 2. 启动数据库组件
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ async def update_cluster_state(websocket: WebSocket):
|
|||
nodes = ray.nodes()
|
||||
payload = [
|
||||
{
|
||||
"node_id": node["NodeID"],
|
||||
"node_name": node["NodeName"],
|
||||
"alive": node["Alive"],
|
||||
"resources": node["Resources"],
|
||||
"remaining": node["RemainingResources"]
|
||||
"node_id": node.get("NodeID"),
|
||||
"node_name": node.get("NodeName"),
|
||||
"alive": node.get("Alive"),
|
||||
"resources": node.get("Resources", {}),
|
||||
"remaining": node.get("RemainingResources", {})
|
||||
}
|
||||
for node in nodes
|
||||
]
|
||||
|
|
|
|||
|
|
@ -12,104 +12,116 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from typing import Dict
|
||||
from fastapi import FastAPI,WebSocket
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
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 fastapi.responses import JSONResponse
|
||||
from fastapi import Request
|
||||
from pretor.utils.error import (
|
||||
DemandError,
|
||||
ModelNotExistError,
|
||||
UserError,
|
||||
UserNotExistError,
|
||||
UserPasswordError,
|
||||
ProviderError,
|
||||
ProviderNotExistError,
|
||||
WorkflowError,
|
||||
WorkflowExit
|
||||
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.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 = {}
|
||||
|
||||
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.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": "工作流执行错误"})
|
||||
|
||||
frontend_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), "frontend", "dist")
|
||||
|
||||
if os.path.exists(frontend_dir):
|
||||
app.mount("/assets", StaticFiles(directory=os.path.join(frontend_dir, "assets")), name="assets")
|
||||
|
||||
# Serve favicon and other top-level static files if they exist
|
||||
@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):
|
||||
# If a path isn't API or assets, fallback to index.html for React Router / SPA handling
|
||||
# In this specific case, it also fixes any root path reloading issues
|
||||
return FileResponse(os.path.join(frontend_dir, "index.html"))
|
||||
|
||||
|
||||
self.gateway = {}
|
||||
Loading…
Reference in New Issue