From bc8b7fa5a54f8d44bd61ef7a030f3846cfd695f2 Mon Sep 17 00:00:00 2001 From: akalan Date: Wed, 23 Sep 2026 17:03:04 +0300 Subject: [PATCH] sadelestir: DNS/Traefik sekmeleri kaldirildi, tek sayfa --- app.py | 414 ++++----------------------------------------------------- 1 file changed, 29 insertions(+), 385 deletions(-) diff --git a/app.py b/app.py index 9b65589..d972069 100644 --- a/app.py +++ b/app.py @@ -16,7 +16,6 @@ CF_ZONE_ID = os.environ["CF_ZONE_ID"] SELF_NAME = os.environ.get("SELF_CONTAINER_NAME", "") API_TOKEN = os.environ["PANEL_API_TOKEN"] SERVER_IP = os.environ.get("SERVER_IP", "148.135.181.126") -TRAEFIK_API_URL = os.environ.get("TRAEFIK_API_URL", "http://traefik:8080") ROUTER_RULE_RE = re.compile(r"^traefik\.http\.routers\.[^.]+\.rule$") @@ -27,9 +26,6 @@ def check_auth(): def extract_hostname(labels): - """Scan ALL traefik router rule labels, not just one matching the - container's own name — compose-based containers get auto-generated - docker names that no longer match the Traefik router name.""" for key, value in labels.items(): if ROUTER_RULE_RE.match(key) and "`" in value: return value.split("`")[1] @@ -157,46 +153,6 @@ def list_all_containers(): return containers -def list_images(): - out = subprocess.run( - ["docker", "images", "--format", "{{json .}}"], - capture_output=True, text=True, check=True, - ).stdout.strip().splitlines() - images = [] - for line in out: - if not line: - continue - d = json.loads(line) - repo, tag = d.get("Repository", ""), d.get("Tag", "") - if repo == "" and tag == "": - label = d.get("ID", "")[:19] - else: - label = f"{repo}:{tag}" - images.append({ - "id": d.get("ID"), - "label": label, - "size": d.get("Size"), - "created": d.get("CreatedSince"), - }) - images.sort(key=lambda i: i["label"]) - return images - - -def list_volumes(): - out = subprocess.run( - ["docker", "volume", "ls", "--format", "{{json .}}"], - capture_output=True, text=True, check=True, - ).stdout.strip().splitlines() - volumes = [] - for line in out: - if not line: - continue - d = json.loads(line) - volumes.append({"name": d.get("Name"), "driver": d.get("Driver")}) - volumes.sort(key=lambda v: v["name"]) - return volumes - - def docker_system_df(): out = subprocess.run(["docker", "system", "df"], capture_output=True, text=True).stdout lines = out.strip().splitlines() @@ -255,86 +211,18 @@ def delete_dns_record(hostname): ) -def list_dns_records(): - records = [] - page = 1 - while True: - r = requests.get( - f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records", - params={"page": page, "per_page": 100}, - headers={"Authorization": f"Bearer {CF_API_TOKEN}"}, - timeout=10, - ).json() - records.extend(r.get("result", [])) - info = r.get("result_info", {}) - if page >= info.get("total_pages", 1): - break - page += 1 - records.sort(key=lambda x: (x.get("type", ""), x.get("name", ""))) - return records - - -def create_dns_record(rtype, name, content, proxied, ttl=1): - return requests.post( - f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records", - headers={"Authorization": f"Bearer {CF_API_TOKEN}", "Content-Type": "application/json"}, - json={"type": rtype, "name": name, "content": content, "ttl": int(ttl), "proxied": proxied}, - timeout=10, - ).json() - - -def update_dns_record(record_id, content, proxied): - return requests.patch( - f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records/{record_id}", - headers={"Authorization": f"Bearer {CF_API_TOKEN}", "Content-Type": "application/json"}, - json={"content": content, "proxied": proxied}, - timeout=10, - ).json() - - -def delete_dns_record_by_id(record_id): - requests.delete( - f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records/{record_id}", - headers={"Authorization": f"Bearer {CF_API_TOKEN}"}, - timeout=10, - ) - - -def list_traefik_routers(): - try: - routers = requests.get(f"{TRAEFIK_API_URL}/api/http/routers", timeout=3).json() - services = requests.get(f"{TRAEFIK_API_URL}/api/http/services", timeout=3).json() - except requests.RequestException: - return None - - services_by_name = {s["name"].split("@")[0]: s for s in services} - - result = [] - for r in routers: - if r.get("provider") != "docker": - continue - rule = r.get("rule", "") - hostname = rule.split("`")[1] if "`" in rule else rule - service = services_by_name.get(r.get("service"), {}) - servers = service.get("loadBalancer", {}).get("servers", []) - backend = servers[0].get("url") if servers else None - result.append({ - "name": r.get("name", "").split("@")[0], - "hostname": hostname, - "entrypoints": ", ".join(r.get("entryPoints", [])), - "tls": bool(r.get("tls")), - "status": r.get("status"), - "backend": backend, - }) - result.sort(key=lambda x: x["name"]) - return result - - -BASE_STYLE = """ +TEMPLATE = """ + + + +Deploy Panel + + + + .prune-actions { display: flex; gap: 0.6rem; margin-top: 0.8rem; } +

Deploy Panel

-""" + NAV + """
{{ containers|length }} container · 20 saniyede bir otomatik yenilenir · şimdi yenile
@@ -459,7 +324,7 @@ CONTAINERS_TEMPLATE = """ {% endif %}
+ onsubmit="return confirm('{{ c.name }} silinsin mi?{{ ' DNS kaydı da otomatik kaldırılacak.' if c.hostname else '' }}');">
@@ -470,23 +335,8 @@ CONTAINERS_TEMPLATE = """ {% else %}

Hiç container yok.

{% endif %} - - -""" - -SYSTEM_TEMPLATE = """ - - - -Sistem & Disk - Deploy Panel - - - - -

Deploy Panel

-""" + NAV + """ -
Docker'ın diskte kapladığı alan ve imaj/volume yönetimi
+

Disk Kullanımı

{% for row in df %} @@ -499,159 +349,14 @@ SYSTEM_TEMPLATE = """ {% endfor %}
TürToplamKullanımdaBoyutGeri kazanılabilir
- -

İmajlar ({{ images|length }})

-
- -
- {% if images %} - - - {% for img in images %} - - - - - - - {% endfor %} -
İmajBoyutOluşturulma
{{ img.label }}{{ img.size }}{{ img.created }} -
- -
-
- {% else %} -

Hiç imaj yok.

- {% endif %} - -

Volume'ler ({{ volumes|length }})

-
- -
- {% if volumes %} - - - {% for v in volumes %} - - - - - - {% endfor %} -
İsimDriver
{{ v.name }}{{ v.driver }} -
- -
-
- {% else %} -

Hiç volume yok.

- {% endif %} - - -""" - -TRAEFIK_TEMPLATE = """ - - - -Traefik - Deploy Panel - - - - -

Deploy Panel

-""" + NAV + """ -
Traefik'in kendi API'sinden canlı okunan router/servis listesi
- {% if routers is none %} -

Traefik API'sine ulaşılamadı ({{ api_url }}).

- {% elif routers %} - - - {% for r in routers %} - - - - - - - - - {% endfor %} -
RouterAdresEntrypointTLSDurumBackend
{{ r.name }}{% if r.hostname %}{{ r.hostname }}{% else %}{{ r.hostname }}{% endif %}{{ r.entrypoints }}{{ '✅' if r.tls else '—' }}{{ r.status }}{{ r.backend or '—' }}
- {% else %} -

Aktif router yok.

- {% endif %} - - -""" - -DNS_TEMPLATE = """ - - - -DNS - Deploy Panel - - - - - -

Deploy Panel

-""" + NAV + """ -
akalan.dev · Cloudflare DNS kayıtları · {{ records|length }} kayıt
- -
- - - - - -
- - {% if records %} - - - {% for r in records %} - - - - - - - - {% endfor %} -
TipAdDeğerTuruncu Bulut
{{ r.type }}{{ r.name }} -
- - - -
-
{{ '🟠 açık' if r.proxied else '⚪ kapalı' }} -
- -
-
- {% else %} -

Hiç DNS kaydı yok.

- {% endif %} +
+
+ +
+
+ +
+
""" @@ -682,59 +387,10 @@ LOGS_TEMPLATE = """ @app.route("/") def index(): return render_template_string( - CONTAINERS_TEMPLATE, containers=list_all_containers(), s=server_stats(), page="containers" + TEMPLATE, containers=list_all_containers(), s=server_stats(), df=docker_system_df() ) -@app.route("/system") -def system_ui(): - return render_template_string( - SYSTEM_TEMPLATE, df=docker_system_df(), images=list_images(), volumes=list_volumes(), page="system" - ) - - -@app.route("/traefik") -def traefik_ui(): - return render_template_string( - TRAEFIK_TEMPLATE, routers=list_traefik_routers(), api_url=TRAEFIK_API_URL, page="traefik" - ) - - -@app.route("/dns") -def dns_ui(): - return render_template_string(DNS_TEMPLATE, records=list_dns_records(), page="dns") - - -@app.route("/dns/add", methods=["POST"]) -def dns_add(): - rtype = request.form.get("type", "A") - name = request.form.get("name", "").strip() - content = request.form.get("content", "").strip() - proxied = request.form.get("proxied") == "on" - if name and content: - if name == "@": - name = "akalan.dev" - elif not name.endswith("akalan.dev"): - name = f"{name}.akalan.dev" - create_dns_record(rtype, name, content, proxied) - return redirect("/dns") - - -@app.route("/dns/update/", methods=["POST"]) -def dns_update(record_id): - content = request.form.get("content", "").strip() - proxied = request.form.get("proxied") == "on" - if content: - update_dns_record(record_id, content, proxied) - return redirect("/dns") - - -@app.route("/dns/delete/", methods=["POST"]) -def dns_delete(record_id): - delete_dns_record_by_id(record_id) - return redirect("/dns") - - @app.route("/logs/") def logs_ui(name): result = subprocess.run( @@ -748,19 +404,19 @@ def logs_ui(name): @app.route("/start/", methods=["POST"]) def start_ui(name): subprocess.run(["docker", "start", name]) - return redirect(request.referrer or "/") + return redirect("/") @app.route("/stop/", methods=["POST"]) def stop_ui(name): subprocess.run(["docker", "stop", name]) - return redirect(request.referrer or "/") + return redirect("/") @app.route("/restart/", methods=["POST"]) def restart_ui(name): subprocess.run(["docker", "restart", name]) - return redirect(request.referrer or "/") + return redirect("/") @app.route("/delete/", methods=["POST"]) @@ -768,31 +424,19 @@ def delete_ui(name): hostname = find_container_hostname(name) subprocess.run(["docker", "rm", "-f", name]) delete_dns_record(hostname) - return redirect(request.referrer or "/") - - -@app.route("/images/delete/", methods=["POST"]) -def delete_image(image_id): - subprocess.run(["docker", "rmi", image_id]) - return redirect("/system") + return redirect("/") @app.route("/images/prune", methods=["POST"]) def prune_images(): subprocess.run(["docker", "image", "prune", "-af"]) - return redirect("/system") - - -@app.route("/volumes/delete/", methods=["POST"]) -def delete_volume(name): - subprocess.run(["docker", "volume", "rm", name]) - return redirect("/system") + return redirect("/") @app.route("/volumes/prune", methods=["POST"]) def prune_volumes(): subprocess.run(["docker", "volume", "prune", "-f"]) - return redirect("/system") + return redirect("/") @app.route("/api/ensure-dns", methods=["POST"])