"""Shared HTTP helper for the Muninn API scripts. Not meant to be run directly - imported by sites.py, accounts.py, posts.py. """ import json import os import sys import urllib.error import urllib.parse import urllib.request DEFAULT_BASE_URL = "http://localhost:8001" def base_url() -> str: return os.environ.get("MUNINN_API_URL", DEFAULT_BASE_URL).rstrip("/") def request(method: str, path: str, params: dict | None = None, body: dict | None = None) -> None: """Call the API and print the JSON response to stdout. Exits 0 on a 2xx response. On an HTTP error or network failure, prints a JSON error object to stderr and exits 1. """ url = f"{base_url()}{path}" if params: query = "&".join( f"{key}={urllib.parse.quote(str(value))}" for key, value in params.items() if value is not None ) if query: url = f"{url}?{query}" data = json.dumps(body).encode("utf-8") if body is not None else None headers = {"Accept": "application/json"} if data is not None: headers["Content-Type"] = "application/json" req = urllib.request.Request(url, data=data, method=method, headers=headers) try: with urllib.request.urlopen(req) as response: payload = response.read().decode("utf-8") except urllib.error.HTTPError as error: body_text = error.read().decode("utf-8") try: parsed = json.loads(body_text) except json.JSONDecodeError: parsed = {"error": body_text} print(json.dumps(parsed, indent=2), file=sys.stderr) sys.exit(1) except urllib.error.URLError as error: print(json.dumps({"error": f"could not reach {url}: {error.reason}"}, indent=2), file=sys.stderr) sys.exit(1) if payload: parsed = json.loads(payload) print(json.dumps(parsed, indent=2)) sys.exit(0)