fix graphics
This commit is contained in:
117
ui/cloudflare.py
117
ui/cloudflare.py
@@ -1,42 +1,103 @@
|
|||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
CF_API_TOKEN = os.getenv("CF_API_TOKEN")
|
# ----------------------------
|
||||||
ZONE = os.getenv("CF_ZONE")
|
# Config da .env
|
||||||
SUBDOMAINS = os.getenv("CF_SUBDOMAINS").split(",")
|
# ----------------------------
|
||||||
|
CF_API_TOKEN = os.getenv("CF_UI_API_TOKEN")
|
||||||
|
CF_ZONE = os.getenv("CF_ZONE")
|
||||||
|
CF_SUBDOMAINS = os.getenv("CF_UI_SUBDOMAINS", "").split(",")
|
||||||
|
PUBLIC_IP_API = os.getenv("PUBLIC_IP_API", "https://api.ipify.org") # opzionale
|
||||||
|
|
||||||
HEADERS = {
|
HEADERS = {
|
||||||
"Authorization": f"Bearer {CF_API_TOKEN}",
|
"Authorization": f"Bearer {CF_API_TOKEN}",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ----------------------------
|
||||||
|
# Funzioni
|
||||||
|
# ----------------------------
|
||||||
def get_public_ip():
|
def get_public_ip():
|
||||||
return requests.get("https://api.ipify.org", timeout=5).text.strip()
|
"""Recupera l'IP pubblico"""
|
||||||
|
try:
|
||||||
|
ip = requests.get(PUBLIC_IP_API, timeout=5).text.strip()
|
||||||
|
return ip
|
||||||
|
except Exception:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
def get_dns_status():
|
def get_zone_id():
|
||||||
ip_pubblico = get_public_ip()
|
"""Recupera l'ID della zona Cloudflare"""
|
||||||
out = []
|
try:
|
||||||
|
resp = requests.get(
|
||||||
zone = requests.get(
|
"https://api.cloudflare.com/client/v4/zones",
|
||||||
"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,
|
headers=HEADERS,
|
||||||
params={"name": fqdn}
|
params={"name": CF_ZONE}
|
||||||
).json()["result"][0]
|
).json()
|
||||||
|
if resp["success"] and resp["result"]:
|
||||||
|
return resp["result"][0]["id"]
|
||||||
|
else:
|
||||||
|
raise Exception(f"Zona '{CF_ZONE}' non trovata o errore API.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] get_zone_id: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
out.append({
|
def get_dns_records():
|
||||||
"name": fqdn,
|
"""Recupera i record DNS dei sottodomini"""
|
||||||
"dns_ip": dns["content"],
|
zone_id = get_zone_id()
|
||||||
"public_ip": ip_pubblico,
|
if not zone_id:
|
||||||
"proxied": dns["proxied"],
|
return []
|
||||||
"status": "OK" if dns["content"] == ip_pubblico else "MISMATCH"
|
|
||||||
})
|
|
||||||
|
|
||||||
return out
|
public_ip = get_public_ip()
|
||||||
|
records = []
|
||||||
|
|
||||||
|
for sub in CF_SUBDOMAINS:
|
||||||
|
fqdn = f"{sub}.{CF_ZONE}"
|
||||||
|
try:
|
||||||
|
resp = requests.get(
|
||||||
|
f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records",
|
||||||
|
headers=HEADERS,
|
||||||
|
params={"name": fqdn}
|
||||||
|
).json()
|
||||||
|
|
||||||
|
if not resp["success"] or not resp["result"]:
|
||||||
|
# Record non trovato
|
||||||
|
records.append({
|
||||||
|
"name": fqdn,
|
||||||
|
"dns_ip": "N/A",
|
||||||
|
"public_ip": public_ip,
|
||||||
|
"proxied": False,
|
||||||
|
"status": "MISSING",
|
||||||
|
"last_updated": datetime.utcnow().isoformat()
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
dns = resp["result"][0]
|
||||||
|
records.append({
|
||||||
|
"name": fqdn,
|
||||||
|
"dns_ip": dns["content"],
|
||||||
|
"public_ip": public_ip,
|
||||||
|
"proxied": dns["proxied"],
|
||||||
|
"status": "OK" if dns["content"] == public_ip else "MISMATCH",
|
||||||
|
"last_updated": dns.get("modified_on", datetime.utcnow().isoformat())
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR] get_dns_records ({fqdn}): {e}")
|
||||||
|
records.append({
|
||||||
|
"name": fqdn,
|
||||||
|
"dns_ip": "ERROR",
|
||||||
|
"public_ip": public_ip,
|
||||||
|
"proxied": False,
|
||||||
|
"status": "ERROR",
|
||||||
|
"last_updated": datetime.utcnow().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
# ----------------------------
|
||||||
|
# Test rapido
|
||||||
|
# ----------------------------
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from pprint import pprint
|
||||||
|
pprint(get_dns_records())
|
||||||
|
|||||||
@@ -1,42 +1,137 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="it">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="UTF-8">
|
||||||
<title>Cloudflare DDNS Status</title>
|
<title>Cloudflare DDNS Status</title>
|
||||||
<meta http-equiv="refresh" content="{{ 60 }}">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<style>
|
<style>
|
||||||
body { background:#0f1115; color:#eee; font-family:sans-serif; padding:20px }
|
/* Stile generale */
|
||||||
table { width:100%; border-collapse:collapse }
|
body {
|
||||||
th, td { padding:10px; border-bottom:1px solid #333 }
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
.ok { color:#4caf50 }
|
background: #121212;
|
||||||
.bad { color:#ff5252 }
|
color: #e0e0e0;
|
||||||
</style>
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
background: #1f1f1f;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.7em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #00bcd4;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0 10px; /* Spazi tra le righe */
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #1f1f1f;
|
||||||
|
color: #00bcd4;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.9em;
|
||||||
|
text-align: center;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
background: #1a1a1a;
|
||||||
|
text-align: center;
|
||||||
|
padding: 12px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
tr:hover td {
|
||||||
|
background: #272727;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 5px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
.ok {
|
||||||
|
background: linear-gradient(45deg, #4caf50, #81c784);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.bad {
|
||||||
|
background: linear-gradient(45deg, #ff5252, #ff8a65);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
color: #9e9e9e;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h2>Cloudflare DDNS – Stato DNS</h2>
|
<header>Cloudflare DDNS – Stato DNS</header>
|
||||||
|
|
||||||
<table>
|
<main>
|
||||||
<tr>
|
<table>
|
||||||
<th>Record</th>
|
<thead>
|
||||||
<th>DNS IP</th>
|
<tr>
|
||||||
<th>IP Pubblico</th>
|
<th>Record</th>
|
||||||
<th>Proxy</th>
|
<th>DNS IP</th>
|
||||||
<th>Stato</th>
|
<th>IP Pubblico</th>
|
||||||
</tr>
|
<th>Proxy</th>
|
||||||
|
<th>Stato</th>
|
||||||
|
<th>Ultimo aggiornamento</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% 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><span class="badge {{ 'ok' if r.status == 'OK' else 'bad' }}">{{ r.status }}</span></td>
|
||||||
|
<td class="time" data-timestamp="{{ r.last_updated }}">{{ r.last_updated_human }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
|
||||||
{% for r in records %}
|
<footer>
|
||||||
<tr>
|
Visualizzazione aggiornata ogni 60 secondi
|
||||||
<td align="center">{{ r.name }}</td>
|
</footer>
|
||||||
<td align="center">{{ r.dns_ip }}</td>
|
|
||||||
<td align="center">{{ r.public_ip }}</td>
|
<script>
|
||||||
<td align="center">{{ "ON" if r.proxied else "OFF" }}</td>
|
function timeAgo(date) {
|
||||||
<td align="center" class="{{ 'ok' if r.status == 'OK' else 'bad' }}">
|
const seconds = Math.floor((new Date() - new Date(date)) / 1000);
|
||||||
{{ r.status }}
|
let interval = Math.floor(seconds / 86400);
|
||||||
</td>
|
if (interval >= 1) return interval + " giorni fa";
|
||||||
</tr>
|
interval = Math.floor(seconds / 3600);
|
||||||
{% endfor %}
|
if (interval >= 1) return interval + " ore fa";
|
||||||
</table>
|
interval = Math.floor(seconds / 60);
|
||||||
|
if (interval >= 1) return interval + " minuti fa";
|
||||||
|
return "pochi secondi fa";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.time').forEach(td => {
|
||||||
|
td.textContent = timeAgo(td.dataset.timestamp);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user