Add agent one-call purchase API: POST /api/v1/proxy + GET /api/v1/order/<id>
This commit is contained in:
70
app.py
70
app.py
@@ -385,6 +385,8 @@ Sells residential SOCKS5 and HTTP CONNECT proxies with global geo-targeting
|
||||
## API
|
||||
- GET /api/v1/info -> service info (JSON)
|
||||
- GET /api/v1/plans -> residential GB plans (JSON)
|
||||
- POST /api/v1/proxy -> one-call purchase (JSON: plan_id, country, city, username, password) -> creds + BTCPay checkout URL
|
||||
- GET /api/v1/order/<id> -> payment status (pending/paid)
|
||||
- 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
|
||||
@@ -433,6 +435,74 @@ def api_plans():
|
||||
return jsonify({"plans": ps})
|
||||
|
||||
|
||||
@app.route("/api/v1/proxy", methods=["POST"])
|
||||
def api_proxy():
|
||||
"""Agent-facing one-call purchase: create account + invoice, return creds + checkout URL."""
|
||||
data = request.get_json(silent=True) or {}
|
||||
username = (data.get("username") or "").strip()
|
||||
password = data.get("password") or ""
|
||||
plan_id = data.get("plan_id") or data.get("plan")
|
||||
country = (data.get("country") or "").strip().upper()
|
||||
city = (data.get("city") or "").strip()
|
||||
|
||||
d = db.get_db()
|
||||
plan = d.execute("SELECT * FROM plans WHERE id=?", (plan_id,)).fetchone() if plan_id else None
|
||||
if not plan:
|
||||
plan = d.execute("SELECT * FROM plans WHERE active=1 ORDER BY gb LIMIT 1").fetchone()
|
||||
if not plan:
|
||||
d.close()
|
||||
return jsonify({"error": "no plans"}), 500
|
||||
|
||||
user_row = d.execute("SELECT * FROM users WHERE username=?", (username,)).fetchone() if username else None
|
||||
if user_row is None:
|
||||
username = username or ("u" + db.random_token(10))
|
||||
password = password or db.random_token(16)
|
||||
proxy_pass = db.random_token(18)
|
||||
d.execute("INSERT INTO users (username,password_hash,proxy_password_hash,proxy_password_plain,"
|
||||
"active,country,city,sticky,balance_gb,created_at) VALUES (?,?,?,?,0,?,?,1,0,?)",
|
||||
(username, db.scrypt_hash(password), db.scrypt_hash(proxy_pass), proxy_pass,
|
||||
country or "US", city, int(time.time())))
|
||||
d.commit()
|
||||
uid = d.execute("SELECT id FROM users WHERE username=?", (username,)).fetchone()["id"]
|
||||
else:
|
||||
uid = user_row["id"]
|
||||
proxy_pass = user_row["proxy_password_plain"]
|
||||
if country:
|
||||
d.execute("UPDATE users SET country=?, city=? WHERE id=?", (country, city or "", uid))
|
||||
d.commit()
|
||||
|
||||
order_id = db.random_token(12)
|
||||
d.execute("INSERT INTO orders (user_id,plan_id,invoice_id,amount_sats,status,created_at) "
|
||||
"VALUES (?,?,?,?,'pending',?)",
|
||||
(uid, plan["id"], order_id, plan["price_sats"], int(time.time())))
|
||||
d.commit()
|
||||
d.close()
|
||||
inv = create_invoice(plan["price_sats"], order_id)
|
||||
|
||||
return jsonify({
|
||||
"username": username,
|
||||
"password": password,
|
||||
"proxy_password": proxy_pass,
|
||||
"socks5": f"socks5://{username}:{proxy_pass}@{PROXY['host']}:{PROXY['socks_port']}",
|
||||
"http": f"http://{username}:{proxy_pass}@{PROXY['host']}:{PROXY['http_port']}",
|
||||
"country": country or "US",
|
||||
"city": city,
|
||||
"plan": {"id": plan["id"], "name": plan["name"], "gb": plan["gb"], "price_sats": plan["price_sats"]},
|
||||
"invoice": {"id": order_id, "checkout_url": inv["checkoutLink"], "amount_sats": plan["price_sats"]},
|
||||
"status_url": f"https://{SITE_HOST}/api/v1/order/{order_id}",
|
||||
})
|
||||
|
||||
|
||||
@app.route("/api/v1/order/<order_id>")
|
||||
def api_order_status(order_id):
|
||||
d = db.get_db()
|
||||
o = d.execute("SELECT * FROM orders WHERE invoice_id=?", (order_id,)).fetchone()
|
||||
d.close()
|
||||
if not o:
|
||||
return jsonify({"error": "not found"}), 404
|
||||
return jsonify({"order_id": order_id, "status": o["status"], "amount_sats": o["amount_sats"]})
|
||||
|
||||
|
||||
@app.route("/sitemap.xml")
|
||||
def sitemap():
|
||||
urls = [f"https://{SITE_HOST}/{p}" for p in ["", "plans", "signup", "login", "llms.txt"]]
|
||||
|
||||
27
router-forward.sh
Normal file
27
router-forward.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# Add Pleiades proxy port-forwards (CT777 .178): SOCKS5 :1080 + HTTP :8080.
|
||||
# Appends to the JFFS firewall-start hook (reboot-persistent) + applies now.
|
||||
|
||||
cp /jffs/scripts/firewall-start /jffs/scripts/firewall-start.bak-pleiades-$(date +%s)
|
||||
|
||||
cat >> /jffs/scripts/firewall-start << 'EOF'
|
||||
# Pleiades proxy store (CT777 .178): SOCKS5 :1080 + HTTP :8080 inbound
|
||||
iptables -t nat -D VSERVER -p tcp --dport 1080 -j DNAT --to-destination 10.30.20.178:1080 2>/dev/null
|
||||
iptables -t nat -A VSERVER -p tcp --dport 1080 -j DNAT --to-destination 10.30.20.178:1080
|
||||
iptables -I FORWARD 1 -p tcp -d 10.30.20.178 --dport 1080 -j ACCEPT
|
||||
iptables -t nat -D VSERVER -p tcp --dport 8080 -j DNAT --to-destination 10.30.20.178:8080 2>/dev/null
|
||||
iptables -t nat -A VSERVER -p tcp --dport 8080 -j DNAT --to-destination 10.30.20.178:8080
|
||||
iptables -I FORWARD 1 -p tcp -d 10.30.20.178 --dport 8080 -j ACCEPT
|
||||
EOF
|
||||
|
||||
# apply immediately
|
||||
for P in "1080:1080" "8080:8080"; do
|
||||
EXT=${P%%:*}; DPT=${P##*:}
|
||||
iptables -t nat -A VSERVER -p tcp --dport $EXT -j DNAT --to-destination 10.30.20.178:$DPT
|
||||
iptables -I FORWARD 1 -p tcp -d 10.30.20.178 --dport $DPT -j ACCEPT
|
||||
done
|
||||
|
||||
echo "=== VSERVER (pleiades) ==="
|
||||
iptables -t nat -S VSERVER | grep -E "dport 1080|dport 8080"
|
||||
echo "=== FORWARD (pleiades) ==="
|
||||
iptables -S FORWARD | grep "10.30.20.178"
|
||||
Reference in New Issue
Block a user