]> Untitled Git - lemmy.git/blob - crates/api/src/post_report.rs
Rework error handling (fixes #1714) (#2135)
[lemmy.git] / crates / api / src / post_report.rs
1 use crate::Perform;
2 use actix_web::web::Data;
3 use lemmy_api_common::{
4   blocking,
5   check_community_ban,
6   get_local_user_view_from_jwt,
7   is_mod_or_admin,
8   post::{
9     CreatePostReport,
10     ListPostReports,
11     ListPostReportsResponse,
12     PostReportResponse,
13     ResolvePostReport,
14   },
15 };
16 use lemmy_apub::protocol::activities::community::report::Report;
17 use lemmy_apub_lib::object_id::ObjectId;
18 use lemmy_db_schema::{
19   source::post_report::{PostReport, PostReportForm},
20   traits::Reportable,
21 };
22 use lemmy_db_views::{
23   post_report_view::{PostReportQueryBuilder, PostReportView},
24   post_view::PostView,
25 };
26 use lemmy_utils::{ConnectionId, LemmyError};
27 use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
28
29 /// Creates a post report and notifies the moderators of the community
30 #[async_trait::async_trait(?Send)]
31 impl Perform for CreatePostReport {
32   type Response = PostReportResponse;
33
34   #[tracing::instrument(skip(context, websocket_id))]
35   async fn perform(
36     &self,
37     context: &Data<LemmyContext>,
38     websocket_id: Option<ConnectionId>,
39   ) -> Result<PostReportResponse, LemmyError> {
40     let data: &CreatePostReport = self;
41     let local_user_view =
42       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
43
44     // check size of report and check for whitespace
45     let reason = data.reason.trim();
46     if reason.is_empty() {
47       return Err(LemmyError::from_message("report_reason_required"));
48     }
49     if reason.chars().count() > 1000 {
50       return Err(LemmyError::from_message("report_too_long"));
51     }
52
53     let person_id = local_user_view.person.id;
54     let post_id = data.post_id;
55     let post_view = blocking(context.pool(), move |conn| {
56       PostView::read(conn, post_id, None)
57     })
58     .await??;
59
60     check_community_ban(person_id, post_view.community.id, context.pool()).await?;
61
62     let report_form = PostReportForm {
63       creator_id: person_id,
64       post_id,
65       original_post_name: post_view.post.name,
66       original_post_url: post_view.post.url,
67       original_post_body: post_view.post.body,
68       reason: data.reason.to_owned(),
69     };
70
71     let report = blocking(context.pool(), move |conn| {
72       PostReport::report(conn, &report_form)
73     })
74     .await?
75     .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?;
76
77     let post_report_view = blocking(context.pool(), move |conn| {
78       PostReportView::read(conn, report.id, person_id)
79     })
80     .await??;
81
82     let res = PostReportResponse { post_report_view };
83
84     context.chat_server().do_send(SendModRoomMessage {
85       op: UserOperation::CreatePostReport,
86       response: res.clone(),
87       community_id: post_view.community.id,
88       websocket_id,
89     });
90
91     Report::send(
92       ObjectId::new(post_view.post.ap_id),
93       &local_user_view.person.into(),
94       ObjectId::new(post_view.community.actor_id),
95       reason.to_string(),
96       context,
97     )
98     .await?;
99
100     Ok(res)
101   }
102 }
103
104 /// Resolves or unresolves a post report and notifies the moderators of the community
105 #[async_trait::async_trait(?Send)]
106 impl Perform for ResolvePostReport {
107   type Response = PostReportResponse;
108
109   #[tracing::instrument(skip(context, websocket_id))]
110   async fn perform(
111     &self,
112     context: &Data<LemmyContext>,
113     websocket_id: Option<ConnectionId>,
114   ) -> Result<PostReportResponse, LemmyError> {
115     let data: &ResolvePostReport = self;
116     let local_user_view =
117       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
118
119     let report_id = data.report_id;
120     let person_id = local_user_view.person.id;
121     let report = blocking(context.pool(), move |conn| {
122       PostReportView::read(conn, report_id, person_id)
123     })
124     .await??;
125
126     let person_id = local_user_view.person.id;
127     is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
128
129     let resolved = data.resolved;
130     let resolve_fun = move |conn: &'_ _| {
131       if resolved {
132         PostReport::resolve(conn, report_id, person_id)
133       } else {
134         PostReport::unresolve(conn, report_id, person_id)
135       }
136     };
137
138     blocking(context.pool(), resolve_fun)
139       .await?
140       .map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?;
141
142     let post_report_view = blocking(context.pool(), move |conn| {
143       PostReportView::read(conn, report_id, person_id)
144     })
145     .await??;
146
147     let res = PostReportResponse { post_report_view };
148
149     context.chat_server().do_send(SendModRoomMessage {
150       op: UserOperation::ResolvePostReport,
151       response: res.clone(),
152       community_id: report.community.id,
153       websocket_id,
154     });
155
156     Ok(res)
157   }
158 }
159
160 /// Lists post reports for a community if an id is supplied
161 /// or returns all post reports for communities a user moderates
162 #[async_trait::async_trait(?Send)]
163 impl Perform for ListPostReports {
164   type Response = ListPostReportsResponse;
165
166   #[tracing::instrument(skip(context, _websocket_id))]
167   async fn perform(
168     &self,
169     context: &Data<LemmyContext>,
170     _websocket_id: Option<ConnectionId>,
171   ) -> Result<ListPostReportsResponse, LemmyError> {
172     let data: &ListPostReports = self;
173     let local_user_view =
174       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
175
176     let person_id = local_user_view.person.id;
177     let admin = local_user_view.person.admin;
178     let community_id = data.community_id;
179     let unresolved_only = data.unresolved_only;
180
181     let page = data.page;
182     let limit = data.limit;
183     let post_reports = blocking(context.pool(), move |conn| {
184       PostReportQueryBuilder::create(conn, person_id, admin)
185         .community_id(community_id)
186         .unresolved_only(unresolved_only)
187         .page(page)
188         .limit(limit)
189         .list()
190     })
191     .await??;
192
193     let res = ListPostReportsResponse { post_reports };
194
195     Ok(res)
196   }
197 }