Add Web UI
This commit is contained in:
8
.env
8
.env
@@ -15,4 +15,10 @@ GOTIFY_PRIORITY=5
|
||||
|
||||
# Configurazione Container
|
||||
DATA_VOLUME=./data
|
||||
RESTART_POLICY=unless-stopped
|
||||
RESTART_POLICY=unless-stopped
|
||||
|
||||
# ======================
|
||||
# Cloudflare UI (READ ONLY)
|
||||
# ======================
|
||||
UI_REFRESH_SECONDS=60
|
||||
UI_PORT=8088
|
||||
@@ -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"
|
||||
|
||||
11
ui/app.py
Normal file
11
ui/app.py
Normal file
@@ -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)
|
||||
42
ui/cloudflare.py
Normal file
42
ui/cloudflare.py
Normal file
@@ -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
|
||||
42
ui/templates/index.html
Normal file
42
ui/templates/index.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Cloudflare DDNS Status</title>
|
||||
<meta http-equiv="refresh" content="{{ 60 }}">
|
||||
<style>
|
||||
body { background:#0f1115; color:#eee; font-family:sans-serif; padding:20px }
|
||||
table { width:100%; border-collapse:collapse }
|
||||
th, td { padding:10px; border-bottom:1px solid #333 }
|
||||
.ok { color:#4caf50 }
|
||||
.bad { color:#ff5252 }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>Cloudflare DDNS – Stato DNS</h2>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Record</th>
|
||||
<th>DNS IP</th>
|
||||
<th>IP Pubblico</th>
|
||||
<th>Proxy</th>
|
||||
<th>Stato</th>
|
||||
</tr>
|
||||
|
||||
{% for r in records %}
|
||||
<tr>
|
||||
<td>{{ r.name }}</td>
|
||||
<td>{{ r.dns_ip }}</td>
|
||||
<td>{{ r.public_ip }}</td>
|
||||
<td>{{ "ON" if r.proxied else "OFF" }}</td>
|
||||
<td class="{{ 'ok' if r.status == 'OK' else 'bad' }}">
|
||||
{{ r.status }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user