# /// # main.py # 描述:FastAPI主入口,启动中控系统服务 # 作者:AI Generated # 创建日期:2026-04-05 # /// import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse, JSONResponse from config import get_settings, load_config_from_file, save_config_to_file from api import nodes, messages, external, plugins, callback, webhook from plugins.builtin import register_builtin_plugins from plugins.plugin_loader import hot_plugin_loader, create_sample_plugin from services.node_manager import node_manager from services.monitor_service import monitor_service from services.log_service import log_service from services.scheduler_service import scheduler_service settings = get_settings() app = FastAPI( title=settings.app_name, description="WXAuto中控系统 - 多节点微信控制平台", version="1.0.0", docs_url="/docs", redoc_url="/redoc" ) app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): log_service.error(f"Global exception: {str(exc)}", "Error") return JSONResponse( status_code=500, content={"success": False, "error": str(exc)} ) app.include_router(nodes.router, prefix=settings.api_prefix) app.include_router(messages.router, prefix=settings.api_prefix) app.include_router(external.router, prefix=settings.api_prefix) app.include_router(plugins.router, prefix=settings.api_prefix) app.include_router(callback.router, prefix=settings.api_prefix) app.include_router(webhook.router, prefix=settings.api_prefix) @app.on_event("startup") async def startup_event(): load_config_from_file("config.json") register_builtin_plugins() from database_service import init_database, get_db_service from config import get_settings settings = get_settings() init_database(settings.database.url) log_service.info("Database initialized", "Startup") db_service = get_db_service() if db_service: for node in db_service.get_all_nodes(): await node_manager.add_node( node_id=node.node_id, name=node.name, api_url=node.api_url, api_key=node.api_key, description=node.description or "", group=node.group or "default" ) log_service.info(f"Loaded {len(db_service.get_all_nodes())} nodes from database", "Startup") for plugin in db_service.get_all_plugin_configs(): from plugins.base import PluginRegistry p = PluginRegistry.get(plugin.plugin_name) if p: import json config = json.loads(plugin.config_json) if plugin.config_json else {} p.initialize(config) if plugin.enabled: p.enable() else: p.disable() log_service.info("Plugins loaded from database", "Startup") from services.plugin_notification_service import plugin_notification_service plugin_notification_service.initialize() log_service.info("Plugin notification service initialized", "Startup") await monitor_service.start() await scheduler_service.start() hot_plugin_loader.start() create_sample_plugin() print(f"[{settings.app_name}] Started successfully") print(f"API Documentation: http://{settings.host}:{settings.port}/docs") @app.on_event("shutdown") async def shutdown_event(): await monitor_service.stop() await scheduler_service.stop() hot_plugin_loader.stop() save_config_to_file("config.json") print(f"[{settings.app_name}] Shutdown, config saved") @app.get("/", summary="首页") async def root(): return RedirectResponse(url="/static/index.html") @app.get("/health", summary="健康检查") async def health(): return {"status": "ok", "app": settings.app_name} @app.get("/api/v1/config/monitor", summary="获取监控配置") async def get_monitor_config(): return { "success": True, "data": settings.monitor.model_dump() } @app.post("/api/v1/config/monitor", summary="更新监控配置") async def update_monitor_config(request: dict): from config import MonitorConfig global settings settings.monitor = MonitorConfig( enabled=request.get("enabled", True), check_interval=request.get("check_interval", 60) ) save_config_to_file() return {"success": True, "message": "Monitor config updated"} @app.get("/api/v1/monitor/status", summary="获取监控状态") async def get_monitor_status(): return { "success": True, "data": monitor_service.get_all_status() } @app.get("/api/v1/logs/recent", summary="获取最近日志") async def get_recent_logs(count: int = 50): return { "success": True, "data": log_service.get_recent_logs(count) } @app.get("/api/v1/logs/stream", summary="日志流") async def log_stream(): from fastapi.responses import StreamingResponse async def event_generator(): queue = await log_service.subscribe() try: while True: log_entry = await queue.get() yield f"data: {json.dumps(log_entry)}\n\n" except Exception: log_service.unsubscribe(queue) return StreamingResponse( event_generator(), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" } ) static_path = os.path.join(os.path.dirname(__file__), "static") if os.path.exists(static_path): app.mount("/static", StaticFiles(directory=static_path), name="static") def main(): import uvicorn uvicorn.run( "main:app", host=settings.host, port=settings.port, reload=settings.debug ) if __name__ == "__main__": main()