"""BF查询 —— Web API 子应用(挂载到 /api/bf_bot):QQ↔EAID 绑定管理。 读写全部走 repository.py(与群里 /绑定 /解绑 命令改的是同一个库文件)。 鉴权走 hexi.web_hub.web_auth(OAuth2 + SQLite),与统一管理台 /hub 共用登录态。 **每条路由都要自带 `dependencies=[auth]`** —— mount 层没有兜底,漏一条就是匿名可访问。 前端页面:hexi/web/src/plugins/bf_bot/index.tsx。 """ from __future__ import annotations from fastapi import FastAPI from fastapi.responses import JSONResponse from hexi.web_hub.web_auth import require_admin from ..repository import UserManager API = require_admin def _ok(data=None, msg: str = "ok") -> JSONResponse: return JSONResponse({"status": 0, "msg": msg, "data": data}) def _fail(msg: str, status: int = 400) -> JSONResponse: return JSONResponse({"status": status, "msg": msg}) def build_admin_app() -> FastAPI: """构建绑定管理 API 子应用(挂载到 /api/bf_bot)。""" app = FastAPI(title="BF查询绑定管理") auth = require_admin @app.get("/bindings", response_class=JSONResponse, dependencies=[auth]) async def list_bindings(page: int = 1, per_page: int = 10): """绑定列表(QQ / EA 名称 / EA 双 ID / 标签 / 绑定时间),按绑定时间倒序分页。""" page = max(1, page) per_page = min(max(1, per_page), 100) manager = UserManager() total = manager.count_users() users = manager.list_users(limit=per_page, offset=(page - 1) * per_page) return _ok( { "bindings": [ { "qq_id": u.get("qq_id"), "ea_player_name": u.get("ea_player_name"), "ea_player_id": u.get("ea_player_id"), "ea_user_id": u.get("ea_user_id"), "designation": u.get("designation"), "create_time": u.get("create_time"), } for u in users ], "total": total, "page": page, "per_page": per_page, } ) @app.delete("/bindings/{qq_id}", response_class=JSONResponse, dependencies=[auth]) async def delete_binding(qq_id: str): """解绑某 QQ(等价于群里的「解绑」)。""" manager = UserManager() record = manager.get_user_by_qq(qq_id) if not record: return _fail("未找到该 QQ 的绑定记录", 404) rows = manager.delete_user_by_qq(qq_id) if rows > 0: return _ok( {"qq_id": qq_id, "ea_player_name": record.get("ea_player_name")}, f"已解绑 {record.get('ea_player_name')}", ) return _fail("删除失败") return app