Add order history, live proxy test, SQLite rate limiter (cross-worker + real client IP)
This commit is contained in:
45
app.py
45
app.py
@@ -18,6 +18,25 @@ CFG = db.CFG
|
||||
app = Flask(__name__)
|
||||
app.secret_key = CFG["secret_key"]
|
||||
|
||||
# simple SQLite-backed rate limiter (shared across gunicorn workers)
|
||||
def _client_ip():
|
||||
xff = request.headers.get("X-Forwarded-For", "")
|
||||
return xff.split(",")[0].strip() if xff else (request.remote_addr or "?")
|
||||
|
||||
|
||||
def _rate_ok(ip, limit=10, window=60):
|
||||
now = time.time()
|
||||
d = db.get_db()
|
||||
d.execute("DELETE FROM rate_limits WHERE ts < ?", (now - window,))
|
||||
count = d.execute("SELECT COUNT(*) c FROM rate_limits WHERE ip=?", (ip,)).fetchone()["c"]
|
||||
if count >= limit:
|
||||
d.close()
|
||||
return False
|
||||
d.execute("INSERT INTO rate_limits (ip, ts) VALUES (?,?)", (ip, now))
|
||||
d.commit()
|
||||
d.close()
|
||||
return True
|
||||
|
||||
BTCPAY = CFG["btcpay"]
|
||||
PROXY = CFG["proxy_public"]
|
||||
SITE_HOST = CFG.get("site_host", "clean-proxys.thetempleofdoom.com")
|
||||
@@ -229,11 +248,33 @@ def dashboard():
|
||||
d = db.get_db()
|
||||
used = d.execute("SELECT COALESCE(SUM(bytes),0) s FROM usage_log WHERE user_id=?", (u["id"],)).fetchone()["s"]
|
||||
deds = [dict(r) for r in d.execute("SELECT * FROM dedicated_ips WHERE user_id=? ORDER BY id DESC", (u["id"],)).fetchall()]
|
||||
orders = [dict(r) for r in d.execute(
|
||||
"SELECT o.*, p.name plan_name, p.gb plan_gb FROM orders o LEFT JOIN plans p ON o.plan_id=p.id "
|
||||
"WHERE o.user_id=? ORDER BY o.id DESC LIMIT 20", (u["id"],)).fetchall()]
|
||||
sessions = [dict(r) for r in d.execute(
|
||||
"SELECT * FROM usage_log WHERE user_id=? ORDER BY id DESC LIMIT 10", (u["id"],)).fetchall()]
|
||||
d.close()
|
||||
socks = f"socks5://{u['username']}:{u['proxy_password_plain']}@{PROXY['host']}:{PROXY['socks_port']}"
|
||||
http = f"http://{u['username']}:{u['proxy_password_plain']}@{PROXY['host']}:{PROXY['http_port']}"
|
||||
return render_template("dashboard.html", user=u, used_gb=used / 1e9,
|
||||
socks=socks, http=http, dedicated=deds)
|
||||
socks=socks, http=http, dedicated=deds, orders=orders, sessions=sessions)
|
||||
|
||||
|
||||
@app.route("/dashboard/test")
|
||||
@login_required
|
||||
def dashboard_test():
|
||||
"""Live proxy check: egress through the customer's own creds via the gateway."""
|
||||
u = current_user()
|
||||
proxy = f"socks5h://{u['username']}:{u['proxy_password_plain']}@127.0.0.1:{PROXY['socks_port']}"
|
||||
try:
|
||||
import subprocess
|
||||
r = subprocess.run(["curl", "-s", "--max-time", "20", "-x", proxy,
|
||||
"http://ip-api.com/json?fields=query,country,city,isp,status"],
|
||||
capture_output=True, text=True, timeout=25)
|
||||
return app.response_class(r.stdout or '{"status":"fail","message":"no response"}',
|
||||
mimetype="application/json")
|
||||
except Exception as e:
|
||||
return jsonify({"status": "fail", "message": str(e)}), 502
|
||||
|
||||
|
||||
@app.route("/dashboard/location", methods=["POST"])
|
||||
@@ -490,6 +531,8 @@ def api_plans():
|
||||
@app.route("/api/v1/proxy", methods=["POST"])
|
||||
def api_proxy():
|
||||
"""Agent-facing one-call purchase: create account + invoice, return creds + checkout URL."""
|
||||
if not _rate_ok(_client_ip(), limit=10, window=60):
|
||||
return jsonify({"error": "rate limited"}), 429
|
||||
data = request.get_json(silent=True) or {}
|
||||
username = (data.get("username") or "").strip()
|
||||
password = data.get("password") or ""
|
||||
|
||||
Reference in New Issue
Block a user