diff --git a/app.py b/app.py index a5ac8dc..0ab878f 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,8 @@ import json import os +import re import subprocess +from datetime import datetime, timezone import requests from flask import Flask, jsonify, redirect, render_template_string, request @@ -13,20 +15,50 @@ 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") +ROUTER_RULE_RE = re.compile(r"^traefik\.http\.routers\.[^.]+\.rule$") + def check_auth(): auth = request.headers.get("Authorization", "") return auth == f"Bearer {API_TOKEN}" -def extract_hostname(labels, name): - key = f"traefik.http.routers.{name}.rule" - rule = labels.get(key, "") - if "`" in rule: - return rule.split("`")[1] +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] return None +def human_relative(iso_ts): + if not iso_ts or iso_ts.startswith("0001-01-01"): + return None + try: + dt = datetime.fromisoformat(iso_ts.replace("Z", "+00:00")) + except ValueError: + return None + secs = int((datetime.now(timezone.utc) - dt).total_seconds()) + if secs < 60: + return f"{secs} sn önce" + mins = secs // 60 + if mins < 60: + return f"{mins} dk önce" + hours = mins // 60 + if hours < 24: + return f"{hours} sa önce" + days = hours // 24 + return f"{days} gün önce" + + +def inspect_all(cid): + return json.loads(subprocess.run( + ["docker", "inspect", cid], capture_output=True, text=True, check=True + ).stdout)[0] + + def list_projects(): out = subprocess.run( ["docker", "ps", "-a", "--filter", "label=traefik.enable=true", "--format", "{{.ID}}"], @@ -35,18 +67,19 @@ def list_projects(): projects = [] for cid in out: - info = json.loads(subprocess.run( - ["docker", "inspect", cid], capture_output=True, text=True, check=True - ).stdout)[0] + info = inspect_all(cid) name = info["Name"].lstrip("/") if name == SELF_NAME: continue labels = info["Config"]["Labels"] or {} - hostname = extract_hostname(labels, name) + state = info["State"] + status = state["Status"] + when = human_relative(state.get("StartedAt") if status == "running" else state.get("FinishedAt")) projects.append({ "name": name, - "hostname": hostname, - "status": info["State"]["Status"], + "hostname": extract_hostname(labels), + "status": status, + "since": when, "image": info["Config"]["Image"], }) projects.sort(key=lambda p: p["name"]) @@ -59,7 +92,7 @@ def find_container_hostname(name): return None info = json.loads(result.stdout)[0] labels = info["Config"]["Labels"] or {} - return extract_hostname(labels, name) + return extract_hostname(labels) def ensure_dns(hostname): @@ -103,23 +136,36 @@ TEMPLATE = """ Deploy Panel +

Deploy Panel

+
{{ projects|length }} proje · 20 saniyede bir otomatik yenilenir · şimdi yenile
{% if projects %} @@ -127,13 +173,22 @@ TEMPLATE = """ - + {% endfor %} @@ -145,12 +200,50 @@ TEMPLATE = """ """ +LOGS_TEMPLATE = """ + + + +{{ name }} - Loglar + + + + + ← panele dön +

{{ name }} — son {{ lines }} satır

+
{{ logs }}
+ + +""" + @app.route("/") def index(): return render_template_string(TEMPLATE, projects=list_projects()) +@app.route("/logs/") +def logs_ui(name): + result = subprocess.run( + ["docker", "logs", "--tail", "150", name], + capture_output=True, text=True, + ) + output = (result.stdout or "") + (result.stderr or "") + return render_template_string(LOGS_TEMPLATE, name=name, lines=150, logs=output or "(log yok)") + + +@app.route("/restart/", methods=["POST"]) +def restart_ui(name): + subprocess.run(["docker", "restart", name]) + return redirect("/") + + @app.route("/delete/", methods=["POST"]) def delete_ui(name): projects = {p["name"]: p for p in list_projects()}
ProjeAdresDurumİmaj
{{ p.name }} {% if p.hostname %}{{ p.hostname }}{% else %}—{% endif %}{{ p.status }} + {{ p.status }} + {% if p.since %}{{ p.since }}{% endif %} + {{ p.image }} -
- -
+
+ Loglar +
+ +
+
+ +
+