from __future__ import annotations from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from importlib import import_module, reload from os import environ from pathlib import Path import json from gibil.classes.env_loader import EnvLoader EnvLoader().load() HOST = environ.get("ASTRAPE_WEB_HOST", "0.0.0.0") PORT = int(environ.get("ASTRAPE_WEB_PORT", "8080")) PROJECT_ROOT = Path(__file__).resolve().parents[2] WATCHED_PATHS = [ PROJECT_ROOT / "gibil" / "classes" / "webui.py", PROJECT_ROOT / "gibil" / "classes" / "weather_display.py", PROJECT_ROOT / "gibil" / "classes" / "weather_store.py", ] class AstrapeWebHandler(BaseHTTPRequestHandler): def do_GET(self) -> None: if self.path == "/": self._send_html(self._webui().render_page()) return if self.path == "/api/weather": self._send_json_text(self._webui().weather_payload()) return if self.path == "/api/ui-version": self._send_json_text(json.dumps({"version": self._ui_version()})) return self.send_error(404) def log_message(self, format: str, *args: object) -> None: print(f"{self.address_string()} - {format % args}") def _webui(self): weather_store_module = import_module("gibil.classes.weather_store") weather_display_module = import_module("gibil.classes.weather_display") webui_module = import_module("gibil.classes.webui") reload(weather_store_module) reload(weather_display_module) reload(webui_module) return webui_module.WebUI() def _ui_version(self) -> str: mtimes = [ str(path.stat().st_mtime_ns) for path in WATCHED_PATHS if path.exists() ] return ".".join(mtimes) def _send_html(self, body: str) -> None: encoded = body.encode("utf-8") self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") self.send_header("Content-Length", str(len(encoded))) self.end_headers() self.wfile.write(encoded) def _send_json_text(self, body: str) -> None: encoded = body.encode("utf-8") self.send_response(200) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Cache-Control", "no-store") self.send_header("Content-Length", str(len(encoded))) self.end_headers() self.wfile.write(encoded) def main() -> None: server = ThreadingHTTPServer((HOST, PORT), AstrapeWebHandler) print(f"Astrape web UI listening on http://{HOST}:{PORT}") server.serve_forever() if __name__ == "__main__": main()