Files
video/server/app/api/v1/logs.py
T
2026-10-25 11:46:44 +08:00

75 lines
2.4 KiB
Python

"""用户操作日志上报:仅特定账号(如 256kb@gmail.com)开启,用于排查 bug"""
import logging
from fastapi import APIRouter, Depends, Request
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.deps import Identity, require_user
from app.core import errors
from app.core.response import BizException, ok
from app.models.user import User
from app.models.user_action_log import UserActionLog
logger = logging.getLogger(__name__)
router = APIRouter()
# 仅这些邮箱的用户允许上报操作日志(白名单,避免给所有用户上传)
ALLOWED_EMAILS = {"256kb@gmail.com"}
# 单条 detail 最大长度(防御性截断)
MAX_DETAIL_LEN = 800
# 单次批量最大条数
MAX_BATCH = 100
class ActionLogItem(BaseModel):
action: str = Field(..., max_length=50)
page: str = Field("", max_length=100)
detail: str = Field("", max_length=MAX_DETAIL_LEN)
device_info: str = Field("", max_length=200)
app_version: str = Field("", max_length=20)
# 客户端本地时间戳(毫秒),用于在服务端时钟漂移时仍能排序
client_ts: int = 0
class ActionLogBatch(BaseModel):
items: list[ActionLogItem] = Field(..., max_length=MAX_BATCH)
@router.post("/logs/action")
async def report_action_log(
body: ActionLogBatch,
request: Request,
db: AsyncSession = Depends(get_db),
identity: Identity = Depends(require_user),
):
"""接收 App 上报的用户操作日志。仅白名单邮箱可上报,其他账号返回 403 不上报"""
user = (await db.execute(
select(User).where(User.id == identity.user_id)
)).scalar_one_or_none()
if not user or user.email not in ALLOWED_EMAILS:
# 静默拒绝:不报错,不写入,避免在普通用户端暴露此通道存在
raise BizException(errors.FORBIDDEN, "forbidden")
rows = [
UserActionLog(
user_id=user.id,
action=item.action[:50],
page=item.page[:100],
detail=item.detail[:MAX_DETAIL_LEN],
device_info=item.device_info[:200],
app_version=item.app_version[:20],
)
for item in body.items
]
db.add_all(rows)
await db.commit()
logger.info(
"user_action_log user=%s count=%s ip=%s",
user.id, len(rows), request.client.host if request.client else "-",
)
return ok({"received": len(rows)})