add app.py

This commit is contained in:
2026-09-23 15:13:36 +03:00
parent 320e2ccff0
commit 553131a3f2
+131
View File
@@ -0,0 +1,131 @@
import json
import os
import subprocess
import requests
from flask import Flask, redirect, render_template_string
app = Flask(__name__)
CF_API_TOKEN = os.environ["CF_API_TOKEN"]
CF_ZONE_ID = os.environ["CF_ZONE_ID"]
SELF_NAME = os.environ.get("SELF_CONTAINER_NAME", "")
def extract_hostname(labels, name):
key = f"traefik.http.routers.{name}.rule"
rule = labels.get(key, "")
if "`" in rule:
return rule.split("`")[1]
return None
def list_projects():
out = subprocess.run(
["docker", "ps", "-a", "--filter", "label=traefik.enable=true", "--format", "{{.ID}}"],
capture_output=True, text=True, check=True,
).stdout.split()
projects = []
for cid in out:
info = json.loads(subprocess.run(
["docker", "inspect", cid], capture_output=True, text=True, check=True
).stdout)[0]
name = info["Name"].lstrip("/")
if name == SELF_NAME:
continue
labels = info["Config"]["Labels"] or {}
hostname = extract_hostname(labels, name)
projects.append({
"name": name,
"hostname": hostname,
"status": info["State"]["Status"],
"image": info["Config"]["Image"],
})
projects.sort(key=lambda p: p["name"])
return projects
def delete_dns_record(hostname):
if not hostname:
return
r = requests.get(
f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records",
params={"name": hostname},
headers={"Authorization": f"Bearer {CF_API_TOKEN}"},
timeout=10,
).json()
for rec in r.get("result", []):
requests.delete(
f"https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records/{rec['id']}",
headers={"Authorization": f"Bearer {CF_API_TOKEN}"},
timeout=10,
)
TEMPLATE = """
<!doctype html>
<html>
<head>
<title>Deploy Panel</title>
<meta charset="utf-8">
<style>
body { font-family: system-ui, sans-serif; background: #0f172a; color: #e2e8f0; padding: 2.5rem; }
h1 { color: #38bdf8; }
table { width: 100%; border-collapse: collapse; margin-top: 1.5rem; }
td, th { padding: 0.7rem; border-bottom: 1px solid #334155; text-align: left; }
a { color: #38bdf8; text-decoration: none; }
.badge { padding: 0.2rem 0.6rem; border-radius: 4px; font-size: 0.8rem; }
.running { background: #065f46; }
.exited { background: #7f1d1d; }
button { background: #7f1d1d; color: white; border: none; padding: 0.4rem 0.9rem;
border-radius: 4px; cursor: pointer; }
button:hover { background: #991b1b; }
.empty { opacity: 0.6; margin-top: 1.5rem; }
</style>
</head>
<body>
<h1>Deploy Panel</h1>
{% if projects %}
<table>
<tr><th>Proje</th><th>Adres</th><th>Durum</th><th>İmaj</th><th></th></tr>
{% for p in projects %}
<tr>
<td>{{ p.name }}</td>
<td>{% if p.hostname %}<a href="https://{{ p.hostname }}" target="_blank">{{ p.hostname }}</a>{% else %}—{% endif %}</td>
<td><span class="badge {{ p.status }}">{{ p.status }}</span></td>
<td>{{ p.image }}</td>
<td>
<form method="post" action="/delete/{{ p.name }}"
onsubmit="return confirm('{{ p.name }} silinsin mi? Container ve DNS kaydı kalıcı olarak kaldırılacak.');">
<button type="submit">Sil</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<p class="empty">Henüz deploy edilmiş bir proje yok.</p>
{% endif %}
</body>
</html>
"""
@app.route("/")
def index():
return render_template_string(TEMPLATE, projects=list_projects())
@app.route("/delete/<name>", methods=["POST"])
def delete(name):
projects = {p["name"]: p for p in list_projects()}
target = projects.get(name)
if target:
subprocess.run(["docker", "rm", "-f", name])
delete_dns_record(target["hostname"])
return redirect("/")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)