1use crate::Orm;
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Debug, Serialize, Deserialize)]
5pub struct AuditLog {
6 pub id: i32,
7 pub model_type: String,
8 pub model_id: i32,
9 pub event: String,
10 pub old_values: Option<String>,
11 pub new_values: Option<String>,
12 pub created_at: Option<String>,
13}
14
15pub async fn log_audit(
16 model_type: &str,
17 model_id: i32,
18 event: &str,
19 old_values: Option<String>,
20 new_values: Option<String>,
21) -> Result<(), sqlx::Error> {
22 let pool = Orm::pool();
23 let driver = Orm::driver();
24
25 if driver == "postgres" {
26 sqlx::query(
27 "INSERT INTO rullst_audits (model_type, model_id, event, old_values, new_values) VALUES ($1, $2, $3, $4, $5)"
28 )
29 .bind(model_type)
30 .bind(model_id)
31 .bind(event)
32 .bind(old_values)
33 .bind(new_values)
34 .execute(pool)
35 .await?;
36 } else {
37 sqlx::query(
38 "INSERT INTO rullst_audits (model_type, model_id, event, old_values, new_values) VALUES (?, ?, ?, ?, ?)"
39 )
40 .bind(model_type)
41 .bind(model_id)
42 .bind(event)
43 .bind(old_values)
44 .bind(new_values)
45 .execute(pool)
46 .await?;
47 }
48
49 Ok(())
50}
51
52pub async fn log_audit_diff(
53 model_type: &str,
54 model_id: i32,
55 event: &str,
56 old_json: &str,
57 new_json: &str,
58) -> Result<(), sqlx::Error> {
59 let old_val: serde_json::Value =
60 serde_json::from_str(old_json).unwrap_or(serde_json::Value::Null);
61 let new_val: serde_json::Value =
62 serde_json::from_str(new_json).unwrap_or(serde_json::Value::Null);
63
64 let mut diff_old = serde_json::Map::new();
65 let mut diff_new = serde_json::Map::new();
66
67 if let (Some(old_obj), Some(new_obj)) = (old_val.as_object(), new_val.as_object()) {
68 for (k, v) in old_obj {
69 if let Some(new_v) = new_obj.get(k)
70 && v != new_v
71 {
72 diff_old.insert(k.clone(), v.clone());
73 diff_new.insert(k.clone(), new_v.clone());
74 }
75 }
76 }
77
78 if diff_old.is_empty() && diff_new.is_empty() {
79 return Ok(()); }
81
82 let final_old = serde_json::to_string(&diff_old).ok();
83 let final_new = serde_json::to_string(&diff_new).ok();
84
85 log_audit(model_type, model_id, event, final_old, final_new).await
86}
87
88pub async fn create_audit_table() -> Result<(), sqlx::Error> {
89 let pool = Orm::pool();
90 let driver = Orm::driver();
91
92 let query = if driver == "postgres" {
93 r#"
94 CREATE TABLE IF NOT EXISTS rullst_audits (
95 id SERIAL PRIMARY KEY,
96 model_type VARCHAR(255) NOT NULL,
97 model_id INT NOT NULL,
98 event VARCHAR(50) NOT NULL,
99 old_values TEXT,
100 new_values TEXT,
101 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
102 )
103 "#
104 } else if driver == "mysql" {
105 r#"
106 CREATE TABLE IF NOT EXISTS rullst_audits (
107 id INT AUTO_INCREMENT PRIMARY KEY,
108 model_type VARCHAR(255) NOT NULL,
109 model_id INT NOT NULL,
110 event VARCHAR(50) NOT NULL,
111 old_values TEXT,
112 new_values TEXT,
113 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
114 )
115 "#
116 } else {
117 r#"
118 CREATE TABLE IF NOT EXISTS rullst_audits (
119 id INTEGER PRIMARY KEY AUTOINCREMENT,
120 model_type TEXT NOT NULL,
121 model_id INTEGER NOT NULL,
122 event TEXT NOT NULL,
123 old_values TEXT,
124 new_values TEXT,
125 created_at DATETIME DEFAULT CURRENT_TIMESTAMP
126 )
127 "#
128 };
129
130 sqlx::query(query).execute(pool).await?;
131 Ok(())
132}