From 48cc7fca82a8c94b43b7cd1b4f15a4253d408226 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Mon, 17 Aug 2026 13:55:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=A1=E8=AE=A1=E5=BA=93=E5=90=8D?= =?UTF-8?q?=E6=94=B9=E7=94=A8=20ServerEnv().get=5Fmodule=5Fdbname=20?= =?UTF-8?q?=E9=80=82=E9=85=8D=E5=A4=9A=E5=AE=BF=E4=B8=BB=20+=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20customer=5Frecharge=20=E5=AE=A1=E8=AE=A1=E5=8A=A8?= =?UTF-8?q?=E4=BD=9C=20+=20build.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_audit/audit_service.py | 2 ++ app_audit/init.py | 4 +++- build.sh | 30 ++++++++++++++++++++++++++++++ wwwroot/api/audit.dspy | 12 +++++++----- 4 files changed, 42 insertions(+), 6 deletions(-) create mode 100755 build.sh diff --git a/app_audit/audit_service.py b/app_audit/audit_service.py index e711f1e..9ee182b 100644 --- a/app_audit/audit_service.py +++ b/app_audit/audit_service.py @@ -15,6 +15,7 @@ app_audit/audit_service.py - 审计日志全局模块 工作环境:work_env_set / org_key_gen / remote_bwrap 部署账号:account_create / account_remove / sandbox_run 用户机构:user_create / user_disable / user_delete / org_change + 财务:customer_recharge 审计自身:audit_delete / audit_backup """ @@ -29,6 +30,7 @@ VALID_ACTIONS = { "work_env_set", "org_key_gen", "remote_bwrap", "account_create", "account_remove", "sandbox_run", "user_create", "user_disable", "user_delete", "org_change", + "customer_recharge", "audit_delete", "audit_backup", } diff --git a/app_audit/init.py b/app_audit/init.py index eac85d7..5a87cbc 100644 --- a/app_audit/init.py +++ b/app_audit/init.py @@ -16,9 +16,11 @@ def load_app_audit(): async def _init_audit(app): from sqlor.dbpools import DBPools from appPublic.log import debug + from ahserver.serverenv import ServerEnv try: db = DBPools() - async with db.sqlorContext("pipeline") as sor: + dbname = ServerEnv().get_module_dbname('app_audit') + async with db.sqlorContext(dbname) as sor: # 审计日志表(append-only,全局) await sor.sqlExe( "CREATE TABLE IF NOT EXISTS sd_audit_logs (" diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d17c59a --- /dev/null +++ b/build.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# app_audit module build script +# 链接 wwwroot 到宿主应用(Sage 或 pipeline)。审计表由 load_app_audit() 启动时自动建。 + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# 查找宿主应用根目录(含 wwwroot 目录) +APP_ROOT="" +for candidate in "$SCRIPT_DIR/../.." "$HOME/repos/sage" "$HOME/sage" "$HOME/work/repos/sage"; do + if [ -d "$candidate/wwwroot" ]; then + APP_ROOT="$(cd "$candidate" && pwd)" + break + fi +done + +if [ -z "$APP_ROOT" ]; then + echo "ERROR: host app root not found" + exit 1 +fi + +echo "Host app root: $APP_ROOT" + +# 链接 wwwroot 到宿主应用 +rm -f "$APP_ROOT/wwwroot/app_audit" +ln -sf "$SCRIPT_DIR/wwwroot" "$APP_ROOT/wwwroot/app_audit" + +echo "app_audit module build complete (wwwroot linked)." +echo "提示:Python 包需 pip install . ;RBAC 权限用 scripts/load_path.py 注册。" diff --git a/wwwroot/api/audit.dspy b/wwwroot/api/audit.dspy index 458ae2f..5df19fb 100644 --- a/wwwroot/api/audit.dspy +++ b/wwwroot/api/audit.dspy @@ -2,6 +2,8 @@ # 查看/备份/删除 仅 owner.audit 角色(审计独立性:superuser 也无权访问) # action: list / backup / delete +_dbname = get_module_dbname('app_audit') + user_id = await get_user() if not user_id: return json.dumps({'ok': False, 'error': '未登录'}, ensure_ascii=False) @@ -15,7 +17,7 @@ from app_audit.audit_service import ( # 查 username 和 client_ip username = user_id try: - async with get_sor_context(request._run_ns, 'pipeline') as _sor: + async with get_sor_context(request._run_ns, _dbname) 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 @@ -29,7 +31,7 @@ except Exception: pass # 权限校验:所有 action 仅 owner.audit(superuser 也不行) -async with get_sor_context(request._run_ns, 'pipeline') as _sor: +async with get_sor_context(request._run_ns, _dbname) as _sor: if not await is_audit_role(_sor, user_id): return json.dumps({'ok': False, 'error': '仅 owner.audit 角色可访问审计日志'}, ensure_ascii=False) @@ -44,7 +46,7 @@ if action == 'list': page = (params_kw or {}).get('page', 1) rows = (params_kw or {}).get('rows', 60) try: - async with get_sor_context(request._run_ns, 'pipeline') as sor: + async with get_sor_context(request._run_ns, _dbname) as sor: r = await list_audit_logs(sor, filters, page, rows) return json.dumps(r, ensure_ascii=False) except Exception as e: @@ -60,7 +62,7 @@ elif action == 'backup': } blimit = (params_kw or {}).get('limit', 10000) try: - async with get_sor_context(request._run_ns, 'pipeline') as sor: + async with get_sor_context(request._run_ns, _dbname) as sor: r = await backup_audit_logs(sor, user_id, username, bfilters, blimit, client_ip) return json.dumps(r, ensure_ascii=False) except Exception as e: @@ -69,7 +71,7 @@ elif action == 'backup': elif action == 'delete': before = (params_kw or {}).get('before', '') try: - async with get_sor_context(request._run_ns, 'pipeline') as sor: + async with get_sor_context(request._run_ns, _dbname) 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: