]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
Consolidate reqwest clients, use reqwest-middleware for tracing
[lemmy.git] / crates / apub / src / activities / deletion / delete.rs
1 use crate::{
2   activities::{
3     community::{announce::GetCommunity, send_activity_in_community},
4     deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
5     generate_activity_id,
6     verify_activity,
7     verify_is_public,
8   },
9   activity_lists::AnnouncableActivities,
10   objects::{community::ApubCommunity, person::ApubPerson},
11   protocol::activities::deletion::delete::Delete,
12 };
13 use activitystreams_kinds::{activity::DeleteType, public};
14 use lemmy_api_common::blocking;
15 use lemmy_apub_lib::{
16   data::Data,
17   object_id::ObjectId,
18   traits::{ActivityHandler, ActorType},
19 };
20 use lemmy_db_schema::{
21   source::{
22     comment::Comment,
23     community::Community,
24     moderator::{
25       ModRemoveComment,
26       ModRemoveCommentForm,
27       ModRemoveCommunity,
28       ModRemoveCommunityForm,
29       ModRemovePost,
30       ModRemovePostForm,
31     },
32     post::Post,
33   },
34   traits::Crud,
35 };
36 use lemmy_utils::LemmyError;
37 use lemmy_websocket::{
38   send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
39   LemmyContext,
40   UserOperationCrud,
41 };
42 use url::Url;
43
44 #[async_trait::async_trait(?Send)]
45 impl ActivityHandler for Delete {
46   type DataType = LemmyContext;
47
48   #[tracing::instrument(skip_all)]
49   async fn verify(
50     &self,
51     context: &Data<LemmyContext>,
52     request_counter: &mut i32,
53   ) -> Result<(), LemmyError> {
54     verify_is_public(&self.to, &[])?;
55     verify_activity(&self.id, self.actor.inner(), &context.settings())?;
56     let community = self.get_community(context, request_counter).await?;
57     verify_delete_activity(
58       &self.object.id,
59       &self.actor,
60       &community,
61       self.summary.is_some(),
62       context,
63       request_counter,
64     )
65     .await?;
66     Ok(())
67   }
68
69   #[tracing::instrument(skip_all)]
70   async fn receive(
71     self,
72     context: &Data<LemmyContext>,
73     request_counter: &mut i32,
74   ) -> Result<(), LemmyError> {
75     if let Some(reason) = self.summary {
76       // We set reason to empty string if it doesn't exist, to distinguish between delete and
77       // remove. Here we change it back to option, so we don't write it to db.
78       let reason = if reason.is_empty() {
79         None
80       } else {
81         Some(reason)
82       };
83       receive_remove_action(
84         &self.actor,
85         &self.object.id,
86         reason,
87         context,
88         request_counter,
89       )
90       .await
91     } else {
92       receive_delete_action(&self.object.id, &self.actor, true, context, request_counter).await
93     }
94   }
95 }
96
97 impl Delete {
98   pub(in crate::activities::deletion) fn new(
99     actor: &ApubPerson,
100     object: DeletableObjects,
101     summary: Option<String>,
102     context: &LemmyContext,
103   ) -> Result<Delete, LemmyError> {
104     Ok(Delete {
105       actor: ObjectId::new(actor.actor_id()),
106       to: vec![public()],
107       object: object.to_tombstone()?,
108       kind: DeleteType::Delete,
109       summary,
110       id: generate_activity_id(
111         DeleteType::Delete,
112         &context.settings().get_protocol_and_hostname(),
113       )?,
114       unparsed: Default::default(),
115     })
116   }
117
118   #[tracing::instrument(skip_all)]
119   pub(in crate::activities::deletion) async fn send(
120     actor: &ApubPerson,
121     community: &ApubCommunity,
122     object: DeletableObjects,
123     summary: Option<String>,
124     context: &LemmyContext,
125   ) -> Result<(), LemmyError> {
126     let delete = Delete::new(actor, object, summary, context)?;
127     let delete_id = delete.id.clone();
128
129     let activity = AnnouncableActivities::Delete(delete);
130     send_activity_in_community(activity, &delete_id, actor, community, vec![], context).await
131   }
132 }
133
134 #[tracing::instrument(skip_all)]
135 pub(in crate::activities) async fn receive_remove_action(
136   actor: &ObjectId<ApubPerson>,
137   object: &Url,
138   reason: Option<String>,
139   context: &LemmyContext,
140   request_counter: &mut i32,
141 ) -> Result<(), LemmyError> {
142   let actor = actor
143     .dereference(context, context.client(), request_counter)
144     .await?;
145   use UserOperationCrud::*;
146   match DeletableObjects::read_from_db(object, context).await? {
147     DeletableObjects::Community(community) => {
148       if community.local {
149         return Err(LemmyError::from_message(
150           "Only local admin can remove community",
151         ));
152       }
153       let form = ModRemoveCommunityForm {
154         mod_person_id: actor.id,
155         community_id: community.id,
156         removed: Some(true),
157         reason,
158         expires: None,
159       };
160       blocking(context.pool(), move |conn| {
161         ModRemoveCommunity::create(conn, &form)
162       })
163       .await??;
164       let deleted_community = blocking(context.pool(), move |conn| {
165         Community::update_removed(conn, community.id, true)
166       })
167       .await??;
168
169       send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?;
170     }
171     DeletableObjects::Post(post) => {
172       let form = ModRemovePostForm {
173         mod_person_id: actor.id,
174         post_id: post.id,
175         removed: Some(true),
176         reason,
177       };
178       blocking(context.pool(), move |conn| {
179         ModRemovePost::create(conn, &form)
180       })
181       .await??;
182       let removed_post = blocking(context.pool(), move |conn| {
183         Post::update_removed(conn, post.id, true)
184       })
185       .await??;
186
187       send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?;
188     }
189     DeletableObjects::Comment(comment) => {
190       let form = ModRemoveCommentForm {
191         mod_person_id: actor.id,
192         comment_id: comment.id,
193         removed: Some(true),
194         reason,
195       };
196       blocking(context.pool(), move |conn| {
197         ModRemoveComment::create(conn, &form)
198       })
199       .await??;
200       let removed_comment = blocking(context.pool(), move |conn| {
201         Comment::update_removed(conn, comment.id, true)
202       })
203       .await??;
204
205       send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
206     }
207   }
208   Ok(())
209 }
210
211 #[async_trait::async_trait(?Send)]
212 impl GetCommunity for Delete {
213   #[tracing::instrument(skip_all)]
214   async fn get_community(
215     &self,
216     context: &LemmyContext,
217     _request_counter: &mut i32,
218   ) -> Result<ApubCommunity, LemmyError> {
219     let community_id = match DeletableObjects::read_from_db(&self.object.id, context).await? {
220       DeletableObjects::Community(c) => c.id,
221       DeletableObjects::Comment(c) => {
222         let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
223         post.community_id
224       }
225       DeletableObjects::Post(p) => p.community_id,
226     };
227     let community = blocking(context.pool(), move |conn| {
228       Community::read(conn, community_id)
229     })
230     .await??;
231     Ok(community.into())
232   }
233 }