fix(automations): 审计行不再显式插 TEXT id,PG identity 下不再丢审计

旧 _audit_row 显式插 uuid 当 audit_log.id:SQLite 下能跑,
生产 PG 的 id 是 BIGINT GENERATED BY DEFAULT AS IDENTITY,
插 TEXT 直接报错、审计行全丢(best-effort 吞掉后无声无息,
#156 留下的已知局限)。修法:INSERT 一律不带 id,让两端
自增/identity 生成;表不存在时先按 PG 口径建、再按 SQLite
口径建、逐个重试——automations 按设计不 import 其它 cloud
模块,所以用先插、失败再按口径重试做方言探测。
单测:test_automation_run_due 4 项(含新增 PG-like 假连接
1 项:只接受不带 id 的 INSERT,旧实现在此应失败)。
This commit is contained in:
host committed 2026-10-30 01:55:59 +08:00
1 parent 0c458cb2ce
commit 8afcd3253e
2 files changed
+74 -12

No files matched your search

+36 -12
View File
@@ -566,26 +566,50 @@ def _automation_quota_exceeded(conn, owner):
def _audit_row(conn, project_id, action, detail, now):
"""best-effort 写审计行:失败不影响自动化运行本身。"""
"""best-effort 写审计行:失败不影响自动化运行本身。
旧实现显式插 TEXT uuid 当 id:SQLite 下能跑,但生产 PG 的
audit_log.id 是 BIGINT GENERATED BY DEFAULT AS IDENTITY,
插 TEXT 直接报错、审计行全丢(best-effort 吞掉后无声无息)。
修法:INSERT 一律不带 id,让两端自增/identity 生成;
表不存在时先按 PG 口径建、再按 SQLite 口径建、逐个重试,
哪边建表成功就用哪边——automations 按设计不 import 其它
cloud 模块,所以用“先插、失败再按口径重试”做方言探测,
不靠 import 判断后端。
"""
params = ("system", project_id, action, detail, now)
try:
conn.execute(
"CREATE TABLE IF NOT EXISTS audit_log("
"id TEXT PRIMARY KEY, "
"user_id TEXT, "
"project_id TEXT, "
"action TEXT, "
"detail TEXT, "
"created_at DOUBLE PRECISION)")
conn.execute(
"INSERT INTO audit_log(id, user_id, project_id, action,"
" detail, created_at) VALUES(?, 'system', ?, ?, ?, ?)",
(uuid.uuid4().hex, project_id, action, detail, now))
"INSERT INTO audit_log(user_id, project_id, action,"
" detail, created_at) VALUES(?, ?, ?, ?, ?)", params)
conn.commit()
return
except Exception:
try:
conn.rollback()
except Exception:
pass
for ddl in (
"CREATE TABLE IF NOT EXISTS audit_log("
"id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, "
"user_id TEXT, project_id TEXT, action TEXT, detail TEXT, "
"created_at DOUBLE PRECISION)",
"CREATE TABLE IF NOT EXISTS audit_log("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"user_id TEXT, project_id TEXT, action TEXT, detail TEXT, "
"created_at REAL)"):
try:
conn.execute(ddl)
conn.execute(
"INSERT INTO audit_log(user_id, project_id, action,"
" detail, created_at) VALUES(?, ?, ?, ?, ?)", params)
conn.commit()
return
except Exception:
try:
conn.rollback()
except Exception:
pass
def run_due_automation(conn, automation_id, now=None):