Skip to main content

tetratto_core2/database/
user_warnings.rs

1use oiseau::cache::Cache;
2use crate::model::auth::{Notification, UserWarning};
3use crate::model::id::Id;
4use crate::model::moderation::AuditLogEntry;
5use crate::model::{Error, Result, auth::User, permissions::FinePermission};
6use crate::{auto_method, DataManager};
7
8use oiseau::{PostgresRow, execute, get, query_rows, params};
9
10impl DataManager {
11    /// Get a [`UserWarning`] from an SQL row.
12    pub(crate) fn get_user_warning_from_row(x: &PostgresRow) -> UserWarning {
13        UserWarning {
14            id: Id::deserialize(&get!(x->0(String))),
15            created: get!(x->1(i64)) as u128,
16            receiver: Id::deserialize(&get!(x->2(String))),
17            moderator: Id::deserialize(&get!(x->3(String))),
18            content: get!(x->4(String)),
19        }
20    }
21
22    auto_method!(get_user_warning_by_id()@get_user_warning_from_row -> "SELECT * FROM user_warnings WHERE id = $1" --name="user warning" --returns=UserWarning --cache-key-tmpl="atto.user_warning:{}");
23
24    /// Get all user warnings by user (paginated).
25    ///
26    /// # Arguments
27    /// * `user` - the ID of the user to fetch warnings for
28    /// * `batch` - the limit of items in each page
29    /// * `page` - the page number
30    pub async fn get_user_warnings_by_user(
31        &self,
32        user: &Id,
33        batch: usize,
34        page: usize,
35    ) -> Result<Vec<UserWarning>> {
36        let conn = match self.0.connect().await {
37            Ok(c) => c,
38            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
39        };
40
41        let res = query_rows!(
42            &conn,
43            "SELECT * FROM user_warnings WHERE receiver = $1 ORDER BY created DESC LIMIT $2 OFFSET $3",
44            &[&user.printable(), &(batch as i64), &((page * batch) as i64)],
45            |x| { Self::get_user_warning_from_row(x) }
46        );
47
48        if res.is_err() {
49            return Err(Error::GeneralNotFound("user warning".to_string()));
50        }
51
52        Ok(res.unwrap())
53    }
54
55    /// Create a new user warning in the database.
56    ///
57    /// # Arguments
58    /// * `data` - a mock [`UserWarning`] object to insert
59    pub async fn create_user_warning(&self, data: UserWarning) -> Result<()> {
60        let user = self.get_user_by_id(&data.moderator).await?;
61
62        // ONLY moderators can create warnings
63        if !user.permissions.check(FinePermission::ManageWarnings) {
64            return Err(Error::NotAllowed);
65        }
66
67        let conn = match self.0.connect().await {
68            Ok(c) => c,
69            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
70        };
71
72        let res = execute!(
73            &conn,
74            "INSERT INTO user_warnings VALUES ($1, $2, $3, $4, $5)",
75            params![
76                &data.id.printable(),
77                &(data.created as i64),
78                &data.receiver.printable(),
79                &data.moderator.printable(),
80                &data.content
81            ]
82        );
83
84        if let Err(e) = res {
85            return Err(Error::DatabaseError(e.to_string()));
86        }
87
88        // create audit log entry
89        self.create_audit_log_entry(AuditLogEntry::new(
90            user.id,
91            format!(
92                "invoked `create_user_warning` with x value `{}`",
93                data.receiver
94            ),
95        ))
96        .await?;
97
98        // send notification
99        self.create_notification(Notification::new(
100            "You have received a new account warning.".to_string(),
101            data.content,
102            data.receiver,
103        ))
104        .await?;
105
106        // return
107        Ok(())
108    }
109
110    pub async fn delete_user_warning(&self, id: &Id, user: User) -> Result<()> {
111        // ONLY moderators can manage warnings
112        if !user.permissions.check(FinePermission::ManageWarnings) {
113            return Err(Error::NotAllowed);
114        }
115
116        let conn = match self.0.connect().await {
117            Ok(c) => c,
118            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
119        };
120
121        let res = execute!(
122            &conn,
123            "DELETE FROM user_warnings WHERE id = $1",
124            &[&id.printable()]
125        );
126
127        if let Err(e) = res {
128            return Err(Error::DatabaseError(e.to_string()));
129        }
130
131        self.0.1.remove(format!("atto.user_warning:{}", id)).await;
132
133        // create audit log entry
134        self.create_audit_log_entry(AuditLogEntry::new(
135            user.id,
136            format!("invoked `delete_user_warning` with x value `{id}`"),
137        ))
138        .await?;
139
140        // return
141        Ok(())
142    }
143}