# panel.py — Tek Dosya VDS Panel
# Kurulum: pip install flask flask-cors
# Çalıştır: python3 panel.py
# Erişim: http://VDS_IP:8080
import os, re, base64, subprocess, uuid, sys, ast
from flask import Flask, request, jsonify, Response
from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})
UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)
RUNNING = {} # pid -> {file, log, proc}
ANDROID_ONLY = {
"android","pyjnius","jnius","kivy","plyer",
"pydroid","sl4a","androidhelper","chaquopy",
"beeware","briefcase","toga"
}
# ---------- HTML (gömülü) ----------
INDEX_HTML = r"""
VDS Panel
VDS PANEL
Pydroid .py Runner — Telegram Bot Auto-Run
"""
# ---------- Yardımcılar ----------
def find_imports(code: str):
mods = set()
try:
tree = ast.parse(code)
except SyntaxError:
for line in code.splitlines():
m = re.match(r"^\s*(?:import|from)\s+([\w\.]+)", line)
if m: mods.add(m.group(1).split(".")[0])
return mods
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for n in node.names: mods.add(n.name.split(".")[0])
elif isinstance(node, ast.ImportFrom):
if node.module: mods.add(node.module.split(".")[0])
return mods
# ---------- Rotalar ----------
@app.route("/")
def index():
return Response(INDEX_HTML, mimetype="text/html")
@app.route("/ping")
def ping():
return jsonify(status="ok")
@app.route("/upload", methods=["POST","OPTIONS"])
def upload():
if request.method == "OPTIONS":
return ("", 204)
try:
data = request.get_json()
filename = data.get("filename", "script.py")
content_b64 = data.get("content", "")
raw = base64.b64decode(content_b64)
code = raw.decode("utf-8", errors="replace")
blocked = find_imports(code) & ANDROID_ONLY
if blocked:
return jsonify(error=f"Android-only: {', '.join(sorted(blocked))}"), 400
safe = f"{uuid.uuid4().hex[:8]}_{os.path.basename(filename)}"
path = os.path.join(UPLOAD_DIR, safe)
with open(path, "wb") as f:
f.write(raw)
log_path = path + ".log"
log_f = open(log_path, "ab")
proc = subprocess.Popen(
[sys.executable, "-u", path],
stdout=log_f, stderr=log_f,
stdin=subprocess.DEVNULL,
start_new_session=True,
cwd=UPLOAD_DIR
)
RUNNING[proc.pid] = {"file": path, "log": log_path, "proc": proc}
return jsonify(ok=True, pid=proc.pid, file=safe)
except Exception as e:
return jsonify(error=str(e)), 500
@app.route("/processes")
def processes():
return jsonify({pid: i["file"] for pid, i in RUNNING.items() if i["proc"].poll() is None})
@app.route("/logs/")
def logs(pid):
i = RUNNING.get(pid)
if not i: return jsonify(error="pid yok"), 404
with open(i["log"], "r", errors="replace") as f:
return jsonify(log="".join(f.readlines()[-200:]))
if __name__ == "__main__":
print("VDS Panel → http://0.0.0.0:8080")
app.run(host="0.0.0.0", port=8080, debug=False)