auto-sync: 2026-05-17 00:30:50
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
||||
"""v2.6 主入口 - FastAPI + Daemon ticker 共享 asyncio event loop"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
logger = logging.getLogger("moziplus-v2")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 配置加载
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
DEFAULT_CONFIG_PATH = Path(__file__).parent.parent / "config" / "default.yaml"
|
||||
|
||||
|
||||
def load_config() -> dict:
|
||||
"""加载全局默认配置"""
|
||||
if DEFAULT_CONFIG_PATH.exists():
|
||||
with open(DEFAULT_CONFIG_PATH) as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
return {}
|
||||
|
||||
|
||||
config = load_config()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Daemon ticker(占位,F6 实现)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_ticker_task: asyncio.Task | None = None
|
||||
|
||||
|
||||
async def _run_ticker():
|
||||
"""Daemon ticker 主循环(占位)"""
|
||||
tick_interval = config.get("daemon", {}).get("tick_interval", 30)
|
||||
logger.info("Ticker started (interval=%ss)", tick_interval)
|
||||
while True:
|
||||
try:
|
||||
# F6 会在这里注入真正的 tick 逻辑
|
||||
await asyncio.sleep(tick_interval)
|
||||
except asyncio.CancelledError:
|
||||
logger.info("Ticker cancelled")
|
||||
return
|
||||
except Exception:
|
||||
logger.exception("Tick error")
|
||||
await asyncio.sleep(tick_interval)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# FastAPI 生命周期
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""启动 Daemon ticker,关闭时清理"""
|
||||
global _ticker_task
|
||||
logger.info("moziplus-v2 starting...")
|
||||
_ticker_task = asyncio.create_task(_run_ticker())
|
||||
yield
|
||||
if _ticker_task:
|
||||
_ticker_task.cancel()
|
||||
try:
|
||||
await _ticker_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
logger.info("moziplus-v2 stopped")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# FastAPI app
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
app = FastAPI(
|
||||
title="Sanguo MoziPlus v2",
|
||||
description="AI Native DevOps Platform - Blackboard Architecture",
|
||||
version="2.6.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 健康端点
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@app.get("/api/daemon/status")
|
||||
async def daemon_status():
|
||||
"""Daemon 健康检查"""
|
||||
return {
|
||||
"status": "running",
|
||||
"version": "2.6.0",
|
||||
"ticker_running": _ticker_task is not None and not _ticker_task.done(),
|
||||
"config": {
|
||||
"tick_interval": config.get("daemon", {}).get("tick_interval", 30),
|
||||
"max_global_agents": config.get("daemon", {}).get("max_global_agents", 5),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/projects")
|
||||
async def list_projects():
|
||||
"""列出所有项目(占位,F3 实现)"""
|
||||
return {"projects": []}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 静态文件服务(前端 dist/,F18 实现)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
DIST_DIR = Path(__file__).parent / "frontend" / "dist"
|
||||
if DIST_DIR.exists():
|
||||
app.mount("/", StaticFiles(directory=str(DIST_DIR), html=True), name="frontend")
|
||||
Reference in New Issue
Block a user