Add Fleet control plane and split manage.sh deploy menu.

Strategy nodes gain fleet token APIs; control/ app for local ops; manage.sh offers strategy vs control one-click deploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-30 10:46:40 +08:00
parent 200702d066
commit da5eb4c18c
44 changed files with 4623 additions and 15 deletions
+74
View File
@@ -0,0 +1,74 @@
from __future__ import annotations
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from .api import router as api_router
from .config import get_control_settings
from .db import ControlDB, set_control_db
def resolve_frontend_dist() -> Path:
# control/backend/app -> control/frontend/dist
return Path(__file__).resolve().parents[2] / "frontend" / "dist"
@asynccontextmanager
async def lifespan(app: FastAPI):
from .envfile import ensure_env_control_defaults
# 首次启动补全 .env.control 缺项(已有值不覆盖)
ensure_env_control_defaults()
db = ControlDB()
set_control_db(db)
yield
db.close()
set_control_db(None)
app = FastAPI(
title="比特骆驼中控",
version="0.1.0",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=[],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(api_router)
@app.get("/health")
async def health() -> dict:
s = get_control_settings()
return {"ok": True, "app": "control", "poll_interval_sec": s.control_poll_interval_sec}
_DIST = resolve_frontend_dist()
if (_DIST / "assets").is_dir():
app.mount("/assets", StaticFiles(directory=str(_DIST / "assets")), name="assets")
@app.get("/")
@app.get("/monitor")
@app.get("/settings")
@app.get("/login")
async def spa(full_path: str = ""):
index = _DIST / "index.html"
if index.exists():
return FileResponse(index)
if Path(__file__).name:
return {
"ok": True,
"msg": "control frontend not built; cd control/frontend && npm ci && npm run build",
"health": "/health",
}
raise HTTPException(status_code=404, detail="frontend missing")