diff --git a/.env b/.env index 0a9e621..a0e2aec 100644 --- a/.env +++ b/.env @@ -15,4 +15,10 @@ GOTIFY_PRIORITY=5 # Configurazione Container DATA_VOLUME=./data -RESTART_POLICY=unless-stopped \ No newline at end of file +RESTART_POLICY=unless-stopped + +# ====================== +# Cloudflare UI (READ ONLY) +# ====================== +UI_REFRESH_SECONDS=60 +UI_PORT=8088 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index bf1d091..a416301 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,3 +21,23 @@ services: volumes: - ${DATA_VOLUME}:/config + + cloudflare-ddns-ui: + image: python:3.12-slim + container_name: cloudflare-ddns-ui + restart: unless-stopped + env_file: + - .env + environment: + - CF_API_TOKEN=${CF_API_TOKEN} + - CF_ZONE=${CF_ZONE} + - CF_SUBDOMAINS=${CF_SUBDOMAIN} + - REFRESH_SECONDS=${UI_REFRESH_SECONDS} + volumes: + - ./ui:/app + working_dir: /app + command: > + sh -c "pip install --no-cache-dir flask requests && + python app.py" + ports: + - "${UI_PORT}:8080" diff --git a/ui/app.py b/ui/app.py new file mode 100644 index 0000000..5957813 --- /dev/null +++ b/ui/app.py @@ -0,0 +1,11 @@ +from flask import Flask, render_template +from cloudflare import get_dns_status + +app = Flask(__name__) + +@app.route("/") +def index(): + return render_template("index.html", records=get_dns_status()) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8080) diff --git a/ui/cloudflare.py b/ui/cloudflare.py new file mode 100644 index 0000000..eb01187 --- /dev/null +++ b/ui/cloudflare.py @@ -0,0 +1,42 @@ +import os +import requests + +CF_API_TOKEN = os.getenv("CF_API_TOKEN") +ZONE = os.getenv("CF_ZONE") +SUBDOMAINS = os.getenv("CF_SUBDOMAINS").split(",") + +HEADERS = { + "Authorization": f"Bearer {CF_API_TOKEN}", + "Content-Type": "application/json" +} + +def get_public_ip(): + return requests.get("https://api.ipify.org", timeout=5).text.strip() + +def get_dns_status(): + ip_pubblico = get_public_ip() + out = [] + + zone = requests.get( + "https://api.cloudflare.com/client/v4/zones", + headers=HEADERS, + params={"name": ZONE} + ).json()["result"][0] + + for sub in SUBDOMAINS: + fqdn = f"{sub}.{ZONE}" + dns = requests.get( + f"https://api.cloudflare.com/client/v4/zones/{zone['id']}/dns_records", + headers=HEADERS, + params={"name": fqdn} + ).json()["result"][0] + + out.append({ + "name": fqdn, + "dns_ip": dns["content"], + "public_ip": ip_pubblico, + "proxied": dns["proxied"], + "status": "OK" if dns["content"] == ip_pubblico else "MISMATCH" + }) + + return out diff --git a/ui/templates/index.html b/ui/templates/index.html new file mode 100644 index 0000000..43b173a --- /dev/null +++ b/ui/templates/index.html @@ -0,0 +1,42 @@ + + + + + Cloudflare DDNS Status + + + + + +

Cloudflare DDNS – Stato DNS

+ + + + + + + + + + +{% for r in records %} + + + + + + + +{% endfor %} +
RecordDNS IPIP PubblicoProxyStato
{{ r.name }}{{ r.dns_ip }}{{ r.public_ip }}{{ "ON" if r.proxied else "OFF" }} + {{ r.status }} +
+ + +