]> Untitled Git - lemmy.git/blob - crates/api/src/post_report.rs
02afc7957ca4af4875cacee6ebc2fa39288709c0
[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(LemmyError::from)
76     .map_err(|e| e.with_message("couldnt_create_report"))?;
77
78     let post_report_view = blocking(context.pool(), move |conn| {
79       PostReportView::read(conn, report.id, person_id)
80     })
81     .await??;
82
83     let res = PostReportResponse { post_report_view };
84
85     context.chat_server().do_send(SendModRoomMessage {
86       op: UserOperation::CreatePostReport,
87       response: res.clone(),
88       community_id: post_view.community.id,
89       websocket_id,
90     });
91
92     Report::send(
93       ObjectId::new(post_view.post.ap_id),
94       &local_user_view.person.into(),
95       ObjectId::new(post_view.community.actor_id),
96       reason.to_string(),
97       context,
98     )
99     .await?;
100
101     Ok(res)
102   }
103 }
104
105 /// Resolves or unresolves a post report and notifies the moderators of the community
106 #[async_trait::async_trait(?Send)]
107 impl Perform for ResolvePostReport {
108   type Response = PostReportResponse;
109
110   #[tracing::instrument(skip(context, websocket_id))]
111   async fn perform(
112     &self,
113     context: &Data<LemmyContext>,
114     websocket_id: Option<ConnectionId>,
115   ) -> Result<PostReportResponse, LemmyError> {
116     let data: &ResolvePostReport = self;
117     let local_user_view =
118       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
119
120     let report_id = data.report_id;
121     let person_id = local_user_view.person.id;
122     let report = blocking(context.pool(), move |conn| {
123       PostReportView::read(conn, report_id, person_id)
124     })
125     .await??;
126
127     let person_id = local_user_view.person.id;
128     is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
129
130     let resolved = data.resolved;
131     let resolve_fun = move |conn: &'_ _| {
132       if resolved {
133         PostReport::resolve(conn, report_id, person_id)
134       } else {
135         PostReport::unresolve(conn, report_id, person_id)
136       }
137     };
138
139     blocking(context.pool(), resolve_fun)
140       .await?
141       .map_err(LemmyError::from)
142       .map_err(|e| e.with_message("couldnt_resolve_report"))?;
143
144     let post_report_view = blocking(context.pool(), move |conn| {
145       PostReportView::read(conn, report_id, person_id)
146     })
147     .await??;
148
149     let res = PostReportResponse { post_report_view };
150
151     context.chat_server().do_send(SendModRoomMessage {
152       op: UserOperation::ResolvePostReport,
153       response: res.clone(),
154       community_id: report.community.id,
155       websocket_id,
156     });
157
158     Ok(res)
159   }
160 }
161
162 /// Lists post reports for a community if an id is supplied
163 /// or returns all post reports for communities a user moderates
164 #[async_trait::async_trait(?Send)]
165 impl Perform for ListPostReports {
166   type Response = ListPostReportsResponse;
167
168   #[tracing::instrument(skip(context, _websocket_id))]
169   async fn perform(
170     &self,
171     context: &Data<LemmyContext>,
172     _websocket_id: Option<ConnectionId>,
173   ) -> Result<ListPostReportsResponse, LemmyError> {
174     let data: &ListPostReports = self;
175     let local_user_view =
176       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
177
178     let person_id = local_user_view.person.id;
179     let admin = local_user_view.person.admin;
180     let community_id = data.community_id;
181     let unresolved_only = data.unresolved_only;
182
183     let page = data.page;
184     let limit = data.limit;
185     let post_reports = blocking(context.pool(), move |conn| {
186       PostReportQueryBuilder::create(conn, person_id, admin)
187         .community_id(community_id)
188         .unresolved_only(unresolved_only)
189         .page(page)
190         .limit(limit)
191         .list()
192     })
193     .await??;
194
195     let res = ListPostReportsResponse { post_reports };
196
197     Ok(res)
198   }
199 }