"""FastAPI 应用入口。 负责组装数据库、节点管理器、调度器与静态前端,并在应用生命周期内管理 后台线程的启动与清理。 """ from __future__ import annotations import os from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from app.config import DATA_DIR, DB_PATH, STORAGE_DIR, WORKSPACE_ROOT from app.db import Database from app.node_manager import NodeManager from app.routers import apps, instances, nodes, workflows from app.scheduler import WorkflowScheduler from app.seed import seed_demo_workflow, seed_nodes @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期:启动时初始化存储、种子数据和后台服务,退出时回收资源。""" # 确保数据与存储目录存在,避免首次启动写文件失败。 DATA_DIR.mkdir(parents=True, exist_ok=True) STORAGE_DIR.mkdir(parents=True, exist_ok=True) db = Database(DB_PATH) manager = NodeManager(db) # 启动节点空闲回收线程,负责按 TTL 停止空闲节点进程。 manager.start_reaper() # 默认自动注册工作区内的节点并创建演示工作流,可关闭便于测试。 if os.getenv("WOV_AUTO_SEED", "1") == "1": seed_nodes(db, WORKSPACE_ROOT) seed_demo_workflow(db) scheduler = WorkflowScheduler(db, manager, STORAGE_DIR) # 调度器默认开启,处理排队中的任务;测试可关闭后手动执行。 if os.getenv("WOV_SCHEDULER_ENABLED", "1") == "1": scheduler.start() # 共享对象挂到 app.state,路由通过 Depends 延迟获取。 app.state.db = db app.state.node_manager = manager app.state.scheduler = scheduler yield # 退出时先停调度器,再关闭全部节点进程,避免残留进程。 scheduler.stop() manager.shutdown() app = FastAPI(title="WOV API", version="0.1.0", lifespan=lifespan) # MVP 阶段不做鉴权,允许跨域便于本地调试与静态页面访问。 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) app.include_router(nodes.router) app.include_router(instances.router) app.include_router(workflows.router) app.include_router(apps.router) @app.get("/health") def health() -> dict: """进程存活探针,供部署环境与前端检测后端可用性。""" return {"status": "ok", "service": "wov-api"} # 静态前端目录位于工作区下的 wov-web,由 FastAPI 直接挂载。 FRONTEND_DIR = Path(__file__).resolve().parent.parent.parent / "wov-web" app.mount("/", StaticFiles(directory=FRONTEND_DIR, html=True), name="frontend")