add app.py
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user