Files
pleiades/app.py

449 lines
16 KiB
Python

#!/usr/bin/env python3
"""Pleiades storefront — Flask app: signup/login, plans, BTCPay checkout, dashboard."""
import base64
import hashlib
import hmac
import json
import time
from functools import wraps
import requests
from flask import (Flask, abort, flash, jsonify, redirect, render_template,
request, session, url_for)
import db
import iproyal
CFG = db.CFG
app = Flask(__name__)
app.secret_key = CFG["secret_key"]
BTCPAY = CFG["btcpay"]
PROXY = CFG["proxy_public"]
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 = {
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",
"plans": {30: (22, 22000), 60: (24, 44000), 90: (25, 66000)}},
}
# ---------- auth ----------
def current_user():
uid = session.get("uid")
if not uid:
return None
d = db.get_db()
row = d.execute("SELECT * FROM users WHERE id=?", (uid,)).fetchone()
d.close()
return dict(row) if row else None
def login_required(f):
@wraps(f)
def wrapper(*a, **kw):
if not current_user():
flash("Log in first.", "warn")
return redirect(url_for("login"))
return f(*a, **kw)
return wrapper
# ---------- BTCPay ----------
def create_invoice(amount_sats, order_id, meta=None):
data = {
"amount": amount_sats / 1e8,
"currency": "BTC",
"checkout": {
"redirectURL": f"https://{SITE_HOST}/order/{order_id}",
"redirectAutomatically": True,
},
"metadata": {"orderId": order_id},
}
if meta:
data["metadata"].update(meta)
r = requests.post(
f"{BTCPAY['url']}/api/v1/stores/{BTCPAY['store_id']}/invoices",
headers={"Authorization": f"token {BTCPAY['api_key']}"},
json=data, timeout=20, verify=False,
)
r.raise_for_status()
return r.json()
def verify_webhook():
sig = request.headers.get("BTCPay-Sig", "")
if not sig.startswith("sha256="):
return False
expected = sig.split("=", 1)[1]
calc = hmac.new(BTCPAY["webhook_secret"].encode(), request.get_data(),
hashlib.sha256).hexdigest()
return hmac.compare_digest(calc, expected)
# ---------- routes ----------
@app.route("/")
def index():
return render_template("index.html", user=current_user())
@app.route("/signup", methods=["GET", "POST"])
def signup():
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "")
if len(username) < 3 or len(password) < 6:
flash("Username 3+ chars, password 6+ chars.", "warn")
return render_template("signup.html")
d = db.get_db()
exists = d.execute("SELECT 1 FROM users WHERE username=?", (username,)).fetchone()
if exists:
d.close()
flash("Username taken.", "warn")
return render_template("signup.html")
proxy_pass = db.random_token(18)
d.execute(
"INSERT INTO users (username,password_hash,proxy_password_hash,proxy_password_plain,"
"active,region,country,city,sticky,balance_gb,created_at) VALUES (?,?,?,?,0,'','US','',1,0,?)",
(username, db.scrypt_hash(password), db.scrypt_hash(proxy_pass), proxy_pass,
int(time.time())))
d.commit()
uid = d.execute("SELECT id FROM users WHERE username=?", (username,)).fetchone()["id"]
d.close()
session["uid"] = uid
flash(f"Account created. Proxy password: {proxy_pass} (save it)", "ok")
return redirect(url_for("dashboard"))
return render_template("signup.html")
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "")
d = db.get_db()
row = d.execute("SELECT * FROM users WHERE username=?", (username,)).fetchone()
d.close()
if row and db.verify_hash(password, row["password_hash"]):
session["uid"] = row["id"]
return redirect(url_for("dashboard"))
flash("Bad credentials.", "warn")
return render_template("login.html")
@app.route("/logout")
def logout():
session.clear()
return redirect(url_for("index"))
@app.route("/plans")
def plans():
d = db.get_db()
ps = [dict(r) for r in d.execute("SELECT * FROM plans WHERE active=1 ORDER BY gb")]
d.close()
return render_template("plans.html", plans=ps, user=current_user())
@app.route("/buy/<int:plan_id>", methods=["POST"])
@login_required
def buy(plan_id):
d = db.get_db()
plan = d.execute("SELECT * FROM plans WHERE id=?", (plan_id,)).fetchone()
d.close()
if not plan:
abort(404)
order_id = db.random_token(12)
d = db.get_db()
d.execute("INSERT INTO orders (user_id,plan_id,invoice_id,amount_sats,status,created_at) "
"VALUES (?,?,?,?,'pending',?)",
(current_user()["id"], plan_id, order_id, plan["price_sats"], int(time.time())))
d.commit()
d.close()
inv = create_invoice(plan["price_sats"], order_id)
return redirect(inv["checkoutLink"])
@app.route("/order/<order_id>")
@login_required
def order(order_id):
d = db.get_db()
o = d.execute("SELECT * FROM orders WHERE invoice_id=? AND user_id=?",
(order_id, current_user()["id"])).fetchone()
d.close()
if not o:
abort(404)
return render_template("order.html", order=dict(o), user=current_user())
@app.route("/webhook/btcpay", methods=["POST"])
def webhook_btcpay():
if not verify_webhook():
abort(401)
payload = request.get_json(force=True)
if payload.get("type") not in ("InvoiceSettled", "InvoiceProcessing"):
return "ok"
order_id = (payload.get("metadata") or {}).get("orderId")
kind = (payload.get("metadata") or {}).get("kind")
if not order_id:
return "ok"
d = db.get_db()
if kind == "dedicated":
row = d.execute("SELECT * FROM dedicated_ips WHERE invoice_id=? AND status='pending_payment'",
(order_id,)).fetchone()
if row:
try:
plan_id = DEDICATED_PRODUCTS[row["product_id"]]["plans"][row["plan_days"]][0]
o = iproyal.create_order(row["product_id"], plan_id, row["location_id"], 1)
oid = o.get("id") or o.get("order_id") or (o.get("data") or {}).get("id")
d.execute("UPDATE dedicated_ips SET status='provisioning', iproyal_order_id=? WHERE id=?",
(oid, row["id"]))
d.commit()
app.logger.info("dedicated IPRoyal order %s created for dedicated_ips #%s", oid, row["id"])
except Exception as e:
app.logger.error("dedicated order failed: %s", e)
d.execute("UPDATE dedicated_ips SET status='failed' WHERE id=?", (row["id"],))
d.commit()
d.close()
return "ok"
o = d.execute("SELECT * FROM orders WHERE invoice_id=?", (order_id,)).fetchone()
if o and o["status"] != "paid":
plan = d.execute("SELECT * FROM plans WHERE id=?", (o["plan_id"],)).fetchone()
d.execute("UPDATE orders SET status='paid', paid_at=? WHERE id=?", (int(time.time()), o["id"]))
d.execute("UPDATE users SET balance_gb=balance_gb+?, active=1 WHERE id=?",
(plan["gb"], o["user_id"]))
d.commit()
d.close()
return "ok"
@app.route("/dashboard")
@login_required
def dashboard():
u = current_user()
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()]
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)
@app.route("/dashboard/location", methods=["POST"])
@login_required
def set_location():
u = current_user()
country = request.form.get("country", "").strip().upper()
city = request.form.get("city", "").strip()
region = request.form.get("region", "").strip()
sticky = 1 if request.form.get("sticky") else 0
d = db.get_db()
d.execute("UPDATE users SET country=?, city=?, region=?, sticky=? WHERE id=?",
(country, city, region, sticky, u["id"]))
d.commit()
d.close()
flash("Location updated.", "ok")
return redirect(url_for("dashboard"))
@app.route("/dashboard/rotate", methods=["POST"])
@login_required
def rotate():
u = current_user()
newpass = db.random_token(18)
d = db.get_db()
d.execute("UPDATE users SET proxy_password_hash=?, proxy_password_plain=? WHERE id=?",
(db.scrypt_hash(newpass), newpass, u["id"]))
d.commit()
d.close()
flash(f"New proxy password: {newpass}", "ok")
return redirect(url_for("dashboard"))
# ---------- dedicated IPs ----------
@app.route("/dedicated")
def dedicated():
dc_locs = isp_locs = []
try:
dc_locs = iproyal.list_locations(3)
isp_locs = iproyal.list_locations(9)
except Exception as e:
flash(f"Location catalog unavailable: {e}", "warn")
return render_template("dedicated.html", products=DEDICATED_PRODUCTS,
dc_locs=dc_locs, isp_locs=isp_locs, user=current_user())
@app.route("/dedicated/locations")
def dedicated_locations():
pid = request.args.get("product", 3, type=int)
try:
return jsonify({"locations": [{"id": i, "name": n, "in_stock": s}
for i, n, s in iproyal.list_locations(pid)]})
except Exception as e:
return jsonify({"error": str(e)}), 502
@app.route("/dedicated/buy", methods=["POST"])
@login_required
def dedicated_buy():
pid = int(request.form.get("product", 3))
days = int(request.form.get("days", 30))
loc_id = int(request.form.get("location_id", 0))
loc_name = request.form.get("location_name", "")
if pid not in DEDICATED_PRODUCTS or days not in DEDICATED_PRODUCTS[pid]["plans"]:
flash("Bad product or duration.", "warn")
return redirect(url_for("dedicated"))
if not loc_id or not loc_name:
flash("Pick a location.", "warn")
return redirect(url_for("dedicated"))
_, price = DEDICATED_PRODUCTS[pid]["plans"][days]
oid = db.random_token(12)
d = db.get_db()
d.execute("INSERT INTO dedicated_ips (user_id,product_id,product_name,plan_days,location_id,"
"location_name,invoice_id,price_sats,status,created_at) VALUES (?,?,?,?,?,?,?,?,?,?)",
(current_user()["id"], pid, DEDICATED_PRODUCTS[pid]["name"], days, loc_id, loc_name,
oid, price, "pending_payment", int(time.time())))
d.commit()
d.close()
inv = create_invoice(price, oid, meta={"kind": "dedicated"})
return redirect(inv["checkoutLink"])
@app.route("/dedicated/poll", methods=["POST"])
def dedicated_poll():
"""Admin/agent hook: poll provisioning for pending dedicated orders."""
ok, failed = poll_provisioning()
return jsonify({"provisioned": ok, "failed": failed})
def poll_provisioning():
"""Check pending IPRoyal orders, move confirmed ones to active. Returns (ok, failed)."""
d = db.get_db()
pending = d.execute("SELECT * FROM dedicated_ips WHERE status IN ('provisioning')").fetchall()
d.close()
ok = failed = 0
for row in pending:
try:
o = iproyal.get_order(row["iproyal_order_id"])
status = o.get("status")
if status == "confirmed":
pd = o.get("proxy_data") or {}
proxies = pd.get("proxies") or []
ports = pd.get("ports") or {}
if proxies:
p = proxies[0]
host = p.get("ip") or ""
user = p.get("username") or ""
pw = p.get("password") or ""
socks_port = str(ports.get("socks5", ""))
http_port = str(ports.get("http|https", ""))
d = db.get_db()
d.execute("UPDATE dedicated_ips SET status='active', proxy_host=?, proxy_port=?,"
"http_port=?, proxy_user=?, proxy_pass=?, expires_at=? WHERE id=?",
(host, socks_port, http_port, user, pw,
int(time.time()) + row["plan_days"] * 86400, row["id"]))
d.commit()
d.close()
ok += 1
elif status in ("refunded", "expired"):
d = db.get_db()
d.execute("UPDATE dedicated_ips SET status='failed' WHERE id=?", (row["id"],))
d.commit()
d.close()
failed += 1
except Exception as e:
app.logger.warning("poll order %s failed: %s", row["iproyal_order_id"], e)
return ok, failed
# ---------- agent discovery ----------
@app.route("/llms.txt")
def llms_txt():
body = f"""# {SITE_HOST}
> Clean residential SOCKS5/HTTP proxies. No KYC. Crypto checkout. Encrypted backend.
## What this service does
Sells residential SOCKS5 and HTTP CONNECT proxies with global geo-targeting
(country/city/region). Buy with Bitcoin (no KYC). Instant activation.
## Key facts
- Protocol: SOCKS5 (port {PROXY['socks_port']}), HTTP CONNECT (port {PROXY['http_port']})
- Auth: per-user username + password
- Geo: country, city, region targeting
- Sessions: sticky or rotating
- Billing: pay-per-GB, Bitcoin (BTCPay)
- Signup: username + password only (no email, no KYC)
## API
- GET /api/v1/info -> service info (JSON)
- GET /api/v1/plans -> residential GB plans (JSON)
- GET /api/v1/dedicated/locations?product=9 -> ISP dedicated locations (JSON)
- GET /api/v1/dedicated/locations?product=3 -> datacenter dedicated locations (JSON)
- Human signup: {SITE_HOST}/signup
- Dedicated IPs: {SITE_HOST}/dedicated (datacenter $1.80/30d + ISP $4/30d resold at markup)
## Connect (after signup + top-up)
socks5://USER:PASS@{PROXY['host']}:{PROXY['socks_port']}
"""
return app.response_class(body, mimetype="text/plain")
@app.route("/robots.txt")
def robots():
return app.response_class(
"User-agent: *\nAllow: /\nSitemap: https://%s/sitemap.xml\n" % SITE_HOST,
mimetype="text/plain")
@app.route("/api/v1/info")
def api_info():
return jsonify({
"name": "Clean Proxys",
"host": SITE_HOST,
"protocols": ["socks5", "http"],
"socks_port": PROXY["socks_port"],
"http_port": PROXY["http_port"],
"auth": "username:password",
"billing": "bitcoin (btcpay), pay-per-GB",
"kyc": False,
"signup": f"https://{SITE_HOST}/signup",
"docs": f"https://{SITE_HOST}/llms.txt",
"dedicated_ips": {
"products": ["datacenter", "isp"],
"isp_locations": f"https://{SITE_HOST}/dedicated/locations?product=9",
"dc_locations": f"https://{SITE_HOST}/dedicated/locations?product=3",
"browse": f"https://{SITE_HOST}/dedicated",
},
})
@app.route("/api/v1/plans")
def api_plans():
d = db.get_db()
ps = [dict(r) for r in d.execute("SELECT id,name,gb,price_sats FROM plans WHERE active=1 ORDER BY gb")]
d.close()
return jsonify({"plans": ps})
@app.route("/sitemap.xml")
def sitemap():
urls = [f"https://{SITE_HOST}/{p}" for p in ["", "plans", "signup", "login", "llms.txt"]]
body = '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
for u in urls:
body += f" <url><loc>{u}</loc></url>\n"
body += "</urlset>"
return app.response_class(body, mimetype="application/xml")
if __name__ == "__main__":
db.init_db()
app.run(host="127.0.0.1", port=5000, debug=False)