1
0

fix: serve.py 添加 WebSocket 代理支持,控制台连接正常

- serve.py 使用 websockets 库代理 /ws/* 到后端
- 新增 frontend/requirements.txt 依赖
- install.sh 自动安装前端 Python 依赖
Esse commit está contido em:
Hermes Agent
2026-05-13 13:14:25 +08:00
commit 3e365a04fd
3 arquivos alterados com 49 adições e 7 exclusões
+1
Ver Arquivo
@@ -0,0 +1 @@
websockets>=10.0
+43 -7
Ver Arquivo
@@ -1,5 +1,8 @@
"""Simple static file server for the KVM frontend with API proxy"""
"""Simple static file server with API and WebSocket proxy"""
import asyncio
import http.server
import websockets
import threading
import urllib.request
import urllib.error
import os
@@ -8,6 +11,7 @@ import json
PORT = 8006
DIST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dist")
API_BASE = "http://127.0.0.1:8004"
WS_BASE = "ws://127.0.0.1:8004"
class KVMHandler(http.server.SimpleHTTPRequestHandler):
@@ -21,7 +25,6 @@ class KVMHandler(http.server.SimpleHTTPRequestHandler):
self.path = "/index.html"
super().do_GET()
else:
# SPA fallback: if file doesn't exist, serve index.html
file_path = os.path.join(DIST_DIR, self.path.lstrip("/"))
if os.path.isfile(file_path):
super().do_GET()
@@ -48,14 +51,12 @@ class KVMHandler(http.server.SimpleHTTPRequestHandler):
self.send_error(404)
def _proxy(self, method):
"""Proxy API requests to backend"""
content_length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_length) if content_length > 0 else None
url = f"{API_BASE}{self.path}"
req = urllib.request.Request(url, data=body, method=method)
# Forward headers
for key in ["Content-Type", "Authorization"]:
if key in self.headers:
req.add_header(key, self.headers[key])
@@ -82,10 +83,45 @@ class KVMHandler(http.server.SimpleHTTPRequestHandler):
self.wfile.write(json.dumps({"error": str(e)}).encode())
def log_message(self, format, *args):
pass # Suppress logs
pass
async def websocket_proxy(websocket, path):
"""Proxy WebSocket connections to backend"""
ws_url = f"{WS_BASE}{path}"
try:
async with websockets.connect(ws_url) as backend_ws:
async def forward_to_backend():
async for msg in websocket:
await backend_ws.send(msg)
async def forward_to_frontend():
async for msg in backend_ws:
await websocket.send(msg)
await asyncio.gather(
forward_to_backend(),
forward_to_frontend()
)
except Exception as e:
print(f"WebSocket proxy error: {e}")
async def ws_handler(websocket, path):
await websocket_proxy(websocket, path)
def run_websocket_server():
asyncio.run(websockets.serve(ws_handler, "0.0.0.0", PORT))
if __name__ == "__main__":
# Start WebSocket server in background thread
ws_thread = threading.Thread(target=run_websocket_server, daemon=True)
ws_thread.start()
print(f"WebSocket proxy listening on ws://0.0.0.0:{PORT}")
# Start HTTP server
server = http.server.HTTPServer(("0.0.0.0", PORT), KVMHandler)
print(f"KVM Frontend serving on http://0.0.0.0:{PORT}")
print(f"Static file server serving on http://0.0.0.0:{PORT}")
server.serve_forever()
+5
Ver Arquivo
@@ -100,6 +100,11 @@ cd "$FRONTEND_DIR"
npm install
npm run build
# 安装前端 Python 依赖 (serve.py 需要 websockets)
if ! pip show websockets &>/dev/null; then
pip install -r requirements.txt
fi
echo -e "${YELLOW}[5/5] 验证前端...${NC}"
if [[ -d "dist" ]]; then
echo -e "${GREEN} ✓ 前端编译成功, dist/ 已生成${NC}"