Files
deploy-panel/app.py
T
2026-09-23 15:13:36 +03:00

132 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)