tetratto_core/database/
userblocks.rs

1use oiseau::cache::Cache;
2use crate::model::{Error, Result, auth::User, auth::UserBlock, permissions::FinePermission};
3use crate::{auto_method, DataManager};
4
5use oiseau::{PostgresRow, execute, get, query_row, query_rows, params};
6
7impl DataManager {
8    /// Get a [`UserBlock`] from an SQL row.
9    pub(crate) fn get_userblock_from_row(x: &PostgresRow) -> UserBlock {
10        UserBlock {
11            id: get!(x->0(i64)) as usize,
12            created: get!(x->1(i64)) as usize,
13            initiator: get!(x->2(i64)) as usize,
14            receiver: get!(x->3(i64)) as usize,
15        }
16    }
17
18    auto_method!(get_userblock_by_id()@get_userblock_from_row -> "SELECT * FROM userblocks WHERE id = $1" --name="user block" --returns=UserBlock --cache-key-tmpl="atto.userblock:{}");
19
20    /// Fill a vector of user blocks with their receivers.
21    pub async fn fill_userblocks_receivers(&self, list: Vec<UserBlock>) -> Result<Vec<User>> {
22        let mut out = Vec::new();
23
24        for block in list {
25            out.push(match self.get_user_by_id(block.receiver).await {
26                Ok(ua) => ua,
27                Err(_) => {
28                    self.delete_userblock_sudo(block.id).await?;
29                    continue;
30                }
31            });
32        }
33
34        Ok(out)
35    }
36
37    /// Get a user block by `initiator` and `receiver` (in that order).
38    pub async fn get_userblock_by_initiator_receiver(
39        &self,
40        initiator: usize,
41        receiver: usize,
42    ) -> Result<UserBlock> {
43        let conn = match self.0.connect().await {
44            Ok(c) => c,
45            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
46        };
47
48        let res = query_row!(
49            &conn,
50            "SELECT * FROM userblocks WHERE initiator = $1 AND receiver = $2",
51            &[&(initiator as i64), &(receiver as i64)],
52            |x| { Ok(Self::get_userblock_from_row(x)) }
53        );
54
55        if res.is_err() {
56            return Err(Error::GeneralNotFound("user block".to_string()));
57        }
58
59        Ok(res.unwrap())
60    }
61
62    /// Get a user block by `receiver` and `initiator` (in that order).
63    pub async fn get_userblock_by_receiver_initiator(
64        &self,
65        receiver: usize,
66        initiator: usize,
67    ) -> Result<UserBlock> {
68        let conn = match self.0.connect().await {
69            Ok(c) => c,
70            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
71        };
72
73        let res = query_row!(
74            &conn,
75            "SELECT * FROM userblocks WHERE receiver = $1 AND initiator = $2",
76            &[&(receiver as i64), &(initiator as i64)],
77            |x| { Ok(Self::get_userblock_from_row(x)) }
78        );
79
80        if res.is_err() {
81            return Err(Error::GeneralNotFound("user block".to_string()));
82        }
83
84        Ok(res.unwrap())
85    }
86
87    /// Get the receiver of all user blocks for the given `initiator`.
88    pub async fn get_userblocks_receivers(
89        &self,
90        initiator: usize,
91        associated: &Vec<usize>,
92        do_associated: bool,
93    ) -> Vec<usize> {
94        let mut associated_str = String::new();
95
96        if do_associated {
97            for id in associated {
98                associated_str.push_str(&(" OR initiator = ".to_string() + &id.to_string()));
99            }
100        }
101
102        // ...
103        let conn = match self.0.connect().await {
104            Ok(c) => c,
105            Err(_) => return Vec::new(),
106        };
107
108        let res = query_rows!(
109            &conn,
110            &format!("SELECT * FROM userblocks WHERE initiator = $1{associated_str}"),
111            &[&(initiator as i64)],
112            |x| { Self::get_userblock_from_row(x) }
113        );
114
115        if res.is_err() {
116            return Vec::new();
117        }
118
119        // get receivers
120        let mut out: Vec<usize> = Vec::new();
121
122        for b in res.unwrap() {
123            out.push(b.receiver);
124        }
125
126        // return
127        out
128    }
129
130    /// Get all user blocks created by the given `initiator`.
131    pub async fn get_userblocks_by_initiator(&self, initiator: usize) -> Vec<UserBlock> {
132        let conn = match self.0.connect().await {
133            Ok(c) => c,
134            Err(_) => return Vec::new(),
135        };
136
137        let res = query_rows!(
138            &conn,
139            "SELECT * FROM userblocks WHERE initiator = $1",
140            &[&(initiator as i64)],
141            |x| { Self::get_userblock_from_row(x) }
142        );
143
144        if res.is_err() {
145            return Vec::new();
146        }
147
148        // return
149        res.unwrap()
150    }
151
152    /// Get the owner of all user blocks for the given `receiver`.
153    pub async fn get_userblocks_initiator_by_receivers(&self, receiver: usize) -> Vec<usize> {
154        let conn = match self.0.connect().await {
155            Ok(c) => c,
156            Err(_) => return Vec::new(),
157        };
158
159        let res = query_rows!(
160            &conn,
161            "SELECT * FROM userblocks WHERE receiver = $1",
162            &[&(receiver as i64)],
163            |x| { Self::get_userblock_from_row(x) }
164        );
165
166        if res.is_err() {
167            return Vec::new();
168        }
169
170        // get owner
171        let mut out: Vec<usize> = Vec::new();
172
173        for b in res.unwrap() {
174            out.push(b.initiator);
175        }
176
177        // return
178        out
179    }
180
181    /// Create a new user block in the database.
182    ///
183    /// # Arguments
184    /// * `data` - a mock [`UserBlock`] object to insert
185    pub async fn create_userblock(&self, data: UserBlock) -> Result<()> {
186        let initiator = self.get_user_by_id(data.initiator).await?;
187        let receiver = self.get_user_by_id(data.receiver).await?;
188
189        // ...
190        let conn = match self.0.connect().await {
191            Ok(c) => c,
192            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
193        };
194
195        let res = execute!(
196            &conn,
197            "INSERT INTO userblocks VALUES ($1, $2, $3, $4)",
198            params![
199                &(data.id as i64),
200                &(data.created as i64),
201                &(data.initiator as i64),
202                &(data.receiver as i64)
203            ]
204        );
205
206        if let Err(e) = res {
207            return Err(Error::DatabaseError(e.to_string()));
208        }
209
210        // remove initiator from receiver's communities
211        for community in self.get_communities_by_owner(data.receiver).await? {
212            if let Ok(membership) = self
213                .get_membership_by_owner_community_no_void(data.initiator, community.id)
214                .await
215            {
216                self.delete_membership_force(membership.id).await?;
217            }
218        }
219
220        // unfollow/remove follower
221        if let Ok(f) = self
222            .get_userfollow_by_initiator_receiver(data.initiator, data.receiver)
223            .await
224        {
225            self.delete_userfollow(f.id, &initiator, false).await?;
226        }
227
228        if let Ok(f) = self
229            .get_userfollow_by_receiver_initiator(data.initiator, data.receiver)
230            .await
231        {
232            self.delete_userfollow(f.id, &receiver, false).await?;
233        }
234
235        // return
236        Ok(())
237    }
238
239    pub async fn delete_userblock(&self, id: usize, user: User) -> Result<()> {
240        let block = self.get_userblock_by_id(id).await?;
241
242        if user.id != block.initiator {
243            // only the initiator (or moderators) can delete user blocks!
244            if !user.permissions.check(FinePermission::MANAGE_FOLLOWS) {
245                return Err(Error::NotAllowed);
246            }
247        }
248
249        let conn = match self.0.connect().await {
250            Ok(c) => c,
251            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
252        };
253
254        let res = execute!(
255            &conn,
256            "DELETE FROM userblocks WHERE id = $1",
257            &[&(id as i64)]
258        );
259
260        if let Err(e) = res {
261            return Err(Error::DatabaseError(e.to_string()));
262        }
263
264        self.0.1.remove(format!("atto.userblock:{}", id)).await;
265
266        // return
267        Ok(())
268    }
269
270    pub async fn delete_userblock_sudo(&self, id: usize) -> Result<()> {
271        let conn = match self.0.connect().await {
272            Ok(c) => c,
273            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
274        };
275
276        let res = execute!(
277            &conn,
278            "DELETE FROM userblocks WHERE id = $1",
279            &[&(id as i64)]
280        );
281
282        if let Err(e) = res {
283            return Err(Error::DatabaseError(e.to_string()));
284        }
285
286        self.0.1.remove(format!("atto.userblock:{}", id)).await;
287
288        // return
289        Ok(())
290    }
291}