- audit_service.py: audit_log 写入 + is_audit_role + list/backup/delete - init.py: load_app_audit 建 sd_audit_logs 表 + owner.audit 角色 - audit.dspy: list/backup/delete 仅 owner.audit(审计独立性) - models + load_path.py + README
72 lines
2.7 KiB
Plaintext
72 lines
2.7 KiB
Plaintext
# app_audit audit.dspy - 审计日志 API
|
||
# 查看/备份/删除 仅 owner.audit 角色(审计独立性:superuser 也无权访问)
|
||
# action: list / backup / delete
|
||
|
||
user_id = await get_user()
|
||
if not user_id:
|
||
return json.dumps({'ok': False, 'error': '未登录'}, ensure_ascii=False)
|
||
|
||
action = (params_kw or {}).get('action', 'list')
|
||
|
||
from app_audit.audit_service import (
|
||
is_audit_role, list_audit_logs, backup_audit_logs, delete_audit_logs,
|
||
)
|
||
|
||
# 查 username 和 client_ip
|
||
username = user_id
|
||
try:
|
||
async with get_sor_context(request._run_ns, 'pipeline') as _sor:
|
||
_u = await _sor.sqlExe("SELECT username FROM users WHERE id=${u}$", {'u': user_id})
|
||
if _u:
|
||
username = getattr(_u[0], 'username', user_id) or user_id
|
||
except Exception:
|
||
pass
|
||
|
||
client_ip = ''
|
||
try:
|
||
client_ip = request.get('client_ip', '') or ''
|
||
except Exception:
|
||
pass
|
||
|
||
# 权限校验:所有 action 仅 owner.audit(superuser 也不行)
|
||
async with get_sor_context(request._run_ns, 'pipeline') as _sor:
|
||
if not await is_audit_role(_sor, user_id):
|
||
return json.dumps({'ok': False, 'error': '仅 owner.audit 角色可访问审计日志'}, ensure_ascii=False)
|
||
|
||
if action == 'list':
|
||
filters = {
|
||
'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', ''),
|
||
}
|
||
limit = (params_kw or {}).get('limit', 100)
|
||
offset = (params_kw or {}).get('offset', 0)
|
||
try:
|
||
async with get_sor_context(request._run_ns, 'pipeline') as sor:
|
||
rows = await list_audit_logs(sor, filters, limit, offset)
|
||
return json.dumps({'ok': True, 'logs': rows}, ensure_ascii=False)
|
||
except Exception as e:
|
||
return json.dumps({'ok': False, 'error': str(e)}, ensure_ascii=False)
|
||
|
||
elif action == 'backup':
|
||
try:
|
||
async with get_sor_context(request._run_ns, 'pipeline') as sor:
|
||
r = await backup_audit_logs(sor, user_id, username, None, client_ip)
|
||
return json.dumps(r, ensure_ascii=False)
|
||
except Exception as e:
|
||
return json.dumps({'ok': False, 'error': str(e)}, ensure_ascii=False)
|
||
|
||
elif action == 'delete':
|
||
before = (params_kw or {}).get('before', '')
|
||
try:
|
||
async with get_sor_context(request._run_ns, 'pipeline') as sor:
|
||
r = await delete_audit_logs(sor, user_id, username, before, client_ip)
|
||
return json.dumps(r, ensure_ascii=False)
|
||
except Exception as e:
|
||
return json.dumps({'ok': False, 'error': str(e)}, ensure_ascii=False)
|
||
|
||
else:
|
||
return json.dumps({'ok': False, 'error': '未知 action: ' + str(action)}, ensure_ascii=False)
|