Fix: Handle 'user_agent' data truncation error
This commit is contained in:
@@ -35,7 +35,7 @@ class LoginSession(SQLModel, table=True):
|
||||
user_id: int = Field(sa_column=Column(BigInteger, ForeignKey("lazer_users.id"), nullable=False, index=True))
|
||||
session_token: str = Field(unique=True, index=True) # 会话令牌
|
||||
ip_address: str = Field() # 登录IP
|
||||
user_agent: str | None = Field(default=None)
|
||||
user_agent: str | None = Field(default=None, max_length=250)
|
||||
country_code: str | None = Field(default=None)
|
||||
is_verified: bool = Field(default=False) # 是否已验证
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
||||
|
||||
@@ -388,13 +388,18 @@ class LoginSessionService:
|
||||
is_new_location: bool = False
|
||||
) -> LoginSession:
|
||||
"""创建登录会话"""
|
||||
from app.utils import simplify_user_agent
|
||||
|
||||
session_token = EmailVerificationService.generate_session_token()
|
||||
|
||||
# 简化 User-Agent 字符串
|
||||
simplified_user_agent = simplify_user_agent(user_agent, max_length=250)
|
||||
|
||||
session = LoginSession(
|
||||
user_id=user_id,
|
||||
session_token=session_token,
|
||||
ip_address=ip_address,
|
||||
user_agent=user_agent,
|
||||
user_agent=simplified_user_agent,
|
||||
country_code=country_code,
|
||||
is_new_location=is_new_location,
|
||||
expires_at=datetime.now(UTC) + timedelta(hours=24), # 24小时过期
|
||||
|
||||
@@ -45,8 +45,10 @@ class LoginLogService:
|
||||
raw_ip = get_client_ip(request)
|
||||
ip_address = normalize_ip(raw_ip)
|
||||
|
||||
# 获取User-Agent
|
||||
user_agent = request.headers.get("User-Agent", "")
|
||||
# 获取并简化User-Agent
|
||||
from app.utils import simplify_user_agent
|
||||
raw_user_agent = request.headers.get("User-Agent", "")
|
||||
user_agent = simplify_user_agent(raw_user_agent, max_length=500)
|
||||
|
||||
# 创建基本的登录记录
|
||||
login_log = UserLoginLog(
|
||||
|
||||
77
app/utils.py
77
app/utils.py
@@ -146,3 +146,80 @@ def check_image(content: bytes, size: int, width: int, height: int) -> None:
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error processing image: {e}")
|
||||
|
||||
|
||||
def simplify_user_agent(user_agent: str | None, max_length: int = 200) -> str | None:
|
||||
"""
|
||||
简化 User-Agent 字符串,只保留 osu! 和关键设备系统信息浏览器
|
||||
|
||||
Args:
|
||||
user_agent: 原始 User-Agent 字符串
|
||||
max_length: 最大长度限制
|
||||
|
||||
Returns:
|
||||
简化后的 User-Agent 字符串,或 None
|
||||
"""
|
||||
import re
|
||||
|
||||
if not user_agent:
|
||||
return None
|
||||
|
||||
# 如果长度在限制内,直接返回
|
||||
if len(user_agent) <= max_length:
|
||||
return user_agent
|
||||
|
||||
# 提取操作系统信息
|
||||
os_info = ""
|
||||
os_patterns = [
|
||||
r'(Windows[^;)]*)',
|
||||
r'(Mac OS[^;)]*)',
|
||||
r'(Linux[^;)]*)',
|
||||
r'(Android[^;)]*)',
|
||||
r'(iOS[^;)]*)',
|
||||
r'(iPhone[^;)]*)',
|
||||
r'(iPad[^;)]*)'
|
||||
]
|
||||
|
||||
for pattern in os_patterns:
|
||||
match = re.search(pattern, user_agent, re.IGNORECASE)
|
||||
if match:
|
||||
os_info = match.group(1).strip()
|
||||
break
|
||||
|
||||
# 提取浏览器信息
|
||||
browser_info = ""
|
||||
browser_patterns = [
|
||||
r'(osu![^)]*)', # osu! 客户端
|
||||
r'(Chrome/[\d.]+)',
|
||||
r'(Firefox/[\d.]+)',
|
||||
r'(Safari/[\d.]+)',
|
||||
r'(Edge/[\d.]+)',
|
||||
r'(Opera/[\d.]+)'
|
||||
]
|
||||
|
||||
for pattern in browser_patterns:
|
||||
match = re.search(pattern, user_agent, re.IGNORECASE)
|
||||
if match:
|
||||
browser_info = match.group(1).strip()
|
||||
# 如果找到了 osu! 客户端,优先使用
|
||||
if 'osu!' in browser_info.lower():
|
||||
break
|
||||
|
||||
# 构建简化的 User-Agent
|
||||
parts = []
|
||||
if os_info:
|
||||
parts.append(os_info)
|
||||
if browser_info:
|
||||
parts.append(browser_info)
|
||||
|
||||
if parts:
|
||||
simplified = '; '.join(parts)
|
||||
else:
|
||||
# 如果没有识别到关键信息,截断原始字符串
|
||||
simplified = user_agent[:max_length-3] + "..."
|
||||
|
||||
# 确保不超过最大长度
|
||||
if len(simplified) > max_length:
|
||||
simplified = simplified[:max_length-3] + "..."
|
||||
|
||||
return simplified
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Increase the length limit of the user_agent field in the login_sessions table
|
||||
|
||||
Revision ID: 5b76689f6e4b
|
||||
Revises: 65e7dc8d5905
|
||||
Create Date: 2025-08-22 15:14:59.242274
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "5b76689f6e4b"
|
||||
down_revision: str | Sequence[str] | None = "65e7dc8d5905"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column("login_sessions", "user_agent",
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
type_=sa.String(length=250),
|
||||
existing_nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column("login_sessions", "user_agent",
|
||||
existing_type=sa.String(length=250),
|
||||
type_=mysql.VARCHAR(length=255),
|
||||
existing_nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
Reference in New Issue
Block a user