"""代理 & 白名单配置路由 + WebSocket 实时日志""" import asyncio import threading from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect from sqlalchemy.orm import Session from core.douyu.proxy_platforms import ( get_credential_fields, get_platform_labels, get_platform_names, ) from ..database import get_db from ..deps import authenticate_websocket, require_permission from ..models import User from ..schemas import PlatformFieldDef, PlatformInfo, ProxyConfigOut, ProxyConfigUpdate from ..services.proxy_service import proxy_service router = APIRouter(prefix="/api/proxy", tags=["代理与白名单"]) @router.get("", response_model=ProxyConfigOut) def get_proxy_config( db: Session = Depends(get_db), _: User = Depends(require_permission("proxy:manage")), ): return proxy_service.get_or_create(db) @router.put("", response_model=ProxyConfigOut) def update_proxy_config( req: ProxyConfigUpdate, db: Session = Depends(get_db), current: User = Depends(require_permission("proxy:manage")), ): return proxy_service.update_config( db, enabled=req.enabled if req.enabled is not None else False, api_url=req.api_url if req.api_url is not None else "", http=req.http if req.http is not None else "", https=req.https if req.https is not None else "", whitelist_enabled=req.whitelist_enabled if req.whitelist_enabled is not None else False, whitelist_platform=req.whitelist_platform or "xiequ", whitelist_credentials=req.whitelist_credentials, whitelist_uid=req.whitelist_uid, whitelist_ukey=req.whitelist_ukey, current_user=current, ) @router.get("/platforms", response_model=list[PlatformInfo]) def list_platforms(): """获取所有可用的代理白名单平台及其凭据字段定义。""" labels = get_platform_labels() result = [] for name in get_platform_names(): fields = get_credential_fields(name) result.append( PlatformInfo( name=name, label=labels.get(name, name), credential_fields=[PlatformFieldDef(**f) for f in fields], ) ) return result # ---- WebSocket 日志推送 ---- @router.websocket("/ws/test/{test_id}") async def ws_test_logs(websocket: WebSocket, test_id: str): """WebSocket 推送代理/白名单测试实时日志(需认证)。""" user = authenticate_websocket(websocket) if not user: await websocket.close(code=1008, reason="未授权") return await websocket.accept() test = proxy_service.get_test(test_id) if not test: await websocket.send_json({"level": "error", "message": "测试任务不存在"}) await websocket.close() return log_queue: asyncio.Queue = test["log_queue"] try: while True: try: msg = await asyncio.wait_for(log_queue.get(), timeout=30) await websocket.send_json(msg) # 收到 result 消息表示测试结束 if msg.get("level") == "result": await asyncio.sleep(0.1) break except TimeoutError: await websocket.send_json({"level": "heartbeat", "message": ""}) except WebSocketDisconnect: pass finally: proxy_service.remove_test(test_id) # ---- 异步测试 API 端点 ---- @router.post("/test") async def test_proxy( db: Session = Depends(get_db), current: User = Depends(require_permission("proxy:manage")), ): """启动代理测试(异步执行,通过 WebSocket 推送日志)。""" cfg = proxy_service.get_or_create(db) test_id, log_queue, loop = proxy_service.register_test() thread = threading.Thread( target=proxy_service.run_proxy_test, args=(cfg, log_queue, loop), daemon=True, ) thread.start() return {"test_id": test_id, "success": True} @router.post("/whitelist/test") async def test_whitelist( db: Session = Depends(get_db), current: User = Depends(require_permission("whitelist:test")), ): """启动白名单测试(异步执行,通过 WebSocket 推送日志)。""" cfg = proxy_service.get_or_create(db) test_id, log_queue, loop = proxy_service.register_test() thread = threading.Thread( target=proxy_service.run_whitelist_test, args=(cfg, log_queue, loop), daemon=True, ) thread.start() return {"test_id": test_id, "success": True}