fix: 审计覆盖 + 备份过滤 + unknown action 保留

- unknown action 在 detail 保留原始值
- backup 支持过滤 + limit 上限(防大响应)
- 提取 _build_filter 公共过滤函数
This commit is contained in:
yumoqing 2026-08-14 13:37:49 +08:00
parent 55e4ce30b8
commit d38e26ffff
2 changed files with 28 additions and 7 deletions

View File

@ -41,6 +41,8 @@ async def audit_log(sor, user_id: str, username: str, action: str,
审计写入失败不应阻断主流程返回 False 由调用方决定是否处理
"""
if action not in VALID_ACTIONS:
# 保留原始 action避免排查时丢失是哪个模块拼错了
detail = (detail or "") + " [原始action:" + str(action) + "]"
action = "unknown"
try:
from appPublic.uniqueID import getID
@ -87,8 +89,8 @@ def _row_to_dict(row) -> dict:
return d
async def list_audit_logs(sor, filters: dict = None, page: int = 1, rows: int = 60):
"""查询审计日志分页DataGrid 兼容),返回 {rows, total}"""
def _build_filter(filters: dict):
"""构造 WHERE 子句 + 参数list/backup/delete 共用)。返回 (wsql, ns)"""
filters = filters or {}
where = []
ns = {}
@ -108,6 +110,12 @@ async def list_audit_logs(sor, filters: dict = None, page: int = 1, rows: int =
where.append("created_at<=${t}$")
ns["t"] = filters["to"]
wsql = (" WHERE " + " AND ".join(where)) if where else ""
return wsql, ns
async def list_audit_logs(sor, filters: dict = None, page: int = 1, rows: int = 60):
"""查询审计日志分页DataGrid 兼容),返回 {rows, total}。"""
wsql, ns = _build_filter(filters)
try:
page = max(1, int(page or 1))
except (ValueError, TypeError):
@ -127,11 +135,16 @@ async def list_audit_logs(sor, filters: dict = None, page: int = 1, rows: int =
async def backup_audit_logs(sor, user_id: str, username: str, filters: dict = None,
client_ip: str = "") -> dict:
"""导出审计日志(仅 owner.audit返回 JSON 内容供下载。"""
# 查最近 10000 条(备份上限,避免一次拉全表拖垮内存)
limit: int = 10000, client_ip: str = "") -> dict:
"""导出审计日志(仅 owner.audit支持过滤 + 上限,返回 JSON 内容供下载。"""
wsql, ns = _build_filter(filters)
try:
limit = max(1, min(int(limit or 10000), 50000))
except (ValueError, TypeError):
limit = 10000
recs = await sor.sqlExe(
"SELECT * FROM sd_audit_logs ORDER BY created_at DESC LIMIT 10000", {})
"SELECT * FROM sd_audit_logs" + wsql + " ORDER BY created_at DESC LIMIT "
+ str(limit), ns)
rows = [_row_to_dict(r) for r in recs]
# 备份操作本身也审计
await audit_log(sor, user_id, username, "audit_backup",

View File

@ -51,9 +51,17 @@ if action == 'list':
return json.dumps({'ok': False, 'error': str(e)}, ensure_ascii=False)
elif action == 'backup':
bfilters = {
'user_id': (params_kw or {}).get('user_id', ''),
'username': (params_kw or {}).get('username', ''),
'action': (params_kw or {}).get('act', ''),
'from': (params_kw or {}).get('from', ''),
'to': (params_kw or {}).get('to', ''),
}
blimit = (params_kw or {}).get('limit', 10000)
try:
async with get_sor_context(request._run_ns, 'pipeline') as sor:
r = await backup_audit_logs(sor, user_id, username, None, client_ip)
r = await backup_audit_logs(sor, user_id, username, bfilters, blimit, client_ip)
return json.dumps(r, ensure_ascii=False)
except Exception as e:
return json.dumps({'ok': False, 'error': str(e)}, ensure_ascii=False)