From 15c103eb654ba3b412e07ccd2ca2c670d1de5930 Mon Sep 17 00:00:00 2001 From: drjones Date: Sat, 19 Sep 2026 19:38:01 -0700 Subject: [PATCH] Self-serve proxy username+password change, config-driven dedicated pricing --- app.py | 50 ++++++++++++++++++++++++++++++++++++++-- templates/dashboard.html | 17 ++++++++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index db01399..c1d8042 100644 --- a/app.py +++ b/app.py @@ -43,8 +43,9 @@ SITE_HOST = CFG.get("site_host", "clean-proxys.thetempleofdoom.com") PRICE_USD_PER_GB = CFG.get("price_usd_per_gb", 8.0) # display only -# Dedicated IP catalog: product_id -> {name, plans: {days: (iproyal_plan_id, price_sats)}} -DEDICATED_PRODUCTS = { +# Dedicated IP catalog: product_id -> {name, desc, plans: {days: (iproyal_plan_id, price_sats)}} +# Overridable via config.json "dedicated_products" (JSON keys are strings -> converted). +_DEDICATED_DEFAULT = { 3: {"name": "Datacenter Dedicated", "desc": "Static datacenter IP — budget tier", "plans": {30: (5, 10000), 60: (26, 20000), 90: (27, 30000)}}, 9: {"name": "ISP Dedicated", "desc": "Clean residential-ISP IP — premium tier", @@ -52,6 +53,20 @@ DEDICATED_PRODUCTS = { } +def _load_dedicated_products(): + raw = CFG.get("dedicated_products") + if not raw: + return _DEDICATED_DEFAULT + out = {} + for pid, p in raw.items(): + plans = {int(d): (int(v[0]), int(v[1])) for d, v in p.get("plans", {}).items()} + out[int(pid)] = {"name": p.get("name", ""), "desc": p.get("desc", ""), "plans": plans} + return out or _DEDICATED_DEFAULT + + +DEDICATED_PRODUCTS = _load_dedicated_products() + + # ---------- auth ---------- def current_user(): uid = session.get("uid") @@ -308,6 +323,37 @@ def rotate(): return redirect(url_for("dashboard")) +@app.route("/dashboard/credentials", methods=["POST"]) +@login_required +def change_credentials(): + """Let the customer set their own proxy username and/or password.""" + u = current_user() + new_username = request.form.get("username", "").strip() + new_password = request.form.get("proxy_password", "").strip() + d = db.get_db() + changed = [] + if new_username and new_username != u["username"]: + taken = d.execute("SELECT 1 FROM users WHERE username=? AND id!=?", + (new_username, u["id"])).fetchone() + if taken: + d.close() + flash("Username already taken.", "warn") + return redirect(url_for("dashboard")) + d.execute("UPDATE users SET username=? WHERE id=?", (new_username, u["id"])) + changed.append("username") + if new_password: + d.execute("UPDATE users SET proxy_password_hash=?, proxy_password_plain=? WHERE id=?", + (db.scrypt_hash(new_password), new_password, u["id"])) + changed.append("password") + d.commit() + d.close() + if changed: + flash("Proxy " + " & ".join(changed) + " updated.", "ok") + else: + flash("Nothing changed.", "warn") + return redirect(url_for("dashboard")) + + # ---------- dedicated IPs ---------- @app.route("/dedicated") def dedicated(): diff --git a/templates/dashboard.html b/templates/dashboard.html index eea3a64..951f294 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -30,8 +30,15 @@

Proxy password is unique to you. Use it for proxy auth, not your web password.

-
- + + + + +
+ + +
+
@@ -105,6 +112,12 @@