- 删除 entries_history / audit_log / secrets_history 的 actor 列及写入逻辑 - MCP secrets_history 透传当前 user_id - Entry 增加 user_id,search 查询不再用伪 UUID - 迁移:保留 users.api_key,从 api_keys 表回退时生成新明文 key 并删表 - 文档:audit_log auth 语义、API Key 存储说明 Made-with: Cursor
76 lines
1.8 KiB
Rust
76 lines
1.8 KiB
Rust
use anyhow::Result;
|
|
use serde_json::Value;
|
|
use sqlx::PgPool;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Debug, serde::Serialize)]
|
|
pub struct HistoryEntry {
|
|
pub version: i64,
|
|
pub action: String,
|
|
pub created_at: String,
|
|
}
|
|
|
|
pub async fn run(
|
|
pool: &PgPool,
|
|
namespace: &str,
|
|
kind: &str,
|
|
name: &str,
|
|
limit: u32,
|
|
user_id: Option<Uuid>,
|
|
) -> Result<Vec<HistoryEntry>> {
|
|
#[derive(sqlx::FromRow)]
|
|
struct Row {
|
|
version: i64,
|
|
action: String,
|
|
created_at: chrono::DateTime<chrono::Utc>,
|
|
}
|
|
|
|
let rows: Vec<Row> = if let Some(uid) = user_id {
|
|
sqlx::query_as(
|
|
"SELECT version, action, created_at FROM entries_history \
|
|
WHERE namespace = $1 AND kind = $2 AND name = $3 AND user_id = $4 \
|
|
ORDER BY id DESC LIMIT $5",
|
|
)
|
|
.bind(namespace)
|
|
.bind(kind)
|
|
.bind(name)
|
|
.bind(uid)
|
|
.bind(limit as i64)
|
|
.fetch_all(pool)
|
|
.await?
|
|
} else {
|
|
sqlx::query_as(
|
|
"SELECT version, action, created_at FROM entries_history \
|
|
WHERE namespace = $1 AND kind = $2 AND name = $3 AND user_id IS NULL \
|
|
ORDER BY id DESC LIMIT $4",
|
|
)
|
|
.bind(namespace)
|
|
.bind(kind)
|
|
.bind(name)
|
|
.bind(limit as i64)
|
|
.fetch_all(pool)
|
|
.await?
|
|
};
|
|
|
|
Ok(rows
|
|
.into_iter()
|
|
.map(|r| HistoryEntry {
|
|
version: r.version,
|
|
action: r.action,
|
|
created_at: r.created_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
|
|
})
|
|
.collect())
|
|
}
|
|
|
|
pub async fn run_json(
|
|
pool: &PgPool,
|
|
namespace: &str,
|
|
kind: &str,
|
|
name: &str,
|
|
limit: u32,
|
|
user_id: Option<Uuid>,
|
|
) -> Result<Value> {
|
|
let entries = run(pool, namespace, kind, name, limit, user_id).await?;
|
|
Ok(serde_json::to_value(entries)?)
|
|
}
|