Skip to main content

tetratto_core2/database/
audit_log.rs

1use oiseau::cache::Cache;
2use crate::model::id::Id;
3use crate::model::{Error, Result, auth::User, moderation::AuditLogEntry, permissions::FinePermission};
4use crate::{auto_method, DataManager};
5
6use oiseau::PostgresRow;
7
8use oiseau::{execute, get, query_rows, params};
9
10impl DataManager {
11    /// Get an [`AuditLogEntry`] from an SQL row.
12    pub(crate) fn get_audit_log_entry_from_row(x: &PostgresRow) -> AuditLogEntry {
13        AuditLogEntry {
14            id: Id::deserialize(&get!(x->0(String))),
15            created: get!(x->1(i64)) as u128,
16            moderator: Id::deserialize(&get!(x->2(String))),
17            content: get!(x->3(String)),
18        }
19    }
20
21    auto_method!(get_audit_log_entry_by_id()@get_audit_log_entry_from_row -> "SELECT * FROM audit_log WHERE id = $1" --name="audit log entry" --returns=AuditLogEntry --cache-key-tmpl="atto.audit_log:{}");
22
23    /// Get all audit log entries (paginated).
24    ///
25    /// # Arguments
26    /// * `batch` - the limit of items in each page
27    /// * `page` - the page number
28    pub async fn get_audit_log_entries(
29        &self,
30        batch: usize,
31        page: usize,
32    ) -> Result<Vec<AuditLogEntry>> {
33        let conn = match self.0.connect().await {
34            Ok(c) => c,
35            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
36        };
37
38        let res = query_rows!(
39            &conn,
40            "SELECT * FROM audit_log ORDER BY created DESC LIMIT $1 OFFSET $2",
41            &[&(batch as i64), &((page * batch) as i64)],
42            |x| { Self::get_audit_log_entry_from_row(x) }
43        );
44
45        if res.is_err() {
46            return Err(Error::GeneralNotFound("audit log entry".to_string()));
47        }
48
49        Ok(res.unwrap())
50    }
51
52    /// Create a new audit log entry in the database.
53    ///
54    /// # Arguments
55    /// * `data` - a mock [`AuditLogEntry`] object to insert
56    pub async fn create_audit_log_entry(&self, data: AuditLogEntry) -> Result<()> {
57        let conn = match self.0.connect().await {
58            Ok(c) => c,
59            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
60        };
61
62        let res = execute!(
63            &conn,
64            "INSERT INTO audit_log VALUES ($1, $2, $3, $4)",
65            params![
66                &data.id.printable(),
67                &(data.created as i64),
68                &data.moderator.printable(),
69                &data.content.as_str(),
70            ]
71        );
72
73        if let Err(e) = res {
74            return Err(Error::DatabaseError(e.to_string()));
75        }
76
77        // return
78        Ok(())
79    }
80
81    pub async fn delete_audit_log_entry(&self, id: &Id, user: User) -> Result<()> {
82        if !user.permissions.check(FinePermission::ManageAuditLog) {
83            return Err(Error::NotAllowed);
84        }
85
86        let conn = match self.0.connect().await {
87            Ok(c) => c,
88            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
89        };
90
91        let res = execute!(
92            &conn,
93            "DELETE FROM audit_log WHERE id = $1",
94            &[&id.printable()]
95        );
96
97        if let Err(e) = res {
98            return Err(Error::DatabaseError(e.to_string()));
99        }
100
101        self.0.1.remove(format!("atto.audit_log:{}", id)).await;
102
103        // return
104        Ok(())
105    }
106}