Files
nx9-url-shortener/src/db/audit_events.rs
T

75 lines
2.8 KiB
Rust

use crate::models::AuditEvent;
use chrono::Utc;
use rusqlite::{params, Connection};
use uuid::Uuid;
/// Write an audit event to the system.db audit_events table.
pub fn write_audit_event(
conn: &Connection,
actor: &str,
action: &str,
object_type: &str,
object_id: &str,
metadata: Option<&str>,
) -> rusqlite::Result<()> {
let id = Uuid::new_v4().to_string();
let now = Utc::now().to_rfc3339();
conn.execute(
"INSERT INTO audit_events (id, actor, action, object_type, object_id, timestamp, metadata)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);",
params![id, actor, action, object_type, object_id, now, metadata],
)?;
Ok(())
}
/// List audit events with optional filtering by actor or action.
pub fn list_audit_events(
conn: &Connection,
limit: i64,
offset: i64,
actor_filter: Option<&str>,
action_filter: Option<&str>,
) -> rusqlite::Result<Vec<AuditEvent>> {
let mut events = Vec::new();
let (sql, params_vec): (String, Vec<Box<dyn rusqlite::types::ToSql>>) = match (actor_filter, action_filter) {
(Some(actor), Some(action)) => (
"SELECT id, actor, action, object_type, object_id, timestamp, metadata FROM audit_events WHERE actor = ?1 AND action = ?2 ORDER BY timestamp DESC LIMIT ?3 OFFSET ?4;".to_string(),
vec![Box::new(actor.to_string()), Box::new(action.to_string()), Box::new(limit), Box::new(offset)],
),
(Some(actor), None) => (
"SELECT id, actor, action, object_type, object_id, timestamp, metadata FROM audit_events WHERE actor = ?1 ORDER BY timestamp DESC LIMIT ?2 OFFSET ?3;".to_string(),
vec![Box::new(actor.to_string()), Box::new(limit), Box::new(offset)],
),
(None, Some(action)) => (
"SELECT id, actor, action, object_type, object_id, timestamp, metadata FROM audit_events WHERE action = ?1 ORDER BY timestamp DESC LIMIT ?2 OFFSET ?3;".to_string(),
vec![Box::new(action.to_string()), Box::new(limit), Box::new(offset)],
),
(None, None) => (
"SELECT id, actor, action, object_type, object_id, timestamp, metadata FROM audit_events ORDER BY timestamp DESC LIMIT ?1 OFFSET ?2;".to_string(),
vec![Box::new(limit), Box::new(offset)],
),
};
let params_refs: Vec<&dyn rusqlite::types::ToSql> =
params_vec.iter().map(|p| p.as_ref()).collect();
let mut stmt = conn.prepare(&sql)?;
let rows = stmt.query_map(params_refs.as_slice(), |row| {
Ok(AuditEvent {
id: row.get(0)?,
actor: row.get(1)?,
action: row.get(2)?,
object_type: row.get(3)?,
object_id: row.get(4)?,
timestamp: row.get(5)?,
metadata: row.get(6)?,
})
})?;
for event in rows {
events.push(event?);
}
Ok(events)
}