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 if self.delete_stackblock_sudo(block.id).await.is_err() {
74 continue;
75 }
76 }
77 }
78
79 list
81 }
82
83 pub async fn get_stackblock_by_initiator_stack(
85 &self,
86 initiator: usize,
87 stack: usize,
88 ) -> Result<StackBlock> {
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 = query_row!(
95 &conn,
96 "SELECT * FROM stackblocks WHERE initiator = $1 AND stack = $2",
97 &[&(initiator as i64), &(stack as i64)],
98 |x| { Ok(Self::get_stackblock_from_row(x)) }
99 );
100
101 if res.is_err() {
102 return Err(Error::GeneralNotFound("stack block".to_string()));
103 }
104
105 Ok(res.unwrap())
106 }
107
108 const MAXIMUM_FREE_STACKBLOCKS: usize = 5;
109 const MAXIMUM_SUPPORTER_STACKBLOCKS: usize = 10;
110
111 pub async fn create_stackblock(&self, data: StackBlock) -> Result<()> {
116 let initiator = self.get_user_by_id(data.initiator).await?;
117
118 let stackblocks = self.get_stackblocks_by_initiator(data.initiator).await;
120
121 if !initiator.permissions.check(FinePermission::SUPPORTER) {
122 if stackblocks.len() >= Self::MAXIMUM_FREE_STACKBLOCKS {
123 return Err(Error::MiscError(
124 "You already have the maximum number of stack blocks you can have".to_string(),
125 ));
126 }
127 } else {
128 if stackblocks.len() >= Self::MAXIMUM_SUPPORTER_STACKBLOCKS {
129 return Err(Error::MiscError(
130 "You already have the maximum number of stack blocks you can have".to_string(),
131 ));
132 }
133 }
134
135 let stack = self.get_stack_by_id(data.stack).await?;
137
138 if initiator.id != stack.owner
139 && stack.privacy == StackPrivacy::Private
140 && !initiator.permissions.check(FinePermission::MANAGE_STACKS)
141 {
142 return Err(Error::NotAllowed);
143 }
144
145 let conn = match self.0.connect().await {
147 Ok(c) => c,
148 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
149 };
150
151 let res = execute!(
152 &conn,
153 "INSERT INTO stackblocks VALUES ($1, $2, $3, $4)",
154 params![
155 &(data.id as i64),
156 &(data.created as i64),
157 &(data.initiator as i64),
158 &(data.stack as i64)
159 ]
160 );
161
162 if let Err(e) = res {
163 return Err(Error::DatabaseError(e.to_string()));
164 }
165
166 for user in stack.users {
168 if let Ok(f) = self
169 .get_userfollow_by_initiator_receiver(data.initiator, user)
170 .await
171 {
172 self.delete_userfollow_sudo(f.id, data.initiator).await?;
173 }
174
175 if let Ok(f) = self
176 .get_userfollow_by_receiver_initiator(data.initiator, user)
177 .await
178 {
179 self.delete_userfollow_sudo(f.id, data.initiator).await?;
180 }
181 }
182
183 Ok(())
185 }
186
187 pub async fn delete_stackblock(&self, id: usize, user: User) -> Result<()> {
188 let block = self.get_stackblock_by_id(id).await?;
189
190 if user.id != block.initiator {
191 if !user.permissions.check(FinePermission::MANAGE_FOLLOWS) {
193 return Err(Error::NotAllowed);
194 }
195 }
196
197 let conn = match self.0.connect().await {
198 Ok(c) => c,
199 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
200 };
201
202 let res = execute!(
203 &conn,
204 "DELETE FROM stackblocks WHERE id = $1",
205 &[&(id as i64)]
206 );
207
208 if let Err(e) = res {
209 return Err(Error::DatabaseError(e.to_string()));
210 }
211
212 self.0.1.remove(format!("atto.stackblock:{}", id)).await;
213
214 Ok(())
216 }
217
218 pub async fn delete_stackblock_sudo(&self, id: usize) -> Result<()> {
219 let conn = match self.0.connect().await {
220 Ok(c) => c,
221 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
222 };
223
224 let res = execute!(
225 &conn,
226 "DELETE FROM stackblocks WHERE id = $1",
227 &[&(id as i64)]
228 );
229
230 if let Err(e) = res {
231 return Err(Error::DatabaseError(e.to_string()));
232 }
233
234 self.0.1.remove(format!("atto.stackblock:{}", id)).await;
235
236 Ok(())
238 }
239}