diff --git a/app.py b/app.py index d972069..6ecc783 100644 --- a/app.py +++ b/app.py @@ -176,6 +176,42 @@ def find_container_hostname(name): return extract_hostname(labels) +def cert_issuer(hostname, timeout=5): + """Traefik'in o hostname icin su an sundugu sertifikanin issuer'ini + dondurur (bos string = baglanti kurulamadi).""" + try: + p1 = subprocess.run( + ["openssl", "s_client", "-connect", "traefik:443", "-servername", hostname], + input="", capture_output=True, text=True, timeout=timeout, + ) + p2 = subprocess.run( + ["openssl", "x509", "-noout", "-issuer"], + input=p1.stdout, capture_output=True, text=True, timeout=timeout, + ) + return p2.stdout.strip() + except Exception: + return "" + + +def wait_for_real_cert(hostname, max_wait=50): + """Traefik yeni bir hostname icin ilk ACME denemesini DNS/Cloudflare + tam hazir olmadan yapabiliyor ve kendiliginden hizli tekrar + denemiyor. Bu yuzden: gercek (Let's Encrypt) sertifika gelene kadar + bekle; birkac saniye icinde gelmezse Traefik'i bir kez yeniden + baslatarak ACME denemesini zorla tetikle.""" + deadline = time.time() + max_wait + nudged = False + while time.time() < deadline: + if "Let's Encrypt" in cert_issuer(hostname): + return True + if not nudged and time.time() > deadline - max_wait + 8: + subprocess.run(["docker", "restart", "traefik"]) + nudged = True + time.sleep(3) + time.sleep(2) + return "Let's Encrypt" in cert_issuer(hostname) + + def ensure_dns(hostname): existing = requests.get( f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records", @@ -486,7 +522,23 @@ def api_deploy(): f"{name}:latest", ], check=True) - return jsonify({"status": "deployed", "url": f"https://{hostname}"}), 200 + cert_ready = wait_for_real_cert(hostname) + + return jsonify({ + "status": "deployed", "url": f"https://{hostname}", "cert_ready": cert_ready, + }), 200 + + +@app.route("/api/wait-for-cert", methods=["POST"]) +def api_wait_for_cert(): + if not check_auth(): + return jsonify({"error": "unauthorized"}), 401 + body = request.get_json(force=True) + hostname = body.get("hostname") + if not hostname: + return jsonify({"error": "hostname required"}), 400 + cert_ready = wait_for_real_cert(hostname) + return jsonify({"cert_ready": cert_ready}), 200 @app.route("/api/deploy/", methods=["DELETE"])