2 use diesel::PgConnection;
5 #[derive(Serialize, Deserialize)]
6 pub struct CreatePost {
11 pub community_id: i32,
15 #[derive(Serialize, Deserialize, Clone)]
16 pub struct PostResponse {
20 #[derive(Serialize, Deserialize)]
26 #[derive(Serialize, Deserialize)]
27 pub struct GetPostResponse {
29 comments: Vec<CommentView>,
30 community: CommunityView,
31 moderators: Vec<CommunityModeratorView>,
32 admins: Vec<UserView>,
36 #[derive(Serialize, Deserialize)]
42 pub community_id: Option<i32>,
46 #[derive(Serialize, Deserialize)]
47 pub struct GetPostsResponse {
51 #[derive(Serialize, Deserialize)]
52 pub struct CreatePostLike {
58 #[derive(Serialize, Deserialize)]
66 removed: Option<bool>,
67 deleted: Option<bool>,
70 stickied: Option<bool>,
71 reason: Option<String>,
75 #[derive(Serialize, Deserialize)]
82 impl Perform<PostResponse> for Oper<CreatePost> {
83 fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
84 let data: &CreatePost = &self.data;
86 let claims = match Claims::decode(&data.auth) {
87 Ok(claims) => claims.claims,
88 Err(_e) => return Err(APIError::err("not_logged_in").into()),
91 if let Err(slurs) = slur_check(&data.name) {
92 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
95 if let Some(body) = &data.body {
96 if let Err(slurs) = slur_check(body) {
97 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
101 let user_id = claims.id;
103 // Check for a community ban
104 if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
105 return Err(APIError::err("community_ban").into());
108 // Check for a site ban
109 if UserView::read(&conn, user_id)?.banned {
110 return Err(APIError::err("site_ban").into());
113 let post_form = PostForm {
114 name: data.name.to_owned(),
115 url: data.url.to_owned(),
116 body: data.body.to_owned(),
117 community_id: data.community_id,
127 let inserted_post = match Post::create(&conn, &post_form) {
129 Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
132 // They like their own post by default
133 let like_form = PostLikeForm {
134 post_id: inserted_post.id,
139 // Only add the like if the score isnt 0
140 let _inserted_like = match PostLike::like(&conn, &like_form) {
142 Err(_e) => return Err(APIError::err("couldnt_like_post").into()),
146 let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) {
148 Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
151 Ok(PostResponse { post: post_view })
155 impl Perform<GetPostResponse> for Oper<GetPost> {
156 fn perform(&self, conn: &PgConnection) -> Result<GetPostResponse, Error> {
157 let data: &GetPost = &self.data;
159 let user_id: Option<i32> = match &data.auth {
160 Some(auth) => match Claims::decode(&auth) {
162 let user_id = claims.claims.id;
170 let post_view = match PostView::read(&conn, data.id, user_id) {
172 Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
175 let comments = CommentQueryBuilder::create(&conn)
176 .for_post_id(data.id)
181 let community = CommunityView::read(&conn, post_view.community_id, user_id)?;
183 let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id)?;
185 let site_creator_id = Site::read(&conn, 1)?.creator_id;
186 let mut admins = UserView::admins(&conn)?;
187 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
188 let creator_user = admins.remove(creator_index);
189 admins.insert(0, creator_user);
203 impl Perform<GetPostsResponse> for Oper<GetPosts> {
204 fn perform(&self, conn: &PgConnection) -> Result<GetPostsResponse, Error> {
205 let data: &GetPosts = &self.data;
207 let user_claims: Option<Claims> = match &data.auth {
208 Some(auth) => match Claims::decode(&auth) {
209 Ok(claims) => Some(claims.claims),
215 let user_id = match &user_claims {
216 Some(claims) => Some(claims.id),
220 let show_nsfw = match &user_claims {
221 Some(claims) => claims.show_nsfw,
225 let type_ = ListingType::from_str(&data.type_)?;
226 let sort = SortType::from_str(&data.sort)?;
228 let posts = match PostQueryBuilder::create(&conn)
231 .show_nsfw(show_nsfw)
232 .for_community_id(data.community_id)
239 Err(_e) => return Err(APIError::err("couldnt_get_posts").into()),
242 Ok(GetPostsResponse { posts })
246 impl Perform<PostResponse> for Oper<CreatePostLike> {
247 fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
248 let data: &CreatePostLike = &self.data;
250 let claims = match Claims::decode(&data.auth) {
251 Ok(claims) => claims.claims,
252 Err(_e) => return Err(APIError::err("not_logged_in").into()),
255 let user_id = claims.id;
257 // Don't do a downvote if site has downvotes disabled
258 if data.score == -1 {
259 let site = SiteView::read(&conn)?;
260 if !site.enable_downvotes {
261 return Err(APIError::err("downvotes_disabled").into());
265 // Check for a community ban
266 let post = Post::read(&conn, data.post_id)?;
267 if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
268 return Err(APIError::err("community_ban").into());
271 // Check for a site ban
272 if UserView::read(&conn, user_id)?.banned {
273 return Err(APIError::err("site_ban").into());
276 let like_form = PostLikeForm {
277 post_id: data.post_id,
282 // Remove any likes first
283 PostLike::remove(&conn, &like_form)?;
285 // Only add the like if the score isnt 0
286 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
288 let _inserted_like = match PostLike::like(&conn, &like_form) {
290 Err(_e) => return Err(APIError::err("couldnt_like_post").into()),
294 let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) {
296 Err(_e) => return Err(APIError::err("couldnt_find_post").into()),
299 // just output the score
300 Ok(PostResponse { post: post_view })
304 impl Perform<PostResponse> for Oper<EditPost> {
305 fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
306 let data: &EditPost = &self.data;
308 if let Err(slurs) = slur_check(&data.name) {
309 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
312 if let Some(body) = &data.body {
313 if let Err(slurs) = slur_check(body) {
314 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
318 let claims = match Claims::decode(&data.auth) {
319 Ok(claims) => claims.claims,
320 Err(_e) => return Err(APIError::err("not_logged_in").into()),
323 let user_id = claims.id;
325 // Verify its the creator or a mod or admin
326 let mut editors: Vec<i32> = vec![data.creator_id];
328 &mut CommunityModeratorView::for_community(&conn, data.community_id)?
333 editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
334 if !editors.contains(&user_id) {
335 return Err(APIError::err("no_post_edit_allowed").into());
338 // Check for a community ban
339 if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
340 return Err(APIError::err("community_ban").into());
343 // Check for a site ban
344 if UserView::read(&conn, user_id)?.banned {
345 return Err(APIError::err("site_ban").into());
348 let post_form = PostForm {
349 name: data.name.to_owned(),
350 url: data.url.to_owned(),
351 body: data.body.to_owned(),
352 creator_id: data.creator_id.to_owned(),
353 community_id: data.community_id,
354 removed: data.removed.to_owned(),
355 deleted: data.deleted.to_owned(),
357 locked: data.locked.to_owned(),
358 stickied: data.stickied.to_owned(),
359 updated: Some(naive_now()),
362 let _updated_post = match Post::update(&conn, data.edit_id, &post_form) {
364 Err(_e) => return Err(APIError::err("couldnt_update_post").into()),
368 if let Some(removed) = data.removed.to_owned() {
369 let form = ModRemovePostForm {
370 mod_user_id: user_id,
371 post_id: data.edit_id,
372 removed: Some(removed),
373 reason: data.reason.to_owned(),
375 ModRemovePost::create(&conn, &form)?;
378 if let Some(locked) = data.locked.to_owned() {
379 let form = ModLockPostForm {
380 mod_user_id: user_id,
381 post_id: data.edit_id,
382 locked: Some(locked),
384 ModLockPost::create(&conn, &form)?;
387 if let Some(stickied) = data.stickied.to_owned() {
388 let form = ModStickyPostForm {
389 mod_user_id: user_id,
390 post_id: data.edit_id,
391 stickied: Some(stickied),
393 ModStickyPost::create(&conn, &form)?;
396 let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
398 Ok(PostResponse { post: post_view })
402 impl Perform<PostResponse> for Oper<SavePost> {
403 fn perform(&self, conn: &PgConnection) -> Result<PostResponse, Error> {
404 let data: &SavePost = &self.data;
406 let claims = match Claims::decode(&data.auth) {
407 Ok(claims) => claims.claims,
408 Err(_e) => return Err(APIError::err("not_logged_in").into()),
411 let user_id = claims.id;
413 let post_saved_form = PostSavedForm {
414 post_id: data.post_id,
419 match PostSaved::save(&conn, &post_saved_form) {
421 Err(_e) => return Err(APIError::err("couldnt_save_post").into()),
424 match PostSaved::unsave(&conn, &post_saved_form) {
426 Err(_e) => return Err(APIError::err("couldnt_save_post").into()),
430 let post_view = PostView::read(&conn, data.post_id, Some(user_id))?;
432 Ok(PostResponse { post: post_view })