#!/usr/bin/env python3 """Serve the browser UI independently of the FunASR backend.""" from __future__ import annotations import json import os from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from urllib.parse import urlsplit from dotenv import load_dotenv PROJECT_ROOT = Path(__file__).resolve().parents[1] STATIC_ROOT = PROJECT_ROOT / "realtime_websocket" / "static" load_dotenv(PROJECT_ROOT / ".env") class FrontendHandler(SimpleHTTPRequestHandler): """Serve static assets and a browser-visible backend URL at runtime.""" def __init__(self, *args, **kwargs): super().__init__(*args, directory=str(STATIC_ROOT), **kwargs) def do_GET(self) -> None: if self.path.split("?", 1)[0] != "/runtime-config.js": return super().do_GET() backend_url = os.getenv("BACKEND_PUBLIC_URL", "http://127.0.0.1:8082").rstrip("/") parsed = urlsplit(backend_url) if parsed.scheme not in {"http", "https"} or not parsed.netloc or parsed.path: self.send_error(500, "BACKEND_PUBLIC_URL must be an HTTP origin") return # JSON escaping also yields a valid JavaScript string literal. body = ("window.ASR_BACKEND_URL = " + json.dumps(backend_url) + ";\n").encode("utf-8") self.send_response(200) self.send_header("Content-Type", "application/javascript; charset=utf-8") self.send_header("Cache-Control", "no-store") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def main() -> None: host = os.getenv("FRONTEND_HOST", "127.0.0.1") port = int(os.getenv("FRONTEND_PORT", "8080")) server = ThreadingHTTPServer((host, port), FrontendHandler) print(f"Frontend: http://{host}:{port}/", flush=True) try: server.serve_forever() except KeyboardInterrupt: pass finally: server.server_close() if __name__ == "__main__": main()