Skip to main content

tetratto_core2/database/
groups.rs

1use crate::{
2    DataManager, auto_method,
3    model::{
4        Error, Result,
5        groups::{GroupSettings, GroupMembership, Group, GroupRole},
6        id::Id,
7    },
8};
9use oiseau::query_rows;
10use oiseau::{PostgresRow, cache::Cache, execute, get, params, query_row};
11
12impl DataManager {
13    /// Get a [`Group`] from an SQL row.
14    pub(crate) fn get_group_from_row(x: &PostgresRow) -> Group {
15        Group {
16            id: Id::deserialize(&get!(x->0(String))),
17            created: get!(x->1(i64)) as u128,
18            owner: Id::deserialize(&get!(x->2(String))),
19            name: get!(x->3(String)),
20            settings: serde_json::from_str(&get!(x->4(String))).unwrap(),
21            members: get!(x->5(i32)),
22        }
23    }
24
25    auto_method!(get_group_by_id()@get_group_from_row -> "SELECT * FROM groups WHERE id = $1" --name="group" --returns=Group --cache-key-tmpl="atto.group:{}");
26    auto_method!(get_group_by_name(&str)@get_group_from_row -> "SELECT * FROM groups WHERE name = $1" --name="group" --returns=Group --cache-key-tmpl="atto.group:{}");
27
28    /// Get groups by a search query.
29    pub async fn get_groups_searched(
30        &self,
31        query: &str,
32        batch: usize,
33        page: usize,
34    ) -> Result<Vec<Group>> {
35        let conn = match self.0.connect().await {
36            Ok(c) => c,
37            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
38        };
39
40        let res = query_rows!(
41            &conn,
42            "SELECT * FROM groups WHERE name LIKE $1 ORDER BY created DESC LIMIT $2 OFFSET $3",
43            &[
44                &format!("%{query}%"),
45                &(batch as i64),
46                &((page * batch) as i64)
47            ],
48            |x| { Self::get_group_from_row(x) }
49        );
50
51        if res.is_err() {
52            return Err(Error::GeneralNotFound("group".to_string()));
53        }
54
55        Ok(res.unwrap())
56    }
57
58    /// Create a new group in the database.
59    ///
60    /// # Arguments
61    /// * `data` - a mock [`Group`] object to insert
62    pub async fn create_group(&self, mut data: Group) -> Result<Id> {
63        data.name = data.name.to_lowercase();
64
65        // check for existing
66        if self.get_group_by_name(&data.name).await.is_ok() {
67            return Err(Error::MiscError("Name is already in use".to_string()));
68        }
69
70        // ...
71        let conn = match self.0.connect().await {
72            Ok(c) => c,
73            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
74        };
75
76        let res = execute!(
77            &conn,
78            "INSERT INTO groups VALUES ($1, $2, $3, $4, $5, $6)",
79            params![
80                &data.id.printable(),
81                &(data.created as i64),
82                &data.owner.printable(),
83                &data.name,
84                &serde_json::to_string(&data.settings).unwrap(),
85                &data.members,
86            ]
87        );
88
89        if let Err(e) = res {
90            return Err(Error::DatabaseError(e.to_string()));
91        }
92
93        // create membership
94        self.create_group_membership(GroupMembership::new(
95            data.owner,
96            data.id.clone(),
97            GroupRole::Owner,
98        ))
99        .await?;
100
101        // return
102        Ok(data.id)
103    }
104
105    pub async fn update_group_settings(&self, id: &Id, settings: GroupSettings) -> Result<()> {
106        let y = self.get_group_by_id(id).await?;
107
108        let conn = match self.0.connect().await {
109            Ok(c) => c,
110            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
111        };
112
113        let res = execute!(
114            &conn,
115            "UPDATE groups SET settings = $1 WHERE id = $2",
116            &[&serde_json::to_string(&settings).unwrap(), &id.printable()]
117        );
118
119        if let Err(e) = res {
120            return Err(Error::DatabaseError(e.to_string()));
121        }
122
123        self.cache_clear_group(&y).await;
124
125        // return
126        Ok(())
127    }
128
129    pub async fn update_group_owner(&self, id: &Id, owner: Id) -> Result<()> {
130        let y = self.get_group_by_id(id).await?;
131
132        let conn = match self.0.connect().await {
133            Ok(c) => c,
134            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
135        };
136
137        let res = execute!(
138            &conn,
139            "UPDATE groups SET owner = $1 WHERE id = $2",
140            &[&owner.printable(), &id.printable()]
141        );
142
143        if let Err(e) = res {
144            return Err(Error::DatabaseError(e.to_string()));
145        }
146
147        self.cache_clear_group(&y).await;
148
149        // return
150        Ok(())
151    }
152
153    pub async fn delete_group(&self, id: &Id) -> Result<()> {
154        let y = self.get_group_by_id(id).await?;
155
156        let conn = match self.0.connect().await {
157            Ok(c) => c,
158            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
159        };
160
161        let res = execute!(
162            &conn,
163            "DELETE FROM groups WHERE id = $1",
164            &[&id.printable()]
165        );
166
167        if let Err(e) = res {
168            return Err(Error::DatabaseError(e.to_string()));
169        }
170
171        self.cache_clear_group(&y).await;
172
173        // delete images
174        if let Ok(avatar) = self
175            .1
176            .get_upload_by_id_bucket(id.as_usize(), "group_avatars")
177            .await
178            && let Err(e) = self
179                .1
180                .delete_upload_with_bucket(avatar.id, "group_avatars")
181                .await
182            {
183                return Err(Error::MiscError(e.to_string()));
184            }
185
186        if let Ok(banner) = self
187            .1
188            .get_upload_by_id_bucket(id.as_usize(), "group_banners")
189            .await
190            && let Err(e) = self
191                .1
192                .delete_upload_with_bucket(banner.id, "group_banners")
193                .await
194            {
195                return Err(Error::MiscError(e.to_string()));
196            }
197
198        // return
199        Ok(())
200    }
201
202    pub async fn cache_clear_group(&self, c: &Group) -> bool {
203        self.0.1.remove(format!("atto.group:{}", c.id)).await
204            && self.0.1.remove(format!("atto.group:{}", c.name)).await
205    }
206
207    auto_method!(incr_group_members()@get_group_by_id -> "UPDATE groups SET members = members + 1 WHERE id = $1" --cache-key-tmpl=cache_clear_group --incr);
208    auto_method!(decr_group_members()@get_group_by_id -> "UPDATE groups SET members = members - 1 WHERE id = $1" --cache-key-tmpl=cache_clear_group --decr=members);
209}