fix: serve.py 添加 WebSocket 代理支持,控制台连接正常
- serve.py 使用 websockets 库代理 /ws/* 到后端 - 新增 frontend/requirements.txt 依赖 - install.sh 自动安装前端 Python 依赖
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
websockets>=10.0
|
||||||
+43
-7
@@ -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 http.server
|
||||||
|
import websockets
|
||||||
|
import threading
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import os
|
import os
|
||||||
@@ -8,6 +11,7 @@ import json
|
|||||||
PORT = 8006
|
PORT = 8006
|
||||||
DIST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dist")
|
DIST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dist")
|
||||||
API_BASE = "http://127.0.0.1:8004"
|
API_BASE = "http://127.0.0.1:8004"
|
||||||
|
WS_BASE = "ws://127.0.0.1:8004"
|
||||||
|
|
||||||
|
|
||||||
class KVMHandler(http.server.SimpleHTTPRequestHandler):
|
class KVMHandler(http.server.SimpleHTTPRequestHandler):
|
||||||
@@ -21,7 +25,6 @@ class KVMHandler(http.server.SimpleHTTPRequestHandler):
|
|||||||
self.path = "/index.html"
|
self.path = "/index.html"
|
||||||
super().do_GET()
|
super().do_GET()
|
||||||
else:
|
else:
|
||||||
# SPA fallback: if file doesn't exist, serve index.html
|
|
||||||
file_path = os.path.join(DIST_DIR, self.path.lstrip("/"))
|
file_path = os.path.join(DIST_DIR, self.path.lstrip("/"))
|
||||||
if os.path.isfile(file_path):
|
if os.path.isfile(file_path):
|
||||||
super().do_GET()
|
super().do_GET()
|
||||||
@@ -48,14 +51,12 @@ class KVMHandler(http.server.SimpleHTTPRequestHandler):
|
|||||||
self.send_error(404)
|
self.send_error(404)
|
||||||
|
|
||||||
def _proxy(self, method):
|
def _proxy(self, method):
|
||||||
"""Proxy API requests to backend"""
|
|
||||||
content_length = int(self.headers.get("Content-Length", 0))
|
content_length = int(self.headers.get("Content-Length", 0))
|
||||||
body = self.rfile.read(content_length) if content_length > 0 else None
|
body = self.rfile.read(content_length) if content_length > 0 else None
|
||||||
|
|
||||||
url = f"{API_BASE}{self.path}"
|
url = f"{API_BASE}{self.path}"
|
||||||
req = urllib.request.Request(url, data=body, method=method)
|
req = urllib.request.Request(url, data=body, method=method)
|
||||||
|
|
||||||
# Forward headers
|
|
||||||
for key in ["Content-Type", "Authorization"]:
|
for key in ["Content-Type", "Authorization"]:
|
||||||
if key in self.headers:
|
if key in self.headers:
|
||||||
req.add_header(key, self.headers[key])
|
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())
|
self.wfile.write(json.dumps({"error": str(e)}).encode())
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
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__":
|
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)
|
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()
|
server.serve_forever()
|
||||||
|
|||||||
@@ -100,6 +100,11 @@ cd "$FRONTEND_DIR"
|
|||||||
npm install
|
npm install
|
||||||
npm run build
|
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}"
|
echo -e "${YELLOW}[5/5] 验证前端...${NC}"
|
||||||
if [[ -d "dist" ]]; then
|
if [[ -d "dist" ]]; then
|
||||||
echo -e "${GREEN} ✓ 前端编译成功, dist/ 已生成${NC}"
|
echo -e "${GREEN} ✓ 前端编译成功, dist/ 已生成${NC}"
|
||||||
|
|||||||
Reference in New Issue
Block a user