Files
2026-04-07 14:11:45 +08:00

209 lines
6.1 KiB
Python

# ///
# 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()