"""代理 & 白名单配置路由 + WebSocket 实时日志""" import asyncio import threading from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect from sqlalchemy.orm import Session from ..database import get_db from ..models import User from ..schemas import ProxyConfigOut, ProxyConfigUpdate from ..deps import require_permission, authenticate_websocket 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, api_url=req.api_url, http=req.http, https=req.https, whitelist_enabled=req.whitelist_enabled, whitelist_uid=req.whitelist_uid, whitelist_ukey=req.whitelist_ukey, current_user=current, ) # ---- 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 asyncio.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}