tetratto_core/database/
stackblocks.rs1use oiseau::cache::Cache;
2use crate::model::stacks::StackPrivacy;
3use crate::model::{Error, Result, auth::User, stacks::StackBlock, permissions::FinePermission};
4use crate::{auto_method, DataManager};
5
6use oiseau::PostgresRow;
7
8use oiseau::{execute, get, params, query_row, query_rows};
9
10impl DataManager {
11 pub(crate) fn get_stackblock_from_row(x: &PostgresRow) -> StackBlock {
13 StackBlock {
14 id: get!(x->0(i64)) as usize,
15 created: get!(x->1(i64)) as usize,
16 initiator: get!(x->2(i64)) as usize,
17 stack: get!(x->3(i64)) as usize,
18 }
19 }
20
21 auto_method!(get_stackblock_by_id()@get_stackblock_from_row -> "SELECT * FROM stackblocks WHERE id = $1" --name="stack block" --returns=StackBlock --cache-key-tmpl="atto.stackblock:{}");
22
23 pub async fn get_user_stack_blocked_users(&self, user_id: usize) -> Vec<usize> {
24 let mut stack_block_users = Vec::new();
25
26 for block in self.get_stackblocks_by_initiator(user_id).await {
27 for user in match self.fill_stackblocks_receivers(block.stack).await {
28 Ok(ul) => ul,
29 Err(_) => continue,
30 } {
31 stack_block_users.push(user);
32 }
33 }
34
35 stack_block_users
36 }
37
38 pub async fn fill_stackblocks_receivers(&self, stack: usize) -> Result<Vec<usize>> {
40 let stack = self.get_stack_by_id(stack).await?;
41 let mut out = Vec::new();
42
43 for block in stack.users {
44 out.push(block);
45 }
46
47 Ok(out)
48 }
49
50 pub async fn get_stackblocks_by_initiator(&self, initiator: usize) -> Vec<StackBlock> {
52 let conn = match self.0.connect().await {
53 Ok(c) => c,
54 Err(_) => return Vec::new(),
55 };
56
57 let res = query_rows!(
58 &conn,
59 "SELECT * FROM stackblocks WHERE initiator = $1",
60 &[&(initiator as i64)],
61 |x| { Self::get_stackblock_from_row(x) }
62 );
63
64 if res.is_err() {
65 return Vec::new();
66 }
67
68 let list = res.unwrap();
70
71 for block in &list {
72 if self.get_stack_by_id(block.stack).await.is_err()
73 && self.delete_stackblock_sudo(block.id).await.is_err() {
74 continue;
75 }
76 }
77
78 list
80 }
81
82 pub async fn get_stackblock_by_initiator_stack(
84 &self,
85 initiator: usize,
86 stack: usize,
87 ) -> Result<StackBlock> {
88 let conn = match self.0.connect().await {
89 Ok(c) => c,
90 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
91 };
92
93 let res = query_row!(
94 &conn,
95 "SELECT * FROM stackblocks WHERE initiator = $1 AND stack = $2",
96 &[&(initiator as i64), &(stack as i64)],
97 |x| { Ok(Self::get_stackblock_from_row(x)) }
98 );
99
100 if res.is_err() {
101 return Err(Error::GeneralNotFound("stack block".to_string()));
102 }
103
104 Ok(res.unwrap())
105 }
106
107 const MAXIMUM_FREE_STACKBLOCKS: usize = 5;
108 const MAXIMUM_SUPPORTER_STACKBLOCKS: usize = 10;
109
110 pub async fn create_stackblock(&self, data: StackBlock) -> Result<()> {
115 let initiator = self.get_user_by_id(data.initiator).await?;
116
117 let stackblocks = self.get_stackblocks_by_initiator(data.initiator).await;
119
120 if !initiator.permissions.check(FinePermission::SUPPORTER) {
121 if stackblocks.len() >= Self::MAXIMUM_FREE_STACKBLOCKS {
122 return Err(Error::MiscError(
123 "You already have the maximum number of stack blocks you can have".to_string(),
124 ));
125 }
126 } else if stackblocks.len() >= Self::MAXIMUM_SUPPORTER_STACKBLOCKS {
127 return Err(Error::MiscError(
128 "You already have the maximum number of stack blocks you can have".to_string(),
129 ));
130 }
131
132 let stack = self.get_stack_by_id(data.stack).await?;
134
135 if initiator.id != stack.owner
136 && stack.privacy == StackPrivacy::Private
137 && !initiator.permissions.check(FinePermission::MANAGE_STACKS)
138 {
139 return Err(Error::NotAllowed);
140 }
141
142 let conn = match self.0.connect().await {
144 Ok(c) => c,
145 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
146 };
147
148 let res = execute!(
149 &conn,
150 "INSERT INTO stackblocks VALUES ($1, $2, $3, $4)",
151 params![
152 &(data.id as i64),
153 &(data.created as i64),
154 &(data.initiator as i64),
155 &(data.stack as i64)
156 ]
157 );
158
159 if let Err(e) = res {
160 return Err(Error::DatabaseError(e.to_string()));
161 }
162
163 for user in stack.users {
165 if let Ok(f) = self
166 .get_userfollow_by_initiator_receiver(data.initiator, user)
167 .await
168 {
169 self.delete_userfollow_sudo(f.id, data.initiator).await?;
170 }
171
172 if let Ok(f) = self
173 .get_userfollow_by_receiver_initiator(data.initiator, user)
174 .await
175 {
176 self.delete_userfollow_sudo(f.id, data.initiator).await?;
177 }
178 }
179
180 Ok(())
182 }
183
184 pub async fn delete_stackblock(&self, id: usize, user: User) -> Result<()> {
185 let block = self.get_stackblock_by_id(id).await?;
186
187 if user.id != block.initiator {
188 if !user.permissions.check(FinePermission::MANAGE_FOLLOWS) {
190 return Err(Error::NotAllowed);
191 }
192 }
193
194 let conn = match self.0.connect().await {
195 Ok(c) => c,
196 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
197 };
198
199 let res = execute!(
200 &conn,
201 "DELETE FROM stackblocks WHERE id = $1",
202 &[&(id as i64)]
203 );
204
205 if let Err(e) = res {
206 return Err(Error::DatabaseError(e.to_string()));
207 }
208
209 self.0.1.remove(format!("atto.stackblock:{}", id)).await;
210
211 Ok(())
213 }
214
215 pub async fn delete_stackblock_sudo(&self, id: usize) -> Result<()> {
216 let conn = match self.0.connect().await {
217 Ok(c) => c,
218 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
219 };
220
221 let res = execute!(
222 &conn,
223 "DELETE FROM stackblocks WHERE id = $1",
224 &[&(id as i64)]
225 );
226
227 if let Err(e) = res {
228 return Err(Error::DatabaseError(e.to_string()));
229 }
230
231 self.0.1.remove(format!("atto.stackblock:{}", id)).await;
232
233 Ok(())
235 }
236}