docs: add docstrings to all API endpoints and daemon handlers

Add comprehensive docstrings to firewall, DHCP, proxy, wireguard, certs,
and logs API endpoints. Document parameters, return values, and error cases
for the documentation system.
This commit is contained in:
2026-05-30 16:15:45 +00:00
parent bd98830638
commit 2f215793e9
17 changed files with 1550 additions and 28 deletions
+116 -10
View File
@@ -24,20 +24,46 @@ SOCKET_PATH = PROJECT_DIR / "data" / "daemon.sock"
class Handler:
"""Wrapper for a daemon handler function."""
"""Wrapper for a daemon handler function.
Attributes:
method: HTTP method (e.g. "GET", "POST").
path: URL path pattern.
"""
def __init__(self, method: str, path: str) -> None:
"""Initialize the handler metadata.
Args:
method: HTTP method.
path: URL path pattern.
"""
self.method = method.upper()
self.path = path
class Registry:
"""Route registry for daemon handlers."""
"""Route registry for daemon handlers.
Attributes:
_routes: Mapping of (method, path) tuples to handler callables.
"""
def __init__(self) -> None:
"""Initialize an empty route registry."""
self._routes: dict[tuple[str, str], Callable] = {}
def register(self, method: str, path: str):
"""Decorator that registers a handler for the given method and path.
Args:
method: HTTP method (e.g. "GET", "POST").
path: URL path to register the handler under.
Returns:
Decorator function wrapping the handler.
"""
def decorator(fn: Callable) -> Callable:
self._routes[(method.upper(), path)] = fn
fn._handler = Handler(method, path) # type: ignore[attr-defined]
@@ -46,6 +72,15 @@ class Registry:
return decorator
def get(self, method: str, path: str) -> Callable | None:
"""Look up a registered handler by method and path.
Args:
method: HTTP method to match.
path: URL path to match.
Returns:
The handler callable, or None if not registered.
"""
return self._routes.get((method.upper(), path))
@@ -53,7 +88,11 @@ registry = Registry()
def refresh_state(subsystems: list[str] | None = None) -> None:
"""Refresh the pre-computed state for the given subsystems (or all)."""
"""Refresh the pre-computed state for the given subsystems (or all).
Args:
subsystems: List of subsystem names to refresh. If None, all subsystems are refreshed.
"""
state_store.populate(subsystems)
@@ -64,15 +103,39 @@ class NotFoundError(Exception):
def ok(data: Any = None) -> web.Response:
"""Create a success JSON response.
Args:
data: Response payload. Defaults to None.
Returns:
A JSON Response with `ok` set to True.
"""
return web.json_response({"ok": True, "data": data})
def error(msg: str, code: int = 400) -> web.Response:
"""Create an error JSON response.
Args:
msg: Error message.
code: HTTP status code. Defaults to 400.
Returns:
A JSON Response with `ok` set to False.
"""
return web.json_response({"ok": False, "error": msg}, status=code)
async def _handle_request(request: web.Request) -> web.Response:
"""Dispatch a request to the appropriate handler."""
"""Dispatch a request to the appropriate handler.
Args:
request: The incoming HTTP request.
Returns:
The handler's response.
"""
handler_fn = registry.get(request.method, request.path)
if handler_fn is None:
return error(f"Method {request.method} not allowed for {request.path}", 404)
@@ -126,7 +189,14 @@ async def _handle_request(request: web.Request) -> web.Response:
async def _handle_batch(request: web.Request) -> web.Response:
"""Handle batch requests: execute operations in order, return keyed results."""
"""Handle batch requests: execute operations in order, return keyed results.
Args:
request: The incoming HTTP request containing operations.
Returns:
A JSON response keyed by operation IDs.
"""
try:
body = await request.json()
except json.JSONDecodeError:
@@ -175,6 +245,11 @@ async def _handle_batch(request: web.Request) -> web.Response:
def create_app() -> web.Application:
"""Create and configure the aiohttp application.
Returns:
A configured web.Application instance.
"""
app = web.Application()
app.router.add_route("GET", "/health", _health)
app.router.add_route("GET", "/status/all", get_status_all)
@@ -185,16 +260,32 @@ def create_app() -> web.Application:
async def _health(_request: web.Request) -> web.Response:
"""Return the health check response.
Returns:
JSON response with process ID and socket path.
"""
return ok({"pid": os.getpid(), "socket": str(SOCKET_PATH)})
async def get_status_all(_request: web.Request) -> web.Response:
"""Return the entire state snapshot in one call."""
"""Return the entire state snapshot in one call.
Returns:
JSON response containing state data for all subsystems.
"""
return ok({name: state_store.get(name) for name in state_store.SUBSYSTEMS})
async def refresh_status(_request: web.Request) -> web.Response:
"""Re-collect all state from system."""
"""Re-collect all state from system.
Args:
_request: The incoming request. JSON body may contain a "subsystems" list.
Returns:
JSON response with the updated state snapshot.
"""
try:
body = await _request.json()
except (json.JSONDecodeError, ValueError):
@@ -207,12 +298,23 @@ async def refresh_status(_request: web.Request) -> web.Response:
async def _catch_all(request: web.Request) -> web.Response:
"""Catch-all for registered routes."""
"""Catch-all for registered routes.
Args:
request: The incoming HTTP request.
Returns:
The dispatched handler's response.
"""
return await _handle_request(request)
def _register_routes() -> None:
"""Import all handler modules to register routes."""
"""Import all handler modules to register routes.
Side effect: each imported module's `@registry.register` calls populate the
route registry with their handler endpoints.
"""
from daemon.handlers import (
acme, # noqa: F401
dnsmasq, # noqa: F401
@@ -224,7 +326,11 @@ def _register_routes() -> None:
def main() -> None:
"""Entry point for vacuum-walld."""
"""Entry point for vacuum-walld.
Sets up logging, registers routes, creates the aiohttp application,
and starts listening on the Unix socket.
"""
from lib.logging import setup_logging
setup_logging()