add ip log

This commit is contained in:
咕谷酱
2025-08-18 00:23:57 +08:00
parent de0c86f4a2
commit 6e496a1123
9 changed files with 450 additions and 19 deletions

View File

@@ -2,6 +2,7 @@
"""
GeoIP dependency for FastAPI
"""
import ipaddress
from functools import lru_cache
from app.helpers.geoip_helper import GeoIPHelper
from app.config import settings
@@ -23,29 +24,76 @@ def get_geoip_helper() -> GeoIPHelper:
def get_client_ip(request) -> str:
"""
Get the real client IP address
Supports proxies, load balancers, and Cloudflare headers
获取客户端真实 IP 地址
支持 IPv4 和 IPv6考虑代理、负载均衡器等情况
"""
headers = request.headers
# 1. Cloudflare specific headers
# 1. Cloudflare 专用头部
cf_ip = headers.get("CF-Connecting-IP")
if cf_ip:
return cf_ip.strip()
ip = cf_ip.strip()
if is_valid_ip(ip):
return ip
true_client_ip = headers.get("True-Client-IP")
if true_client_ip:
return true_client_ip.strip()
ip = true_client_ip.strip()
if is_valid_ip(ip):
return ip
# 2. Standard proxy headers
# 2. 标准代理头部
forwarded_for = headers.get("X-Forwarded-For")
if forwarded_for:
# X-Forwarded-For may contain multiple IPs, take the first
return forwarded_for.split(",")[0].strip()
# X-Forwarded-For 可能包含多个 IP取第一个有效的
for ip_str in forwarded_for.split(","):
ip = ip_str.strip()
if is_valid_ip(ip) and not is_private_ip(ip):
return ip
real_ip = headers.get("X-Real-IP")
if real_ip:
return real_ip.strip()
ip = real_ip.strip()
if is_valid_ip(ip):
return ip
# 3. Fallback to client host
return request.client.host if request.client else "127.0.0.1"
# 3. 回退到客户端 IP
client_ip = request.client.host if request.client else "127.0.0.1"
return client_ip if is_valid_ip(client_ip) else "127.0.0.1"
def is_valid_ip(ip_str: str) -> bool:
"""
验证 IP 地址是否有效(支持 IPv4 和 IPv6
"""
try:
ipaddress.ip_address(ip_str)
return True
except ValueError:
return False
def is_private_ip(ip_str: str) -> bool:
"""
判断是否为私有 IP 地址
"""
try:
ip = ipaddress.ip_address(ip_str)
return ip.is_private
except ValueError:
return False
def normalize_ip(ip_str: str) -> str:
"""
标准化 IP 地址格式
对于 IPv6转换为压缩格式
"""
try:
ip = ipaddress.ip_address(ip_str)
if isinstance(ip, ipaddress.IPv6Address):
return ip.compressed
else:
return str(ip)
except ValueError:
return ip_str

View File

@@ -89,6 +89,9 @@ async def get_client_user(
user = (await db.exec(select(User).where(User.id == token_record.user_id))).first()
if not user:
raise HTTPException(status_code=401, detail="Invalid or expired token")
await db.refresh(user)
return user
@@ -125,4 +128,7 @@ async def get_current_user(
user = (await db.exec(select(User).where(User.id == token_record.user_id))).first()
if not user:
raise HTTPException(status_code=401, detail="Invalid or expired token")
await db.refresh(user)
return user