tracked-file opens now report opener's real IP + geo/ISP/ASN/tz/VPN flag + lang + referrer; nginx passes CF-Connecting-IP (was logging tunnel IP)
This commit is contained in:
52
app.py
52
app.py
@@ -781,18 +781,48 @@ def api_track_upload():
|
|||||||
"email_html": f"{SITE}/t/{token}/html",
|
"email_html": f"{SITE}/t/{token}/html",
|
||||||
"note": "attach/email the HTML version — every view fires the pixel and lands in the inbox"})
|
"note": "attach/email the HTML version — every view fires the pixel and lands in the inbox"})
|
||||||
|
|
||||||
|
def _geo_cache():
|
||||||
|
con = db()
|
||||||
|
con.execute("CREATE TABLE IF NOT EXISTS geo_cache(ip TEXT PRIMARY KEY, geo TEXT, ts INTEGER)")
|
||||||
|
return con
|
||||||
|
|
||||||
|
def enrich_ip(ip):
|
||||||
|
"""geo/ISP/ASN for an IP, cached 24h."""
|
||||||
|
if not ip or ip == "created" or ip.startswith(("10.30.20.", "127.", "172.17.")): return {}
|
||||||
|
con = _geo_cache()
|
||||||
|
r = con.execute("SELECT geo FROM geo_cache WHERE ip=? AND ts > ?", (ip, int(time.time())-86400)).fetchone()
|
||||||
|
if r: return json.loads(r["geo"])
|
||||||
|
st, b = http(f"http://ip-api.com/json/{ip}?fields=66846719")
|
||||||
|
d = jf(b) or {}
|
||||||
|
geo = {k: d.get(k) for k in ("country","countryCode","regionName","city","zip","lat","lon","timezone","isp","org","as","asname","mobile","proxy","hosting","reverse","query") if d.get(k) is not None}
|
||||||
|
con.execute("INSERT OR REPLACE INTO geo_cache(ip,geo,ts) VALUES(?,?,?)", (ip, json.dumps(geo), int(time.time())))
|
||||||
|
con.commit()
|
||||||
|
return geo
|
||||||
|
|
||||||
|
def _log_open(t, extra=""):
|
||||||
|
con = db()
|
||||||
|
ip = request.headers.get("X-Real-IP") or request.remote_addr or "?"
|
||||||
|
ua = request.headers.get("User-Agent","")
|
||||||
|
lang = request.headers.get("Accept-Language","")
|
||||||
|
ref = request.headers.get("Referer","")
|
||||||
|
geo = enrich_ip(ip)
|
||||||
|
where = ""
|
||||||
|
if geo: where = f" — {geo.get('city','')}, {geo.get('regionName','')} {geo.get('countryCode','')} · {geo.get('isp','')} · tz {geo.get('timezone','')}"
|
||||||
|
if geo.get("proxy"): where += " · VPN/proxy ⚠"
|
||||||
|
con.execute("INSERT INTO track_events(trackable_id,ts,ip,ua) VALUES(?,?,?,?)",
|
||||||
|
(t["id"], int(time.time()), ip + (" " + json.dumps(geo) if geo else ""), ua[:200] + (f" | lang={lang}" if lang else "") + (f" | ref={ref[:100]}" if ref else "")))
|
||||||
|
uid = t["user_id"]
|
||||||
|
if uid:
|
||||||
|
con.execute("INSERT INTO messages(user_id,sender,body,created) VALUES(?,?,?,?)",
|
||||||
|
(uid, "operator-bot", f"👁 '{esc(t['filename'])}' just opened{extra} — IP <b>{esc(ip)}</b>{esc(where)}<br>device: {esc(ua[:100])}{'<br>lang: ' + esc(lang) if lang else ''}{'<br>from: ' + esc(ref[:120]) if ref else ''}", int(time.time())))
|
||||||
|
con.commit()
|
||||||
|
|
||||||
@app.route("/t/<token>")
|
@app.route("/t/<token>")
|
||||||
def tracked_download(token):
|
def tracked_download(token):
|
||||||
con = db()
|
con = db()
|
||||||
t = con.execute("SELECT * FROM trackables WHERE token=?", (token,)).fetchone()
|
t = con.execute("SELECT * FROM trackables WHERE token=?", (token,)).fetchone()
|
||||||
if not t or not t["paid"]: return "not found", 404
|
if not t or not t["paid"]: return "not found", 404
|
||||||
con.execute("INSERT INTO track_events(trackable_id,ts,ip,ua) VALUES(?,?,?,?)",
|
_log_open(t, " (link)")
|
||||||
(t["id"], int(time.time()), request.headers.get("X-Real-IP") or request.remote_addr, request.headers.get("User-Agent","")))
|
|
||||||
uid = t["user_id"]
|
|
||||||
if uid:
|
|
||||||
con.execute("INSERT INTO messages(user_id,sender,body,created) VALUES(?,?,?,?)",
|
|
||||||
(uid, "operator-bot", f"👁 your tracked file '{esc(t['filename'])}' was just opened — IP {esc(request.headers.get('X-Real-IP') or request.remote_addr)}, device: {esc(request.headers.get('User-Agent','')[:80])}", int(time.time())))
|
|
||||||
con.commit()
|
|
||||||
path = os.path.join(UPLOAD_DIR, token + ".bin")
|
path = os.path.join(UPLOAD_DIR, token + ".bin")
|
||||||
if not os.path.exists(path): return "file gone", 404
|
if not os.path.exists(path): return "file gone", 404
|
||||||
return send_file(path, as_attachment=True, download_name=t["filename"])
|
return send_file(path, as_attachment=True, download_name=t["filename"])
|
||||||
@@ -802,13 +832,7 @@ def tracked_pixel(token):
|
|||||||
con = db()
|
con = db()
|
||||||
t = con.execute("SELECT * FROM trackables WHERE token=?", (token,)).fetchone()
|
t = con.execute("SELECT * FROM trackables WHERE token=?", (token,)).fetchone()
|
||||||
if t and t["paid"]:
|
if t and t["paid"]:
|
||||||
con.execute("INSERT INTO track_events(trackable_id,ts,ip,ua) VALUES(?,?,?,?)",
|
_log_open(t, " (email/pixel)")
|
||||||
(t["id"], int(time.time()), request.headers.get("X-Real-IP") or request.remote_addr, request.headers.get("User-Agent","")))
|
|
||||||
uid = t["user_id"]
|
|
||||||
if uid:
|
|
||||||
con.execute("INSERT INTO messages(user_id,sender,body,created) VALUES(?,?,?,?)",
|
|
||||||
(uid, "operator-bot", f"👁 '{esc(t['filename'])}' was just viewed (email/pixel) — IP {esc(request.headers.get('X-Real-IP') or request.remote_addr)}", int(time.time())))
|
|
||||||
con.commit()
|
|
||||||
px = base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
|
px = base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
|
||||||
return Response(px, mimetype="image/gif", headers={"Cache-Control": "no-store"})
|
return Response(px, mimetype="image/gif", headers={"Cache-Control": "no-store"})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user