"""Nginx proxy domain management API blueprint. Exposed at /api/proxy/* and delegates to vacuum-walld. """ from typing import Any from flask import Blueprint from daemon.client import ( # noqa: F401 (resolved via module globals) BadRequest, delete, get, patch, post, ) from daemon.iface import ( DELETE_NGINX_BACKENDS_REMOVE, DELETE_NGINX_DOMAINS_REMOVE, GET_NGINX_BACKENDS, GET_NGINX_CONFIG, GET_NGINX_DOMAINS, PATCH_NGINX_BACKENDS, PATCH_NGINX_CONFIG, POST_NGINX_APPLY, POST_NGINX_BACKENDS_ADD, POST_NGINX_CONFIG, POST_NGINX_DOMAINS_ADD, POST_NGINX_DOMAINS_UPDATE, POST_NGINX_SSL_APPLY, POST_NGINX_TEST, ) from webui.api.common import NO_BODY, daemon_route, require_dict_body, void_transform bp = Blueprint("proxy", __name__) # --------------------------------------------------------------------------- # Config # --------------------------------------------------------------------------- @daemon_route(POST_NGINX_SSL_APPLY, bp, body=NO_BODY, transform=void_transform) def ssl_apply_bp(): """POST /api/proxy/ssl-apply — Apply the SSL snippet config.""" @daemon_route(GET_NGINX_CONFIG, bp) def get_config_bp(): """GET /api/proxy/config — Get the current nginx proxy configuration.""" @daemon_route( POST_NGINX_CONFIG, bp, precheck=require_dict_body, transform=void_transform ) def post_config(): """POST /api/proxy/config — Save the nginx proxy configuration.""" @daemon_route( PATCH_NGINX_CONFIG, bp, precheck=require_dict_body, transform=void_transform ) def patch_config(): """PATCH /api/proxy/config — Partially update the nginx proxy configuration.""" @daemon_route(GET_NGINX_DOMAINS, bp) def list_domains(): """GET /api/proxy/domains — List all configured proxy domains.""" # --------------------------------------------------------------------------- # Domain CRUD # --------------------------------------------------------------------------- def _add_domain_body(request: Any, _va: Any) -> dict[str, Any]: body = request.get_json(silent=True) or {} domain = (body.get("domain") or "").strip() if not domain: raise ValueError("'domain' is required") backend = (body.get("backend") or "").strip() if not backend: raise ValueError("'backend' is required") payload: dict[str, Any] = { "domain": domain, "backend": backend, "force_ssl": body.get("force_ssl", True), } if body.get("cert") is not None: payload["cert"] = body["cert"] if body.get("auth") is not None: payload["auth"] = body["auth"] return payload def _domain_echo(_data: Any, _va: Any, sent: Any) -> Any: return {"domain": sent.get("domain")} @daemon_route( POST_NGINX_DOMAINS_ADD, bp, rule="/domains", body=_add_domain_body, transform=_domain_echo, ) def add_domain_bp(): """POST /api/proxy/domains — Add a new proxy domain referencing a backend.""" def _update_domain_precheck(json: Any, _va: Any) -> None: if not json: raise ValueError("Request body must be a JSON object with fields to update") @daemon_route( POST_NGINX_DOMAINS_UPDATE, bp, rule="/domains/", methods=["PUT"], precheck=_update_domain_precheck, transform=_domain_echo, ) def update_domain_bp(): """PUT /api/proxy/domains/ — Update an existing proxy domain in-place.""" @daemon_route( DELETE_NGINX_DOMAINS_REMOVE, bp, rule="/domains/", transform=_domain_echo ) def remove_domain_bp(): """DELETE /api/proxy/domains/ — Remove a proxy domain.""" @daemon_route(POST_NGINX_APPLY, bp, body=NO_BODY, transform=void_transform) def apply_bp(): """POST /api/proxy/apply — Generate all nginx configs and reload nginx.""" def _test_transform(data: Any, _va: Any, _sent: Any) -> Any: if data.get("valid"): return {"valid": True, "output": data.get("output", "")} raise BadRequest(data.get("output", "unknown error")) @daemon_route(POST_NGINX_TEST, bp, body=NO_BODY, transform=_test_transform) def test_bp(): """POST /api/proxy/test — Test nginx configuration without reloading.""" # --------------------------------------------------------------------------- # Backend CRUD # --------------------------------------------------------------------------- def _backend_echo(_data: Any, _va: Any, sent: Any) -> Any: return {"backend": sent.get("name")} @daemon_route(GET_NGINX_BACKENDS, bp) def list_backends(): """GET /api/proxy/backends — List all configured backends (secrets stripped).""" @daemon_route( PATCH_NGINX_BACKENDS, bp, precheck=require_dict_body, transform=_backend_echo ) def patch_backend_bp(): """PATCH /api/proxy/backends — Partially update a backend entry.""" def _add_backend_precheck(json: Any, _va: Any) -> None: if not ((json or {}).get("name") or "").strip(): raise ValueError("'name' is required") @daemon_route( POST_NGINX_BACKENDS_ADD, bp, rule="/backends", precheck=_add_backend_precheck, transform=_backend_echo, ) def add_backend_bp(): """POST /api/proxy/backends — Add a new backend.""" @daemon_route( DELETE_NGINX_BACKENDS_REMOVE, bp, rule="/backends/", transform=_backend_echo ) def remove_backend_bp(): """DELETE /api/proxy/backends/ — Remove a non-builtin backend."""