from fastapi import FastAPI, Request from fastapi.responses import RedirectResponse from user_agents import parse import geoip2.database from pathlib import Path import os import httpx app = FastAPI() # ================== ENV ================== GOOGLE_SHEET_WEBHOOK = os.getenv("GOOGLE_SHEET_WEBHOOK") if not GOOGLE_SHEET_WEBHOOK: raise RuntimeError("GOOGLE_SHEET_WEBHOOK environment variable not set") REDIRECT_URL = os.getenv("REDIRECT_URL") if not REDIRECT_URL: raise RuntimeError("REDIRECT_URL environment variable not set") # ================== GEO DB ================== BASE_DIR = Path(__file__).resolve().parent CITY_DB_PATH = BASE_DIR / "databases" / "GeoLite2-City.mmdb" ASN_DB_PATH = BASE_DIR / "databases" / "GeoLite2-ASN.mmdb" city_reader = geoip2.database.Reader(str(CITY_DB_PATH)) asn_reader = geoip2.database.Reader(str(ASN_DB_PATH)) # ================== TRACK ================== @app.get("/offer") async def track_ip(request: Request): # -------- Client IP -------- client_ip = request.headers.get("X-Forwarded-For") if client_ip: client_ip = client_ip.split(",")[0].strip() else: client_ip = request.client.host # -------- User Agent -------- ua_string = request.headers.get("User-Agent", "Unknown") ua = parse(ua_string) browser_info = { "browser": ua.browser.family, "version": ua.browser.version_string, "os": ua.os.family, "os_version": ua.os.version_string, "device": ua.device.family, "is_mobile": ua.is_mobile, "is_bot": ua.is_bot } # -------- Geo -------- geo_data = {} try: city = city_reader.city(client_ip) geo_data = { "country": city.country.name, "city": city.city.name, "region": city.subdivisions.most_specific.name, "latitude": city.location.latitude, "longitude": city.location.longitude } except Exception as e: print("Geo error:", e) # -------- ASN -------- asn_data = {} try: asn = asn_reader.asn(client_ip) asn_data = { "asn": asn.autonomous_system_number, "org": asn.autonomous_system_organization } except Exception as e: print("ASN error:", e) payload = { "ip": client_ip, "browser": browser_info, "geolocation": geo_data, "asn": asn_data } # -------- Send to Google Sheet -------- try: async with httpx.AsyncClient(timeout=5, follow_redirects=True) as client: await client.post( GOOGLE_SHEET_WEBHOOK, json=payload, headers={"Content-Type": "application/json"} ) except Exception as e: print("Sheet error:", e) '''return payload''' # -------- Redirect -------- return RedirectResponse( url=REDIRECT_URL, status_code=302 ) @app.on_event("shutdown") def shutdown(): city_reader.close() asn_reader.close()