Skip to main content

tetratto_core2/database/
reports.rs

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