#!/usr/bin/env python3 """api_stub.py — DEV-ONLY заглушка health-эндпоинта (задача #21). Существует ровно для одной цели: до реализации FastAPI-каркаса (#31) проверить плумбинг dev-стенда — маппинг порта, healthcheck контейнера, тома /config и /data, переживание `docker compose restart`. Отдаёт: GET /api/v1/health -> 200 {"status":"ok","stub":true,...} GET /healthz -> 200 (алиас для отладки) всё остальное -> 404 {"error":"not_implemented","issue":"#31"} Чего НЕ делает: никакой логики шлюза, никакого доступа к awg/iptables. В staging/prod запускать запрещено (см. deploy/dev-stand.md). """ from __future__ import annotations import argparse import json import os import time from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer STARTED_AT = time.time() STUB_ENV = os.environ.get("AWG_API_STUB", "0") class Handler(BaseHTTPRequestHandler): server_version = "awg-dash-stub/0.1" protocol_version = "HTTP/1.1" def _json(self, code: int, payload: dict) -> None: body = (json.dumps(payload, sort_keys=True) + "\n").encode() self.send_response(code) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_GET(self) -> None: # noqa: N802 (HTTP-метод) path = self.path.split("?", 1)[0] if path in ("/api/v1/health", "/healthz"): self._json( 200, { "status": "ok", "stub": True, "issue": "#31", "note": "заглушка dev-стенда: реальный API появится в #31", "uptime_s": round(time.time() - STARTED_AT, 3), "stub_env": STUB_ENV, }, ) return self._json(404, {"error": "not_implemented", "issue": "#31", "path": path}) def do_HEAD(self) -> None: # noqa: N802 self.send_response(200 if self.path.split("?", 1)[0] in ("/api/v1/health", "/healthz") else 404) self.send_header("Content-Length", "0") self.end_headers() def log_message(self, fmt: str, *args) -> None: # лог в stdout контейнера print(f"[api-stub] {self.address_string()} {fmt % args}", flush=True) def main() -> int: ap = argparse.ArgumentParser(description="DEV-ONLY заглушка health для dev-стенда (#21)") ap.add_argument("--host", default="0.0.0.0") ap.add_argument("--port", type=int, default=8080) ap.add_argument("--domain", default="/config", help="каталог, доступность которого проверяем (для лога)") args = ap.parse_args() print( f"[api-stub] DEV-ONLY заглушка на {args.host}:{args.port}; каталог {args.domain} " f"{'доступен' if os.path.isdir(args.domain) else 'НЕ доступен'}", flush=True, ) ThreadingHTTPServer((args.host, args.port), Handler).serve_forever() return 0 if __name__ == "__main__": raise SystemExit(main())