106 lines
3.3 KiB
Python
106 lines
3.3 KiB
Python
import hashlib
|
|
import uuid
|
|
from fastapi import Request
|
|
import re
|
|
import requests
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_client_ip(request: Request) -> str:
|
|
client_host = request.client.host if request.client else "0.0.0.0"
|
|
|
|
is_trusted = client_host.startswith("10.") or client_host.startswith("172.") or client_host.startswith("192.168.") or client_host == "127.0.0.1"
|
|
|
|
if is_trusted:
|
|
x_forwarded_for = request.headers.get("X-Forwarded-For")
|
|
if x_forwarded_for:
|
|
return x_forwarded_for.split(",")[0].strip()
|
|
|
|
return client_host
|
|
|
|
def parse_user_agent(user_agent: str):
|
|
try:
|
|
from user_agents import parse
|
|
user_agent_parsed = parse(user_agent)
|
|
|
|
browser = user_agent_parsed.browser.family
|
|
os = user_agent_parsed.os.family
|
|
|
|
device_type = "Desktop"
|
|
if user_agent_parsed.is_mobile:
|
|
device_type = "Mobile"
|
|
elif user_agent_parsed.is_tablet:
|
|
device_type = "Tablet"
|
|
|
|
return browser, os, device_type
|
|
except ImportError:
|
|
browser = "Unknown"
|
|
os = "Unknown"
|
|
device_type = "Desktop"
|
|
|
|
ua_lower = user_agent.lower()
|
|
|
|
if "mobile" in ua_lower or "android" in ua_lower or "iphone" in ua_lower:
|
|
device_type = "Mobile"
|
|
if "tablet" in ua_lower or "ipad" in ua_lower:
|
|
device_type = "Tablet"
|
|
|
|
if "windows" in ua_lower:
|
|
os = "Windows"
|
|
elif "mac os" in ua_lower or "macos" in ua_lower:
|
|
os = "macOS"
|
|
elif "android" in ua_lower:
|
|
os = "Android"
|
|
elif "iphone" in ua_lower or "ipad" in ua_lower:
|
|
os = "iOS"
|
|
elif "linux" in ua_lower:
|
|
os = "Linux"
|
|
|
|
if "edg" in ua_lower:
|
|
browser = "Edge"
|
|
elif "opr" in ua_lower or "opera" in ua_lower:
|
|
browser = "Opera"
|
|
elif "chrome" in ua_lower:
|
|
browser = "Chrome"
|
|
elif "safari" in ua_lower:
|
|
browser = "Safari"
|
|
elif "firefox" in ua_lower:
|
|
browser = "Firefox"
|
|
|
|
return browser, os, device_type
|
|
|
|
def generate_device_fingerprint(request: Request) -> str:
|
|
device_id = request.cookies.get("docqube_device_id")
|
|
if device_id:
|
|
return device_id
|
|
|
|
new_device_id = str(uuid.uuid4())
|
|
request.state.new_device_id = new_device_id
|
|
return new_device_id
|
|
|
|
def get_location_from_ip(ip: str):
|
|
"""
|
|
Fetch approximate location from IP using free ip-api.com.
|
|
Returns (country, state, city, lat, lon)
|
|
"""
|
|
if not ip or ip in ("127.0.0.1", "0.0.0.0", "localhost"):
|
|
return "Unknown", "Unknown", "Unknown", None, None
|
|
|
|
try:
|
|
response = requests.get(f"http://ip-api.com/json/{ip}", timeout=3)
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if data.get("status") == "success":
|
|
return (
|
|
data.get("country", "Unknown"),
|
|
data.get("regionName", "Unknown"),
|
|
data.get("city", "Unknown"),
|
|
data.get("lat"),
|
|
data.get("lon")
|
|
)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to fetch location for IP")
|
|
|
|
return "Unknown", "Unknown", "Unknown", None, None
|