tetratto_core/database/
drafts.rs1use oiseau::cache::Cache;
2use crate::model::moderation::AuditLogEntry;
3use crate::model::{Error, Result, auth::User, communities::PostDraft, permissions::FinePermission};
4use crate::{auto_method, DataManager};
5
6use oiseau::PostgresRow;
7
8use oiseau::{execute, get, query_rows, params};
9
10impl DataManager {
11 pub(crate) fn get_draft_from_row(x: &PostgresRow) -> PostDraft {
13 PostDraft {
14 id: get!(x->0(i64)) as usize,
15 created: get!(x->1(i64)) as usize,
16 content: get!(x->2(String)),
17 owner: get!(x->3(i64)) as usize,
18 }
19 }
20
21 auto_method!(get_draft_by_id()@get_draft_from_row -> "SELECT * FROM drafts WHERE id = $1" --name="draft" --returns=PostDraft --cache-key-tmpl="atto.draft:{}");
22
23 pub async fn get_drafts_by_user(
30 &self,
31 id: usize,
32 batch: usize,
33 page: usize,
34 ) -> Result<Vec<PostDraft>> {
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!(
42 &conn,
43 "SELECT * FROM drafts WHERE owner = $1 ORDER BY created DESC LIMIT $2 OFFSET $3",
44 &[&(id as i64), &(batch as i64), &((page * batch) as i64)],
45 |x| { Self::get_draft_from_row(x) }
46 );
47
48 if res.is_err() {
49 return Err(Error::GeneralNotFound("draft".to_string()));
50 }
51
52 Ok(res.unwrap())
53 }
54
55 pub async fn get_drafts_by_user_all(&self, id: usize) -> Result<Vec<PostDraft>> {
60 let conn = match self.0.connect().await {
61 Ok(c) => c,
62 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
63 };
64
65 let res = query_rows!(
67 &conn,
68 "SELECT * FROM drafts WHERE owner = $1 ORDER BY created DESC",
69 &[&(id as i64)],
70 |x| { Self::get_draft_from_row(x) }
71 );
72
73 if res.is_err() {
74 return Err(Error::GeneralNotFound("draft".to_string()));
75 }
76
77 Ok(res.unwrap())
78 }
79
80 const MAXIMUM_FREE_DRAFTS: usize = 10;
81
82 pub async fn create_draft(&self, data: PostDraft) -> Result<usize> {
87 if data.content.len() < 2 {
89 return Err(Error::DataTooShort("content".to_string()));
90 } else if data.content.len() > 4096 {
91 return Err(Error::DataTooLong("content".to_string()));
92 }
93
94 let owner = self.get_user_by_id(data.owner).await?;
96
97 if !owner.permissions.check(FinePermission::SUPPORTER) {
98 let stacks = self.get_stacks_by_user(data.owner).await?;
99
100 if stacks.len() >= Self::MAXIMUM_FREE_DRAFTS {
101 return Err(Error::MiscError(
102 "You already have the maximum number of drafts you can have".to_string(),
103 ));
104 }
105 }
106
107 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 "INSERT INTO drafts VALUES ($1, $2, $3, $4)",
116 params![
117 &(data.id as i64),
118 &(data.created as i64),
119 &data.content,
120 &(data.owner as i64),
121 ]
122 );
123
124 if let Err(e) = res {
125 return Err(Error::DatabaseError(e.to_string()));
126 }
127
128 Ok(data.id)
129 }
130
131 pub async fn delete_draft(&self, id: usize, user: User) -> Result<()> {
132 let y = self.get_draft_by_id(id).await?;
133
134 if user.id != y.owner {
135 if !user.permissions.check(FinePermission::MANAGE_POSTS) {
136 return Err(Error::NotAllowed);
137 } else {
138 self.create_audit_log_entry(AuditLogEntry::new(
139 user.id,
140 format!("invoked `delete_draft` with x value `{id}`"),
141 ))
142 .await?
143 }
144 }
145 let conn = match self.0.connect().await {
146 Ok(c) => c,
147 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
148 };
149
150 let res = execute!(&conn, "DELETE FROM drafts WHERE id = $1", &[&(id as i64)]);
151
152 if let Err(e) = res {
153 return Err(Error::DatabaseError(e.to_string()));
154 }
155
156 self.0.1.remove(format!("atto.draft:{}", id)).await;
157
158 Ok(())
159 }
160
161 pub async fn update_draft_content(&self, id: usize, user: User, x: String) -> Result<()> {
162 let y = self.get_draft_by_id(id).await?;
163
164 if user.id != y.owner {
165 if !user.permissions.check(FinePermission::MANAGE_POSTS) {
166 return Err(Error::NotAllowed);
167 } else {
168 self.create_audit_log_entry(AuditLogEntry::new(
169 user.id,
170 format!("invoked `update_draft_content` with x value `{id}`"),
171 ))
172 .await?
173 }
174 }
175
176 let conn = match self.0.connect().await {
178 Ok(c) => c,
179 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
180 };
181
182 let res = execute!(
183 &conn,
184 "UPDATE drafts SET content = $1 WHERE id = $2",
185 params![&x, &(id as i64)]
186 );
187
188 if let Err(e) = res {
189 return Err(Error::DatabaseError(e.to_string()));
190 }
191
192 Ok(())
193 }
194}