Self-serve proxy username+password change, config-driven dedicated pricing
This commit is contained in:
50
app.py
50
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():
|
||||
|
||||
@@ -30,8 +30,15 @@
|
||||
<p class="muted" style="font-size:12px;margin-bottom:0">Proxy password is unique to you. Use it for proxy auth, not your web password.</p>
|
||||
<button class="btn btn-ghost" id="testbtn" onclick="testProxy()" style="margin-top:12px">⚡ Test my proxy</button>
|
||||
<pre class="code" id="testout" style="display:none;margin-top:10px"></pre>
|
||||
<form method="post" action="/dashboard/rotate" style="margin-top:14px">
|
||||
<input type="submit" value="Rotate proxy password">
|
||||
<form method="post" action="/dashboard/credentials" style="margin-top:14px">
|
||||
<label>Proxy username</label>
|
||||
<input type="text" name="username" value="{{ user.username }}" autocomplete="off">
|
||||
<label>Proxy password</label>
|
||||
<div class="copy" style="margin:0">
|
||||
<input type="text" name="proxy_password" id="pwfield" placeholder="leave blank to keep" autocomplete="off">
|
||||
<button type="button" onclick="randompw()">⚡ random</button>
|
||||
</div>
|
||||
<input type="submit" value="Update proxy credentials">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,6 +112,12 @@
|
||||
|
||||
<script>
|
||||
function copy(el){ el.select(); document.execCommand('copy'); }
|
||||
function randompw(){
|
||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789';
|
||||
let s = '';
|
||||
for (let i=0;i<18;i++) s += chars[Math.floor(Math.random()*chars.length)];
|
||||
document.getElementById('pwfield').value = s;
|
||||
}
|
||||
async function testProxy(){
|
||||
const btn = document.getElementById('testbtn');
|
||||
const out = document.getElementById('testout');
|
||||
|
||||
Reference in New Issue
Block a user