{"repo": "butteryiyi/talk", "files": {"app.py": {"a57df361159da4d2bd891669839bed7213702e99": {"t": 1785674067.2076998, "n": 14411, "h": "1f83599701dba475", "c": "# app.py\r\n\"\"\"\r\n主服务器:FastAPI + WebSocket 代理\r\n外部请求通过 HTTP/WebSocket 进入,由内部浏览器通过 DOM 交互执行。\r\n\"\"\"\r\n\r\nimport os\r\nimport sys\r\nimport json\r\nimport asyncio\r\nimport time\r\nfrom datetime import datetime\r\nfrom contextlib import asynccontextmanager\r\nfrom browser_manager import BrowserManager, HEARTBEAT_MARKER\r\n\r\nfrom fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, Request\r\nfrom fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse\r\nfrom fastapi.security import HTTPBearer\r\nimport uvicorn\r\n\r\nfrom browser_manager import BrowserManager, HEARTBEAT_MARKER\r\nfrom keepalive import KeepaliveService\r\n\r\n# ============================================================\r\n# 全局实例 & 配置\r\n# ============================================================\r\nbrowser_mgr: BrowserManager = None\r\nkeepalive_svc: KeepaliveService = None\r\n\r\nAPI_SECRET_KEY = os.getenv(\"API_SECRET_KEY\", \"123456\")\r\nsecurity = HTTPBearer(auto_error=False)\r\n\r\n\r\ndef verify_api_key(request: Request):\r\n auth_header = request.headers.get(\"authorization\", \"\")\r\n if auth_header.startswith(\"Bearer \"):\r\n token = auth_header[7:].strip()\r\n if token == API_SECRET_KEY:\r\n return True\r\n\r\n x_api_key = request.headers.get(\"x-api-key\", \"\").strip()\r\n if x_api_key == API_SECRET_KEY:\r\n return True\r\n\r\n api_key_param = request.query_params.get(\"api_key\", \"\").strip()\r\n if api_key_param == API_SECRET_KEY:\r\n return True\r\n\r\n raise HTTPException(\r\n status_code=401,\r\n detail={\r\n \"error\": {\r\n \"message\": \"Invalid API key.\",\r\n \"type\": \"invalid_request_error\",\r\n \"code\": \"invalid_api_key\",\r\n }\r\n },\r\n )\r\n\r\n\r\nasync def ensure_browser_ready():\r\n if browser_mgr is None:\r\n raise HTTPException(status_code=503, detail=\"服务正在启动中,请稍后重试\")\r\n\r\n if not browser_mgr.is_ready:\r\n print(\" ⏳ 请求到达,等待浏览器初始化完成...\")\r\n ok = await browser_mgr.wait_until_ready(timeout=180)\r\n if not ok:\r\n raise HTTPException(status_code=503, detail=\"浏览器初始化超时,请稍后重试\")\r\n\r\n if not await browser_mgr.is_alive():\r\n raise HTTPException(status_code=503, detail=\"浏览器会话已断开\")\r\n\r\n # ═══ 请求到达时检查登录状态 ═══\r\n if not browser_mgr.logged_in:\r\n print(\" ⚠️ 请求到达但未登录,尝试重新登录...\")\r\n success = await browser_mgr.re_login()\r\n if not success:\r\n raise HTTPException(\r\n status_code=503,\r\n detail=\"登录状态失效且重新登录失败,请更新 Cookie\"\r\n )\r\n\r\n # 每 20 个请求做一次深度检查\r\n if browser_mgr.total_requests % 20 == 0:\r\n is_ok = await browser_mgr.check_login_status()\r\n if not is_ok:\r\n print(\" ⚠️ 抽查发现登录失效,尝试重新登录...\")\r\n\r\n\r\n@asynccontextmanager\r\nasync def lifespan(app: FastAPI):\r\n global browser_mgr, keepalive_svc\r\n\r\n print(f\"\\n{'='*60}\")\r\n print(f\" 应用启动 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\")\r\n print(f\" 模式: DOM 交互(无需手动 PoW)\")\r\n print(f\" API_SECRET_KEY: {API_SECRET_KEY[:3]}{'*' * (len(API_SECRET_KEY) - 3)}\")\r\n print(f\"{'='*60}\\n\")\r\n\r\n browser_mgr = BrowserManager()\r\n keepalive_svc = KeepaliveService(browser_mgr)\r\n\r\n asyncio.create_task(initialize_background())\r\n\r\n print(\"🚀 服务已就绪(浏览器后台初始化中),等待请求...\\n\")\r\n yield\r\n\r\n print(\"\\n⏹️ 正在关闭服务...\")\r\n if keepalive_svc:\r\n await keepalive_svc.stop()\r\n if browser_mgr:\r\n await browser_mgr.shutdown()\r\n print(\"✅ 服务已安全关闭。\")\r\n\r\n\r\nasync def initialize_background():\r\n global browser_mgr, keepalive_svc\r\n print(\"⏳ 后台任务:开始初始化浏览器...\")\r\n try:\r\n await browser_mgr.initialize()\r\n print(\"✅ 后台任务:浏览器初始化完成。\")\r\n\r\n if keepalive_svc and not keepalive_svc.is_running:\r\n await keepalive_svc.start()\r\n except Exception as e:\r\n print(f\"❌ 后台任务:浏览器初始化失败: {e}\")\r\n import traceback\r\n traceback.print_exc()\r\n\r\n\r\napp = FastAPI(title=\"DeepSeek Proxy\", lifespan=lifespan)\r\n\r\n\r\n# ============================================================\r\n# 健康检查 & 状态页\r\n# ============================================================\r\n@app.get(\"/\")\r\nasync def index():\r\n status = await browser_mgr.get_status() if browser_mgr else {\"status\": \"initializing\"}\r\n uptime = status.get(\"uptime_seconds\", 0)\r\n hours, remainder = divmod(int(uptime), 3600)\r\n minutes, seconds = divmod(remainder, 60)\r\n\r\n ready = status.get(\"ready\", False)\r\n alive = status.get(\"browser_alive\", False)\r\n\r\n if ready and alive:\r\n status_text = \"运行中\"\r\n dot_color = \"#3fb950\"\r\n elif not ready:\r\n status_text = \"初始化中...\"\r\n dot_color = \"#f0ad4e\"\r\n else:\r\n status_text = \"离线\"\r\n dot_color = \"#f85149\"\r\n\r\n html = f\"\"\"\r\n \r\n \r\n
\r\n\r\n POST /v1/chat/completions 发送聊天请求(需要 API Key)
\r\n 鉴权方式:Authorization: Bearer <your-api-key>\r\n