""" webui/api/proxy.py - Nginx proxy domain management API blueprint. Exposed at /api/proxy/* and delegates to lib.nginx. """ import logging from flask import Blueprint, jsonify, request from lib.nginx import ( add_domain, apply, get_config, get_domains, remove_domain, set_management_proxy, test_config, update_domain, ) logger = logging.getLogger(__name__) bp = Blueprint("proxy", __name__) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _error(msg, code=400): return jsonify({"ok": False, "error": msg}), code def _ok(data=None): return jsonify({"ok": True, "data": data}) # --------------------------------------------------------------------------- # Domains # --------------------------------------------------------------------------- @bp.route("/domains", methods=["GET"]) def list_domains(): try: return _ok(get_domains()) except RuntimeError as exc: logger.error("Failed to list proxy domains: %s", exc) return _error(str(exc), 500) @bp.route("/domains", methods=["POST"]) def add_domain_bp(): body = request.get_json(silent=True) or {} domain = body.get("domain", "").strip() backend_host = body.get("backend_host", "").strip() backend_port = body.get("backend_port") backend_proto = body.get("backend_proto", "http").strip() or "http" cert = body.get("cert") extra_headers = body.get("extra_headers") if not domain: return _error("'domain' is required", 400) if not backend_host: return _error("'backend_host' is required", 400) if backend_port is None: return _error("'backend_port' is required", 400) try: add_domain( domain, backend_host, int(backend_port), backend_proto, cert, extra_headers ) logger.info("Proxy domain added via API: %s", domain) return _ok({"domain": domain}) except (ValueError, RuntimeError) as exc: logger.error("Failed to add proxy domain '%s': %s", domain, exc) return _error(str(exc), 500) @bp.route("/domains/", methods=["GET"]) def domain_details(domain): try: cfg = get_config() entry = cfg.get("domains", {}).get(domain) if entry is None: return _error(f"Domain '{domain}' not found", 404) return _ok({"domain": domain, **entry}) except RuntimeError as exc: logger.error("Failed to get domain details: %s", exc) return _error(str(exc), 500) @bp.route("/domains/", methods=["PUT"]) def update_domain_bp(domain): body = request.get_json(silent=True) or {} if not body: return _error("Request body must be a JSON object with fields to update", 400) try: update_domain(domain, **body) logger.info("Proxy domain '%s' updated via API", domain) return _ok({"domain": domain}) except KeyError as exc: return _error(str(exc), 404) except RuntimeError as exc: logger.error("Failed to update domain '%s': %s", domain, exc) return _error(str(exc), 500) @bp.route("/domains/", methods=["DELETE"]) def remove_domain_bp(domain): try: cfg = get_config() if domain not in cfg.get("domains", {}): return _error(f"Domain '{domain}' not found", 404) remove_domain(domain) logger.info("Proxy domain removed via API: %s", domain) return _ok({"domain": domain}) except RuntimeError as exc: logger.error("Failed to remove domain '%s': %s", domain, exc) return _error(str(exc), 500) # --------------------------------------------------------------------------- # Apply / test # --------------------------------------------------------------------------- @bp.route("/apply", methods=["POST"]) def apply_bp(): try: apply() logger.info("nginx config applied via API") return _ok(None) except RuntimeError as exc: logger.error("Failed to apply nginx config: %s", exc) return _error(str(exc), 500) @bp.route("/test", methods=["POST"]) def test_bp(): try: valid, output = test_config() if valid: return _ok({"valid": True, "output": output}) return _error(output, 400) except RuntimeError as exc: logger.error("nginx config test failed: %s", exc) return _error(str(exc), 500) # --------------------------------------------------------------------------- # Management proxy # --------------------------------------------------------------------------- @bp.route("/management", methods=["POST"]) def management_bp(): body = request.get_json(silent=True) or {} domain = body.get("domain", "").strip() if not domain: return _error("'domain' is required", 400) flask_host = body.get("flask_host", "127.0.0.1").strip() or "127.0.0.1" flask_port = body.get("flask_port", 9090) auth_user = body.get("auth_user") auth_pass = body.get("auth_pass") try: set_management_proxy(domain, flask_host, int(flask_port), auth_user, auth_pass) logger.info("Management proxy configured via API: %s", domain) return _ok(None) except (ValueError, RuntimeError) as exc: code = 400 if isinstance(exc, ValueError) else 500 logger.error("Failed to set management proxy: %s", exc) return _error(str(exc), code)