Files
kvm-manager/frontend/serve.py
T

131 baris
4.1 KiB
Python

"""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
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):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIST_DIR, **kwargs)
def do_GET(self):
if self.path.startswith("/api/"):
self._proxy("GET")
elif self.path == "/" or self.path == "":
self.path = "/index.html"
super().do_GET()
else:
file_path = os.path.join(DIST_DIR, self.path.lstrip("/"))
if os.path.isfile(file_path):
super().do_GET()
else:
self.path = "/index.html"
super().do_GET()
def do_POST(self):
if self.path.startswith("/api/"):
self._proxy("POST")
else:
self.send_error(404)
def do_PUT(self):
if self.path.startswith("/api/"):
self._proxy("PUT")
else:
self.send_error(404)
def do_DELETE(self):
if self.path.startswith("/api/"):
self._proxy("DELETE")
else:
self.send_error(404)
def _proxy(self, method):
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)
for key in ["Content-Type", "Authorization"]:
if key in self.headers:
req.add_header(key, self.headers[key])
try:
with urllib.request.urlopen(req, timeout=30) as resp:
resp_body = resp.read()
self.send_response(resp.status)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(resp_body)
except urllib.error.HTTPError as e:
resp_body = e.read()
self.send_response(e.code)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(resp_body)
except Exception as e:
self.send_response(502)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"error": str(e)}).encode())
def log_message(self, format, *args):
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():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(websockets.serve(ws_handler, "0.0.0.0", PORT))
loop.run_forever()
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"Static file server serving on http://0.0.0.0:{PORT}")
server.serve_forever()