dabfc128b3
- 3389远程页 + guacd 侧车 WebSocket 桥(无 RDP 会话审计) - 文件推送完成独立提示音,至少一台成功即播放 - 子机列表展开 CPU/内存/磁盘 sparkline 与指标采样落库 - 终端全量服务器列表、连接中状态;告警 Telegram 公网 IP - SSH 密钥凭据 UI 简化;子机搜索未设路径筛选 Co-authored-by: Cursor <cursoragent@cursor.com>
59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
"""Async WebSocket ↔ guacd TCP bridge (Guacamole protocol relay)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
|
|
from fastapi import WebSocket, WebSocketDisconnect
|
|
|
|
logger = logging.getLogger("nexus.guacamole.tunnel")
|
|
|
|
|
|
async def bridge_websocket_to_guacd(
|
|
websocket: WebSocket,
|
|
guacd_host: str,
|
|
guacd_port: int,
|
|
) -> None:
|
|
"""Relay Guacamole instructions between browser and guacd."""
|
|
reader: asyncio.StreamReader
|
|
writer: asyncio.StreamWriter
|
|
reader, writer = await asyncio.open_connection(guacd_host, guacd_port)
|
|
|
|
async def ws_to_guacd() -> None:
|
|
try:
|
|
while True:
|
|
data = await websocket.receive_text()
|
|
writer.write(data.encode("utf-8"))
|
|
await writer.drain()
|
|
except WebSocketDisconnect:
|
|
pass
|
|
except Exception as exc:
|
|
logger.debug("RDP WS→guacd ended: %s", exc)
|
|
|
|
async def guacd_to_ws() -> None:
|
|
try:
|
|
while True:
|
|
chunk = await reader.read(8192)
|
|
if not chunk:
|
|
break
|
|
await websocket.send_text(chunk.decode("utf-8", errors="replace"))
|
|
except Exception as exc:
|
|
logger.debug("RDP guacd→WS ended: %s", exc)
|
|
|
|
ws_task = asyncio.create_task(ws_to_guacd())
|
|
guacd_task = asyncio.create_task(guacd_to_ws())
|
|
try:
|
|
await asyncio.wait(
|
|
[ws_task, guacd_task],
|
|
return_when=asyncio.FIRST_COMPLETED,
|
|
)
|
|
finally:
|
|
for task in (ws_task, guacd_task):
|
|
task.cancel()
|
|
writer.close()
|
|
try:
|
|
await writer.wait_closed()
|
|
except Exception:
|
|
pass
|