""" webui/api/certs.py - ACME certificate management API blueprint. Exposed at /api/certs/* and delegates to lib.acme. """ from flask import Blueprint, jsonify, request from lib.acme import ( get_cert_info, issue, list_certs, remove, renew, set_email, ) bp = Blueprint("certs", __name__) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _error(msg, code=400): return jsonify({"ok": False, "error": msg}), code def _ok(data=None): body = {"ok": True} if data is not None: body["data"] = data return jsonify(body) # --------------------------------------------------------------------------- # Certificate listing # --------------------------------------------------------------------------- @bp.route("/list", methods=["GET"]) def list_certs_bp(): try: return _ok(list_certs()) except RuntimeError as exc: return _error(str(exc), 500) @bp.route("/", methods=["GET"]) def cert_details(domain): try: info = get_cert_info(domain) return _ok(info) except ValueError as exc: return _error(str(exc), 404) except RuntimeError as exc: return _error(str(exc), 500) # --------------------------------------------------------------------------- # Issue # --------------------------------------------------------------------------- @bp.route("/issue", methods=["POST"]) def issue_bp(): body = request.get_json(silent=True) or {} domain = body.get("domain", "").strip() if not domain: return _error("'domain' is required", 400) webroot = body.get("webroot") standalone = body.get("standalone", False) try: result = issue(domain, webroot=webroot, standalone=standalone) if result.get("success"): return _ok(result) return jsonify( {"ok": False, "error": result.get("error", "Unknown error"), "data": result} ), 400 except RuntimeError as exc: return _error(str(exc), 500) # --------------------------------------------------------------------------- # Renew # --------------------------------------------------------------------------- @bp.route("//renew", methods=["POST"]) def renew_bp(domain): try: result = renew(domain) if result.get("success"): return _ok(result) return jsonify( {"ok": False, "error": result.get("error", "Unknown error"), "data": result} ), 400 except RuntimeError as exc: return _error(str(exc), 500) # --------------------------------------------------------------------------- # Remove # --------------------------------------------------------------------------- @bp.route("/", methods=["DELETE"]) def remove_bp(domain): try: remove(domain) return _ok({"domain": domain}) except (RuntimeError, FileNotFoundError) as exc: return _error(str(exc), 500) # --------------------------------------------------------------------------- # Contact email # --------------------------------------------------------------------------- @bp.route("/email", methods=["POST"]) def set_email_bp(): body = request.get_json(silent=True) or {} email = body.get("email", "").strip() if not email: return _error("'email' is required", 400) try: set_email(email) return _ok({"email": email}) except RuntimeError as exc: return _error(str(exc), 500)