feat(system): 完成后端系统管理(审计/角色/操作日志/日志查询)
- 操作日志审计机制:audit 上下文(成功与失败都记、独立提交、操作人标量 固化、commit 后查询前置约定),用户/角色全部写操作接入;request_data 按键名的结构化脱敏 + UTF-8 字节边界截断 - 角色管理:CRUD 与启停;admin 禁删禁停、有绑定禁删(FOR UPDATE 锁行)、 编码创建后不可改、停用角色不可再分配 - 日志查询:操作/登录两端点,列表与详情两段式(详情含已脱敏请求参数); 时间参数统一转本地 naive(修混用时区 500 与偏移丢弃) - 用户管理:补齐 reset_password 操作人、编辑空串归一 NULL、并发唯一键兜底 400 - 测试 301 例全绿(单元 223 / 集成 78) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
e22c4bcdc9
commit
c83287819a
32 files changed
+3158
-137
No files matched your search
@@ -4,7 +4,7 @@ from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
|
||||
from app.api.deps import DbSession, get_current_user, get_raw_token
|
||||
from app.api.deps import DbSession, client_ip, get_current_user, get_raw_token
|
||||
from app.core.response import ApiResponse, ok
|
||||
from app.models import User
|
||||
from app.repositories.user_repository import get_role_codes
|
||||
@@ -14,16 +14,6 @@ from app.services import auth_service, captcha_service
|
||||
router = APIRouter(prefix="/auth", tags=["认证"])
|
||||
|
||||
|
||||
def _client_ip(request: Request) -> str | None:
|
||||
"""取客户端 IP。
|
||||
|
||||
当前取直连地址。生产环境经 Nginx 反代后直连地址会变成代理地址,
|
||||
届时需改为读取 `X-Forwarded-For` 的**第一个**值 —— 且必须确保该头
|
||||
由网关覆写,否则客户端可以伪造。此改动留到部署阶段一并处理。
|
||||
"""
|
||||
return request.client.host if request.client else None
|
||||
|
||||
|
||||
@router.get("/captcha", response_model=ApiResponse[CaptchaOut], summary="获取图形验证码")
|
||||
def get_captcha() -> ApiResponse[CaptchaOut]:
|
||||
"""签发图形验证码。无需认证。
|
||||
@@ -52,7 +42,7 @@ def login(payload: LoginIn, request: Request, session: DbSession) -> ApiResponse
|
||||
password=payload.password,
|
||||
captcha_id=payload.captcha_id,
|
||||
captcha_code=payload.captcha_code,
|
||||
ip=_client_ip(request),
|
||||
ip=client_ip(request),
|
||||
user_agent=request.headers.get("user-agent"),
|
||||
)
|
||||
return ok(result, message="登录成功")
|
||||
|
||||
Reference in new issue
Block a user