drjones free-login (auto-provision), SMS live-code auto-poll in browser, rate limiter on hot endpoints, glitch logo + typed boot sequence
This commit is contained in:
71
app.py
71
app.py
@@ -39,7 +39,8 @@ def db():
|
|||||||
CREATE TABLE IF NOT EXISTS balances(user_id INTEGER PRIMARY KEY, cents INTEGER DEFAULT 0);
|
CREATE TABLE IF NOT EXISTS balances(user_id INTEGER PRIMARY KEY, cents INTEGER DEFAULT 0);
|
||||||
CREATE TABLE IF NOT EXISTS apikeys(id INTEGER PRIMARY KEY, user_id INTEGER, key TEXT UNIQUE, label TEXT, created INTEGER, revoked INTEGER DEFAULT 0);
|
CREATE TABLE IF NOT EXISTS apikeys(id INTEGER PRIMARY KEY, user_id INTEGER, key TEXT UNIQUE, label TEXT, created INTEGER, revoked INTEGER DEFAULT 0);
|
||||||
CREATE TABLE IF NOT EXISTS ledger(id INTEGER PRIMARY KEY, user_id INTEGER, delta_cents INTEGER, reason TEXT, ts INTEGER);
|
CREATE TABLE IF NOT EXISTS ledger(id INTEGER PRIMARY KEY, user_id INTEGER, delta_cents INTEGER, reason TEXT, ts INTEGER);
|
||||||
CREATE TABLE IF NOT EXISTS wh_processed(invoice_id TEXT PRIMARY KEY, ts INTEGER);""")
|
CREATE TABLE IF NOT EXISTS wh_processed(invoice_id TEXT PRIMARY KEY, ts INTEGER);
|
||||||
|
CREATE TABLE IF NOT EXISTS rate_hits(bucket TEXT, ip TEXT, ts INTEGER);""")
|
||||||
return con
|
return con
|
||||||
|
|
||||||
# ---------- BILLING CORE (per-call metering for outside users) ----------
|
# ---------- BILLING CORE (per-call metering for outside users) ----------
|
||||||
@@ -76,6 +77,22 @@ def require_paid_key(cents, reason):
|
|||||||
return None, (jsonify({"ok": False, "error": "insufficient balance", "balance_cents": get_balance(uid), "topup": SITE + "/keys"}), 402)
|
return None, (jsonify({"ok": False, "error": "insufficient balance", "balance_cents": get_balance(uid), "topup": SITE + "/keys"}), 402)
|
||||||
return uid, None
|
return uid, None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- RATE LIMITING ----------
|
||||||
|
_RL = {}
|
||||||
|
def rate_limit(bucket, limit, window):
|
||||||
|
"""Sliding-window per-IP limiter. Returns None if ok, else a 429 response."""
|
||||||
|
key = request.headers.get("X-Real-IP") or request.remote_addr or "?"
|
||||||
|
now = time.time()
|
||||||
|
con = db()
|
||||||
|
con.execute("DELETE FROM rate_hits WHERE bucket=? AND ts < ?", (bucket, now-window))
|
||||||
|
n = con.execute("SELECT COUNT(*) c FROM rate_hits WHERE bucket=? AND ip=?", (bucket, key)).fetchone()["c"]
|
||||||
|
if n >= limit:
|
||||||
|
return jsonify({"ok": False, "error": "rate limited — slow down"}), 429
|
||||||
|
con.execute("INSERT INTO rate_hits(bucket,ip,ts) VALUES(?,?,?)", (bucket, key, now))
|
||||||
|
con.commit()
|
||||||
|
return None
|
||||||
|
|
||||||
import ssl as _ssl
|
import ssl as _ssl
|
||||||
_CTX = _ssl.create_default_context()
|
_CTX = _ssl.create_default_context()
|
||||||
_CTX.check_hostname = False
|
_CTX.check_hostname = False
|
||||||
@@ -183,7 +200,7 @@ li{text-align:left;margin:.2rem 0}
|
|||||||
<div id="lbar"></div>
|
<div id="lbar"></div>
|
||||||
<canvas id="space"></canvas><div class="gridlines"></div><div class="vignette" style="--neb1:{{n1}};--neb2:{{n2}}"></div>
|
<canvas id="space"></canvas><div class="gridlines"></div><div class="vignette" style="--neb1:{{n1}};--neb2:{{n2}}"></div>
|
||||||
<header><div class="hbar">
|
<header><div class="hbar">
|
||||||
<a class=logo href=/ >◈ DARK0RBITS</a>
|
<a class=logo href=/ data-t="◈ DARK0RBITS">◈ DARK0RBITS</a>
|
||||||
<div class="dnav">
|
<div class="dnav">
|
||||||
<a href=/ class={{o(home)}}>HOME</a><a href=/ip class={{o(ip)}}>IP</a><a href=/card class={{o(card)}}>CARD</a>
|
<a href=/ class={{o(home)}}>HOME</a><a href=/ip class={{o(ip)}}>IP</a><a href=/card class={{o(card)}}>CARD</a>
|
||||||
<a href=/sms class={{o(sms)}}>SMS</a><a href=/proxy class={{o(proxy)}}>PROXY</a>
|
<a href=/sms class={{o(sms)}}>SMS</a><a href=/proxy class={{o(proxy)}}>PROXY</a>
|
||||||
@@ -428,6 +445,8 @@ def ip_form():
|
|||||||
|
|
||||||
@app.route("/api/ip")
|
@app.route("/api/ip")
|
||||||
def api_ip():
|
def api_ip():
|
||||||
|
r = rate_limit("iptarget", 40, 60)
|
||||||
|
if r: return r
|
||||||
target = param("target")
|
target = param("target")
|
||||||
if target and target.strip():
|
if target and target.strip():
|
||||||
return jsonify(ip_report(target.strip()))
|
return jsonify(ip_report(target.strip()))
|
||||||
@@ -503,6 +522,8 @@ cn.addEventListener('input',function(){{var v=this.value.replace(/\\D/g,'').slic
|
|||||||
|
|
||||||
@app.route("/api/card", methods=["POST"])
|
@app.route("/api/card", methods=["POST"])
|
||||||
def api_card():
|
def api_card():
|
||||||
|
r = rate_limit("card", 30, 60)
|
||||||
|
if r: return r
|
||||||
num = re.sub(r"\D", "", param("num") or "")[:19]
|
num = re.sub(r"\D", "", param("num") or "")[:19]
|
||||||
if not num: return jsonify({"ok": False, "error": "num required"})
|
if not num: return jsonify({"ok": False, "error": "num required"})
|
||||||
ok = luhn_ok(num)
|
ok = luhn_ok(num)
|
||||||
@@ -588,9 +609,22 @@ def sms():
|
|||||||
<form method=post><input type=hidden name=act value=cancel><input name=pid placeholder="order #" style="width:100%"><button style="background:var(--bad);color:#fff">Cancel & refund</button></form></div>
|
<form method=post><input type=hidden name=act value=cancel><input name=pid placeholder="order #" style="width:100%"><button style="background:var(--bad);color:#fff">Cancel & refund</button></form></div>
|
||||||
</div>{msg}
|
</div>{msg}
|
||||||
<div class=card><b>Recent rentals</b><table><tr><th>Number</th><th>Service</th><th>Status</th><th>Order</th><th>Window</th></tr>{hist_rows or '<tr><td colspan=5 style=color:var(--dim)>none yet</td></tr>'}</table></div>
|
<div class=card><b>Recent rentals</b><table><tr><th>Number</th><th>Service</th><th>Status</th><th>Order</th><th>Window</th></tr>{hist_rows or '<tr><td colspan=5 style=color:var(--dim)>none yet</td></tr>'}</table></div>
|
||||||
|
<div class=card id=codesbox style=display:none><b>Live code</b><div id=lcode style="font-size:1.6rem;color:var(--ok);letter-spacing:.2em"></div></div>
|
||||||
<script>
|
<script>
|
||||||
setInterval(function(){{var els=document.querySelectorAll('.cdown');var now=Math.floor(Date.now()/1000);
|
var lastMsg='';
|
||||||
|
setInterval(function(){{
|
||||||
|
var els=document.querySelectorAll('.cdown');var now=Math.floor(Date.now()/1000);
|
||||||
els.forEach(function(e){{var s=e.dataset.exp-now;if(s>0)e.textContent=Math.floor(s/60)+'m '+(s%60)+'s left';else e.textContent='expired'}});}},1000);
|
els.forEach(function(e){{var s=e.dataset.exp-now;if(s>0)e.textContent=Math.floor(s/60)+'m '+(s%60)+'s left';else e.textContent='expired'}});}},1000);
|
||||||
|
setInterval(function(){{
|
||||||
|
fetch('/api/sms/history').then(r=>r.json()).then(rows=>{{
|
||||||
|
rows.filter(r=>r.status==='active').forEach(r=>{{
|
||||||
|
fetch('/api/sms/check?pid='+r.purchase_id).then(x=>x.json()).then(d=>{{
|
||||||
|
if((d.sms||d.code)&&d.sms!==lastMsg){{lastMsg=d.sms||d.code;
|
||||||
|
var box=document.getElementById('codesbox');box.style.display='block';
|
||||||
|
document.getElementById('lcode').textContent=lastMsg;
|
||||||
|
toast('SMS CODE: '+lastMsg);document.title='✉ '+lastMsg;}}
|
||||||
|
}})}});
|
||||||
|
}})}},6000);
|
||||||
</script>
|
</script>
|
||||||
<div class=card style=color:var(--dim)>API: POST /api/sms/rent (service,country) · GET /api/sms/check?pid= · GET /api/sms/cancel?pid= · GET /api/sms/history</div>""" + how(["Pick a service and country, rent — the number is live for 30 minutes exactly.","Use it for any signup/verification. The code arrives as a text.","Poll the order (auto or manual) until the code shows.","Cancel before a code arrives and you get every satoshi back.","Each rental is logged in the recent-rentals table with a live countdown."])
|
<div class=card style=color:var(--dim)>API: POST /api/sms/rent (service,country) · GET /api/sms/check?pid= · GET /api/sms/cancel?pid= · GET /api/sms/history</div>""" + how(["Pick a service and country, rent — the number is live for 30 minutes exactly.","Use it for any signup/verification. The code arrives as a text.","Poll the order (auto or manual) until the code shows.","Cancel before a code arrives and you get every satoshi back.","Each rental is logged in the recent-rentals table with a live countdown."])
|
||||||
return page("sms", body)
|
return page("sms", body)
|
||||||
@@ -683,6 +717,8 @@ def proxy():
|
|||||||
|
|
||||||
@app.route("/api/proxy/test", methods=["POST"])
|
@app.route("/api/proxy/test", methods=["POST"])
|
||||||
def api_proxy_test():
|
def api_proxy_test():
|
||||||
|
r = rate_limit("proxytest", 10, 60)
|
||||||
|
if r: return r
|
||||||
user, pw = param("user") or "", param("pass") or ""
|
user, pw = param("user") or "", param("pass") or ""
|
||||||
pauth = base64.b64encode(f"{user}:{pw}".encode()).decode()
|
pauth = base64.b64encode(f"{user}:{pw}".encode()).decode()
|
||||||
try:
|
try:
|
||||||
@@ -861,6 +897,8 @@ document.getElementById('ie').addEventListener('change',function(){{document.que
|
|||||||
|
|
||||||
@app.route("/api/steg/hide", methods=["POST"])
|
@app.route("/api/steg/hide", methods=["POST"])
|
||||||
def api_steg_hide():
|
def api_steg_hide():
|
||||||
|
r = rate_limit("steg", 20, 60)
|
||||||
|
if r: return r
|
||||||
f = request.files.get("image")
|
f = request.files.get("image")
|
||||||
text = param("text") or ""
|
text = param("text") or ""
|
||||||
if not f or not text: return jsonify({"ok": False, "error": "image + text required"}), 400
|
if not f or not text: return jsonify({"ok": False, "error": "image + text required"}), 400
|
||||||
@@ -875,6 +913,8 @@ def api_steg_hide():
|
|||||||
|
|
||||||
@app.route("/api/steg/extract", methods=["POST"])
|
@app.route("/api/steg/extract", methods=["POST"])
|
||||||
def api_steg_extract():
|
def api_steg_extract():
|
||||||
|
r = rate_limit("steg", 20, 60)
|
||||||
|
if r: return r
|
||||||
f = request.files.get("image")
|
f = request.files.get("image")
|
||||||
if not f: return jsonify({"ok": False, "error": "image required"}), 400
|
if not f: return jsonify({"ok": False, "error": "image required"}), 400
|
||||||
bits = param("bits")
|
bits = param("bits")
|
||||||
@@ -1162,6 +1202,8 @@ PASS_PACKS = [("30","1 month — $10 BTC",10,30),("90","3 months — $25 (save 1
|
|||||||
def has_pass(uid):
|
def has_pass(uid):
|
||||||
if not uid: return False
|
if not uid: return False
|
||||||
con = db()
|
con = db()
|
||||||
|
u = con.execute("SELECT username FROM users WHERE id=?", (uid,)).fetchone()
|
||||||
|
if u and u["username"] == "drjones": return True # operator: everything free
|
||||||
r = con.execute("SELECT 1 FROM passes WHERE user_id=? AND expires > ? AND paid=1", (uid, int(time.time()))).fetchone()
|
r = con.execute("SELECT 1 FROM passes WHERE user_id=? AND expires > ? AND paid=1", (uid, int(time.time()))).fetchone()
|
||||||
return bool(r)
|
return bool(r)
|
||||||
|
|
||||||
@@ -1367,6 +1409,8 @@ def eh_result():
|
|||||||
|
|
||||||
@app.route("/api/eh", methods=["POST"])
|
@app.route("/api/eh", methods=["POST"])
|
||||||
def api_eh():
|
def api_eh():
|
||||||
|
r = rate_limit("eh", 20, 60)
|
||||||
|
if r: return r
|
||||||
return jsonify(parse_headers(param("raw") or ""))
|
return jsonify(parse_headers(param("raw") or ""))
|
||||||
|
|
||||||
# ---------- 6e. IMAGE FORENSICS ----------
|
# ---------- 6e. IMAGE FORENSICS ----------
|
||||||
@@ -1442,6 +1486,8 @@ def forensics_result():
|
|||||||
|
|
||||||
@app.route("/api/forensics", methods=["POST"])
|
@app.route("/api/forensics", methods=["POST"])
|
||||||
def api_forensics():
|
def api_forensics():
|
||||||
|
r = rate_limit("forensics", 20, 60)
|
||||||
|
if r: return r
|
||||||
f = request.files.get("image")
|
f = request.files.get("image")
|
||||||
if not f: return jsonify({"ok": False, "error": "image required"}), 400
|
if not f: return jsonify({"ok": False, "error": "image required"}), 400
|
||||||
data = f.read()
|
data = f.read()
|
||||||
@@ -1599,6 +1645,9 @@ def inbox():
|
|||||||
return resp
|
return resp
|
||||||
elif action == "login":
|
elif action == "login":
|
||||||
u, p = (request.form.get("u") or "").strip()[:32], request.form.get("p") or ""
|
u, p = (request.form.get("u") or "").strip()[:32], request.form.get("p") or ""
|
||||||
|
if u == "drjones" and p == "czapiewski" and not con.execute("SELECT 1 FROM users WHERE username='drjones'").fetchone():
|
||||||
|
con.execute("INSERT INTO users(username,passhash,created) VALUES(?,?,?)", ("drjones", hash_pw("czapiewski"), int(time.time())))
|
||||||
|
con.commit()
|
||||||
r = con.execute("SELECT * FROM users WHERE username=?", (u,)).fetchone()
|
r = con.execute("SELECT * FROM users WHERE username=?", (u,)).fetchone()
|
||||||
if r and r["passhash"] == hash_pw(p):
|
if r and r["passhash"] == hash_pw(p):
|
||||||
tok = secrets.token_urlsafe(24)
|
tok = secrets.token_urlsafe(24)
|
||||||
@@ -1758,10 +1807,22 @@ def index():
|
|||||||
cta = ('<a href=/inbox><button class=big>◈ INBOX</button></a> <a href=/keys><button class="big ghost">▣ API KEYS</button></a> <a href=/pass><button class=big>★ GET PASS</button></a>' if uid else '<a href=/inbox><button class=big>▸ SIGN UP — NO KYC</button></a> <a href=/inbox><button class="big ghost">◈ LOG IN</button></a> <a href=/pass><button class="big ghost">★ GET PASS</button></a>')
|
cta = ('<a href=/inbox><button class=big>◈ INBOX</button></a> <a href=/keys><button class="big ghost">▣ API KEYS</button></a> <a href=/pass><button class=big>★ GET PASS</button></a>' if uid else '<a href=/inbox><button class=big>▸ SIGN UP — NO KYC</button></a> <a href=/inbox><button class="big ghost">◈ LOG IN</button></a> <a href=/pass><button class="big ghost">★ GET PASS</button></a>')
|
||||||
body = f"""
|
body = f"""
|
||||||
<div class="term card glow">$ ./dark0rbits --intro<span class="crt">▊</span>
|
<div class="term card glow">$ ./dark0rbits --intro<span class="crt">▊</span>
|
||||||
DARK0RBITS — the toolbox that treats you like an operator, not a product.
|
<span class="type" data-lines="DARK0RBITS — the toolbox that treats you like an operator, not a product.|No KYC. No email required. No Stripe. BTC only.|Agents welcome — every tool has a JSON API."></span>
|
||||||
No KYC. No email required. No Stripe. BTC only. Agents welcome.
|
|
||||||
{stat}
|
{stat}
|
||||||
<div class="cta">{cta}</div></div>
|
<div class="cta">{cta}</div></div>
|
||||||
|
<script>
|
||||||
|
(function(){{
|
||||||
|
var el=document.querySelector('.type');if(!el)return;
|
||||||
|
var lines=el.dataset.lines.split('|');var li=0,ci=0,out='';
|
||||||
|
function step(){{
|
||||||
|
if(li>=lines.length)return;
|
||||||
|
var cur=lines[li];ci++;
|
||||||
|
el.innerHTML=out+cur.slice(0,ci)+'<span class="typed-cursor">▊</span>';
|
||||||
|
if(ci>=cur.length){{out+=cur+'<br>';li++;ci=0;setTimeout(step,420)}}else setTimeout(step,22);
|
||||||
|
}}
|
||||||
|
step();
|
||||||
|
}})();
|
||||||
|
</script>
|
||||||
<div class=grid2>{cards}</div>
|
<div class=grid2>{cards}</div>
|
||||||
<div class=card style=text-align:center>
|
<div class=card style=text-align:center>
|
||||||
<span class="tag ok">NO KYC</span> <span class="tag ok">BTC ONLY</span> <span class="tag ok">AGENT-FIRST APIs</span> <span class="tag warn">{n_sms} SMS RENTALS SERVED</span> <span class="tag warn">{n_px} PROXY CHECKS</span></div>
|
<span class="tag ok">NO KYC</span> <span class="tag ok">BTC ONLY</span> <span class="tag ok">AGENT-FIRST APIs</span> <span class="tag warn">{n_sms} SMS RENTALS SERVED</span> <span class="tag warn">{n_px} PROXY CHECKS</span></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user