2025-10-28 13:00:35 +08:00
|
|
|
|
"""WPS Bot Game - FastAPI主应用"""
|
|
|
|
|
|
import logging
|
2025-10-29 12:06:18 +08:00
|
|
|
|
import argparse
|
|
|
|
|
|
import os
|
2025-10-28 13:00:35 +08:00
|
|
|
|
from fastapi import FastAPI
|
|
|
|
|
|
from fastapi.responses import JSONResponse
|
|
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
|
|
import asyncio
|
|
|
|
|
|
|
2025-10-29 12:14:50 +08:00
|
|
|
|
from config import APP_CONFIG, SESSION_TIMEOUT, SetWebhookURL, GetWebhookURL
|
2025-10-28 13:00:35 +08:00
|
|
|
|
from core.middleware import ConcurrencyLimitMiddleware
|
|
|
|
|
|
from core.database import get_db
|
|
|
|
|
|
from routers import callback, health
|
|
|
|
|
|
|
|
|
|
|
|
# 配置日志
|
|
|
|
|
|
logging.basicConfig(
|
|
|
|
|
|
level=logging.INFO,
|
|
|
|
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
|
|
|
|
)
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@asynccontextmanager
|
|
|
|
|
|
async def lifespan(app: FastAPI):
|
|
|
|
|
|
"""应用生命周期管理"""
|
|
|
|
|
|
# 启动时
|
|
|
|
|
|
logger.info("=" * 50)
|
|
|
|
|
|
logger.info("WPS Bot Game 启动中...")
|
|
|
|
|
|
logger.info("=" * 50)
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化数据库
|
|
|
|
|
|
db = get_db()
|
|
|
|
|
|
logger.info(f"数据库初始化完成: {db.db_path}")
|
|
|
|
|
|
|
|
|
|
|
|
# 启动后台清理任务
|
|
|
|
|
|
cleanup_task = asyncio.create_task(periodic_cleanup())
|
|
|
|
|
|
logger.info("后台清理任务已启动")
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("应用启动完成!")
|
|
|
|
|
|
|
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
|
|
|
|
# 关闭时
|
|
|
|
|
|
logger.info("应用正在关闭...")
|
|
|
|
|
|
cleanup_task.cancel()
|
|
|
|
|
|
try:
|
|
|
|
|
|
await cleanup_task
|
|
|
|
|
|
except asyncio.CancelledError:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
db.close()
|
|
|
|
|
|
logger.info("应用已关闭")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def periodic_cleanup():
|
|
|
|
|
|
"""定期清理过期会话"""
|
|
|
|
|
|
while True:
|
|
|
|
|
|
try:
|
|
|
|
|
|
await asyncio.sleep(300) # 每5分钟执行一次
|
|
|
|
|
|
db = get_db()
|
|
|
|
|
|
db.cleanup_old_sessions(SESSION_TIMEOUT)
|
|
|
|
|
|
except asyncio.CancelledError:
|
|
|
|
|
|
break
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"清理任务错误: {e}", exc_info=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建FastAPI应用
|
|
|
|
|
|
app = FastAPI(**APP_CONFIG, lifespan=lifespan)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加并发限制中间件
|
|
|
|
|
|
app.add_middleware(ConcurrencyLimitMiddleware)
|
|
|
|
|
|
|
|
|
|
|
|
# 注册路由
|
|
|
|
|
|
app.include_router(callback.router, prefix="/api", tags=["callback"])
|
|
|
|
|
|
app.include_router(health.router, tags=["health"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/")
|
|
|
|
|
|
async def root():
|
|
|
|
|
|
"""根路径"""
|
|
|
|
|
|
return JSONResponse({
|
|
|
|
|
|
"message": "WPS Bot Game API",
|
|
|
|
|
|
"version": "1.0.0",
|
|
|
|
|
|
"status": "running"
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(Exception)
|
|
|
|
|
|
async def global_exception_handler(request, exc):
|
|
|
|
|
|
"""全局异常处理"""
|
|
|
|
|
|
logger.error(f"未捕获的异常: {exc}", exc_info=True)
|
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
|
status_code=500,
|
|
|
|
|
|
content={"error": "Internal Server Error", "detail": str(exc)}
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-29 12:06:18 +08:00
|
|
|
|
def parse_args():
|
|
|
|
|
|
"""解析命令行参数"""
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='WPS Bot Game')
|
|
|
|
|
|
parser.add_argument('--webhook-url', '-w',
|
|
|
|
|
|
help='WPS Webhook URL')
|
|
|
|
|
|
parser.add_argument('--host', '-H',
|
|
|
|
|
|
default='0.0.0.0',
|
|
|
|
|
|
help='服务器主机地址 (默认: 0.0.0.0)')
|
|
|
|
|
|
parser.add_argument('--port', '-p',
|
|
|
|
|
|
type=int, default=11000,
|
|
|
|
|
|
help='服务器端口 (默认: 11000)')
|
|
|
|
|
|
parser.add_argument('--workers',
|
|
|
|
|
|
type=int, default=1,
|
|
|
|
|
|
help='工作进程数 (默认: 1)')
|
|
|
|
|
|
parser.add_argument('--log-level',
|
|
|
|
|
|
default='info',
|
|
|
|
|
|
choices=['debug', 'info', 'warning', 'error'],
|
|
|
|
|
|
help='日志级别 (默认: info)')
|
|
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-28 13:00:35 +08:00
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
import uvicorn
|
2025-10-29 12:06:18 +08:00
|
|
|
|
|
|
|
|
|
|
# 解析命令行参数
|
|
|
|
|
|
args = parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
# 如果提供了webhook URL,设置环境变量
|
|
|
|
|
|
if args.webhook_url:
|
2025-10-29 12:14:50 +08:00
|
|
|
|
SetWebhookURL(args.webhook_url)
|
2025-10-29 12:06:18 +08:00
|
|
|
|
logger.info(f"设置Webhook URL: {args.webhook_url}")
|
|
|
|
|
|
|
|
|
|
|
|
# 启动服务器
|
2025-10-28 13:00:35 +08:00
|
|
|
|
uvicorn.run(
|
|
|
|
|
|
"app:app",
|
2025-10-29 12:06:18 +08:00
|
|
|
|
host=args.host,
|
|
|
|
|
|
port=args.port,
|
|
|
|
|
|
workers=args.workers,
|
2025-10-28 13:00:35 +08:00
|
|
|
|
limit_concurrency=5,
|
2025-10-29 12:06:18 +08:00
|
|
|
|
log_level=args.log_level
|
2025-10-28 13:00:35 +08:00
|
|
|
|
)
|
|
|
|
|
|
|