]> Untitled Git - lemmy.git/blob - server/src/apub/inbox/activities/undo.rs
Various adjustments after review
[lemmy.git] / server / src / apub / inbox / activities / undo.rs
1 use crate::{
2   api::{comment::CommentResponse, community::CommunityResponse, post::PostResponse},
3   apub::{
4     fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
5     inbox::shared_inbox::{
6       announce_if_community_is_local,
7       get_user_from_activity,
8       receive_unhandled_activity,
9     },
10     ActorType,
11     FromApub,
12     GroupExt,
13     PageExt,
14   },
15   blocking,
16   routes::ChatServerParam,
17   websocket::{
18     server::{SendComment, SendCommunityRoomMessage, SendPost},
19     UserOperation,
20   },
21   DbPool,
22   LemmyError,
23 };
24 use activitystreams::{
25   activity::*,
26   base::{AnyBase, AsBase},
27   object::Note,
28   prelude::*,
29 };
30 use actix_web::{client::Client, HttpResponse};
31 use anyhow::anyhow;
32 use lemmy_db::{
33   comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
34   comment_view::CommentView,
35   community::{Community, CommunityForm},
36   community_view::CommunityView,
37   naive_now,
38   post::{Post, PostForm, PostLike, PostLikeForm},
39   post_view::PostView,
40   Crud,
41   Likeable,
42 };
43
44 pub async fn receive_undo(
45   activity: AnyBase,
46   client: &Client,
47   pool: &DbPool,
48   chat_server: ChatServerParam,
49 ) -> Result<HttpResponse, LemmyError> {
50   let undo = Undo::from_any_base(activity)?.unwrap();
51   match undo.object().as_single_kind_str() {
52     Some("Delete") => receive_undo_delete(undo, client, pool, chat_server).await,
53     Some("Remove") => receive_undo_remove(undo, client, pool, chat_server).await,
54     Some("Like") => receive_undo_like(undo, client, pool, chat_server).await,
55     Some("Dislike") => receive_undo_dislike(undo, client, pool, chat_server).await,
56     _ => receive_unhandled_activity(undo),
57   }
58 }
59
60 fn check_is_undo_valid<T, A>(outer_activity: &Undo, inner_activity: &T) -> Result<(), LemmyError>
61 where
62   T: AsBase<A> + ActorAndObjectRef,
63 {
64   let outer_actor = outer_activity.actor()?;
65   let outer_actor_uri = outer_actor.as_single_xsd_any_uri().unwrap();
66
67   let inner_actor = inner_activity.actor()?;
68   let inner_actor_uri = inner_actor.as_single_xsd_any_uri().unwrap();
69
70   if outer_actor_uri.domain() != inner_actor_uri.domain() {
71     Err(anyhow!("Cant undo activities from a different instance").into())
72   } else {
73     Ok(())
74   }
75 }
76
77 async fn receive_undo_delete(
78   undo: Undo,
79   client: &Client,
80   pool: &DbPool,
81   chat_server: ChatServerParam,
82 ) -> Result<HttpResponse, LemmyError> {
83   let delete = Delete::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
84   check_is_undo_valid(&undo, &delete)?;
85   let type_ = delete.object().as_single_kind_str().unwrap();
86   match type_ {
87     "Note" => receive_undo_delete_comment(undo, &delete, client, pool, chat_server).await,
88     "Page" => receive_undo_delete_post(undo, &delete, client, pool, chat_server).await,
89     "Group" => receive_undo_delete_community(undo, &delete, client, pool, chat_server).await,
90     d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
91   }
92 }
93
94 async fn receive_undo_remove(
95   undo: Undo,
96   client: &Client,
97   pool: &DbPool,
98   chat_server: ChatServerParam,
99 ) -> Result<HttpResponse, LemmyError> {
100   let remove = Remove::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
101   check_is_undo_valid(&undo, &remove)?;
102
103   let type_ = remove.object().as_single_kind_str().unwrap();
104   match type_ {
105     "Note" => receive_undo_remove_comment(undo, &remove, client, pool, chat_server).await,
106     "Page" => receive_undo_remove_post(undo, &remove, client, pool, chat_server).await,
107     "Group" => receive_undo_remove_community(undo, &remove, client, pool, chat_server).await,
108     d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
109   }
110 }
111
112 async fn receive_undo_like(
113   undo: Undo,
114   client: &Client,
115   pool: &DbPool,
116   chat_server: ChatServerParam,
117 ) -> Result<HttpResponse, LemmyError> {
118   let like = Like::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
119   check_is_undo_valid(&undo, &like)?;
120
121   let type_ = like.object().as_single_kind_str().unwrap();
122   match type_ {
123     "Note" => receive_undo_like_comment(undo, &like, client, pool, chat_server).await,
124     "Page" => receive_undo_like_post(undo, &like, client, pool, chat_server).await,
125     d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
126   }
127 }
128
129 async fn receive_undo_dislike(
130   undo: Undo,
131   _client: &Client,
132   _pool: &DbPool,
133   _chat_server: ChatServerParam,
134 ) -> Result<HttpResponse, LemmyError> {
135   let dislike = Dislike::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
136   check_is_undo_valid(&undo, &dislike)?;
137
138   // TODO: need to implement Undo<Dislike>
139
140   let type_ = dislike.object().as_single_kind_str().unwrap();
141   Err(anyhow!("Undo Delete type {} not supported", type_).into())
142 }
143
144 async fn receive_undo_delete_comment(
145   undo: Undo,
146   delete: &Delete,
147   client: &Client,
148   pool: &DbPool,
149   chat_server: ChatServerParam,
150 ) -> Result<HttpResponse, LemmyError> {
151   let user = get_user_from_activity(delete, client, pool).await?;
152   let note = Note::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
153
154   let comment_ap_id = CommentForm::from_apub(&note, client, pool, Some(user.actor_id()?))
155     .await?
156     .get_ap_id()?;
157
158   let comment = get_or_fetch_and_insert_comment(&comment_ap_id, client, pool).await?;
159
160   let comment_form = CommentForm {
161     content: comment.content.to_owned(),
162     parent_id: comment.parent_id,
163     post_id: comment.post_id,
164     creator_id: comment.creator_id,
165     removed: None,
166     deleted: Some(false),
167     read: None,
168     published: None,
169     updated: Some(naive_now()),
170     ap_id: comment.ap_id,
171     local: comment.local,
172   };
173   let comment_id = comment.id;
174   blocking(pool, move |conn| {
175     Comment::update(conn, comment_id, &comment_form)
176   })
177   .await??;
178
179   // Refetch the view
180   let comment_id = comment.id;
181   let comment_view =
182     blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
183
184   // TODO get those recipient actor ids from somewhere
185   let recipient_ids = vec![];
186   let res = CommentResponse {
187     comment: comment_view,
188     recipient_ids,
189     form_id: None,
190   };
191
192   chat_server.do_send(SendComment {
193     op: UserOperation::EditComment,
194     comment: res,
195     my_id: None,
196   });
197
198   announce_if_community_is_local(undo, &user, client, pool).await?;
199   Ok(HttpResponse::Ok().finish())
200 }
201
202 async fn receive_undo_remove_comment(
203   undo: Undo,
204   remove: &Remove,
205   client: &Client,
206   pool: &DbPool,
207   chat_server: ChatServerParam,
208 ) -> Result<HttpResponse, LemmyError> {
209   let mod_ = get_user_from_activity(remove, client, pool).await?;
210   let note = Note::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
211
212   let comment_ap_id = CommentForm::from_apub(&note, client, pool, Some(mod_.actor_id()?))
213     .await?
214     .get_ap_id()?;
215
216   let comment = get_or_fetch_and_insert_comment(&comment_ap_id, client, pool).await?;
217
218   let comment_form = CommentForm {
219     content: comment.content.to_owned(),
220     parent_id: comment.parent_id,
221     post_id: comment.post_id,
222     creator_id: comment.creator_id,
223     removed: Some(false),
224     deleted: None,
225     read: None,
226     published: None,
227     updated: Some(naive_now()),
228     ap_id: comment.ap_id,
229     local: comment.local,
230   };
231   let comment_id = comment.id;
232   blocking(pool, move |conn| {
233     Comment::update(conn, comment_id, &comment_form)
234   })
235   .await??;
236
237   // Refetch the view
238   let comment_id = comment.id;
239   let comment_view =
240     blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
241
242   // TODO get those recipient actor ids from somewhere
243   let recipient_ids = vec![];
244   let res = CommentResponse {
245     comment: comment_view,
246     recipient_ids,
247     form_id: None,
248   };
249
250   chat_server.do_send(SendComment {
251     op: UserOperation::EditComment,
252     comment: res,
253     my_id: None,
254   });
255
256   announce_if_community_is_local(undo, &mod_, client, pool).await?;
257   Ok(HttpResponse::Ok().finish())
258 }
259
260 async fn receive_undo_delete_post(
261   undo: Undo,
262   delete: &Delete,
263   client: &Client,
264   pool: &DbPool,
265   chat_server: ChatServerParam,
266 ) -> Result<HttpResponse, LemmyError> {
267   let user = get_user_from_activity(delete, client, pool).await?;
268   let page = PageExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
269
270   let post_ap_id = PostForm::from_apub(&page, client, pool, Some(user.actor_id()?))
271     .await?
272     .get_ap_id()?;
273
274   let post = get_or_fetch_and_insert_post(&post_ap_id, client, pool).await?;
275
276   let post_form = PostForm {
277     name: post.name.to_owned(),
278     url: post.url.to_owned(),
279     body: post.body.to_owned(),
280     creator_id: post.creator_id.to_owned(),
281     community_id: post.community_id,
282     removed: None,
283     deleted: Some(false),
284     nsfw: post.nsfw,
285     locked: None,
286     stickied: None,
287     updated: Some(naive_now()),
288     embed_title: post.embed_title,
289     embed_description: post.embed_description,
290     embed_html: post.embed_html,
291     thumbnail_url: post.thumbnail_url,
292     ap_id: post.ap_id,
293     local: post.local,
294     published: None,
295   };
296   let post_id = post.id;
297   blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
298
299   // Refetch the view
300   let post_id = post.id;
301   let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
302
303   let res = PostResponse { post: post_view };
304
305   chat_server.do_send(SendPost {
306     op: UserOperation::EditPost,
307     post: res,
308     my_id: None,
309   });
310
311   announce_if_community_is_local(undo, &user, client, pool).await?;
312   Ok(HttpResponse::Ok().finish())
313 }
314
315 async fn receive_undo_remove_post(
316   undo: Undo,
317   remove: &Remove,
318   client: &Client,
319   pool: &DbPool,
320   chat_server: ChatServerParam,
321 ) -> Result<HttpResponse, LemmyError> {
322   let mod_ = get_user_from_activity(remove, client, pool).await?;
323   let page = PageExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
324
325   let post_ap_id = PostForm::from_apub(&page, client, pool, Some(mod_.actor_id()?))
326     .await?
327     .get_ap_id()?;
328
329   let post = get_or_fetch_and_insert_post(&post_ap_id, client, pool).await?;
330
331   let post_form = PostForm {
332     name: post.name.to_owned(),
333     url: post.url.to_owned(),
334     body: post.body.to_owned(),
335     creator_id: post.creator_id.to_owned(),
336     community_id: post.community_id,
337     removed: Some(false),
338     deleted: None,
339     nsfw: post.nsfw,
340     locked: None,
341     stickied: None,
342     updated: Some(naive_now()),
343     embed_title: post.embed_title,
344     embed_description: post.embed_description,
345     embed_html: post.embed_html,
346     thumbnail_url: post.thumbnail_url,
347     ap_id: post.ap_id,
348     local: post.local,
349     published: None,
350   };
351   let post_id = post.id;
352   blocking(pool, move |conn| Post::update(conn, post_id, &post_form)).await??;
353
354   // Refetch the view
355   let post_id = post.id;
356   let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
357
358   let res = PostResponse { post: post_view };
359
360   chat_server.do_send(SendPost {
361     op: UserOperation::EditPost,
362     post: res,
363     my_id: None,
364   });
365
366   announce_if_community_is_local(undo, &mod_, client, pool).await?;
367   Ok(HttpResponse::Ok().finish())
368 }
369
370 async fn receive_undo_delete_community(
371   undo: Undo,
372   delete: &Delete,
373   client: &Client,
374   pool: &DbPool,
375   chat_server: ChatServerParam,
376 ) -> Result<HttpResponse, LemmyError> {
377   let user = get_user_from_activity(delete, client, pool).await?;
378   let group = GroupExt::from_any_base(delete.object().to_owned().one().unwrap())?.unwrap();
379
380   let community_actor_id = CommunityForm::from_apub(&group, client, pool, Some(user.actor_id()?))
381     .await?
382     .actor_id;
383
384   let community = blocking(pool, move |conn| {
385     Community::read_from_actor_id(conn, &community_actor_id)
386   })
387   .await??;
388
389   let community_form = CommunityForm {
390     name: community.name.to_owned(),
391     title: community.title.to_owned(),
392     description: community.description.to_owned(),
393     category_id: community.category_id, // Note: need to keep this due to foreign key constraint
394     creator_id: community.creator_id,   // Note: need to keep this due to foreign key constraint
395     removed: None,
396     published: None,
397     updated: Some(naive_now()),
398     deleted: Some(false),
399     nsfw: community.nsfw,
400     actor_id: community.actor_id,
401     local: community.local,
402     private_key: community.private_key,
403     public_key: community.public_key,
404     last_refreshed_at: None,
405   };
406
407   let community_id = community.id;
408   blocking(pool, move |conn| {
409     Community::update(conn, community_id, &community_form)
410   })
411   .await??;
412
413   let community_id = community.id;
414   let res = CommunityResponse {
415     community: blocking(pool, move |conn| {
416       CommunityView::read(conn, community_id, None)
417     })
418     .await??,
419   };
420
421   let community_id = res.community.id;
422
423   chat_server.do_send(SendCommunityRoomMessage {
424     op: UserOperation::EditCommunity,
425     response: res,
426     community_id,
427     my_id: None,
428   });
429
430   announce_if_community_is_local(undo, &user, client, pool).await?;
431   Ok(HttpResponse::Ok().finish())
432 }
433
434 async fn receive_undo_remove_community(
435   undo: Undo,
436   remove: &Remove,
437   client: &Client,
438   pool: &DbPool,
439   chat_server: ChatServerParam,
440 ) -> Result<HttpResponse, LemmyError> {
441   let mod_ = get_user_from_activity(remove, client, pool).await?;
442   let group = GroupExt::from_any_base(remove.object().to_owned().one().unwrap())?.unwrap();
443
444   let community_actor_id = CommunityForm::from_apub(&group, client, pool, Some(mod_.actor_id()?))
445     .await?
446     .actor_id;
447
448   let community = blocking(pool, move |conn| {
449     Community::read_from_actor_id(conn, &community_actor_id)
450   })
451   .await??;
452
453   let community_form = CommunityForm {
454     name: community.name.to_owned(),
455     title: community.title.to_owned(),
456     description: community.description.to_owned(),
457     category_id: community.category_id, // Note: need to keep this due to foreign key constraint
458     creator_id: community.creator_id,   // Note: need to keep this due to foreign key constraint
459     removed: Some(false),
460     published: None,
461     updated: Some(naive_now()),
462     deleted: None,
463     nsfw: community.nsfw,
464     actor_id: community.actor_id,
465     local: community.local,
466     private_key: community.private_key,
467     public_key: community.public_key,
468     last_refreshed_at: None,
469   };
470
471   let community_id = community.id;
472   blocking(pool, move |conn| {
473     Community::update(conn, community_id, &community_form)
474   })
475   .await??;
476
477   let community_id = community.id;
478   let res = CommunityResponse {
479     community: blocking(pool, move |conn| {
480       CommunityView::read(conn, community_id, None)
481     })
482     .await??,
483   };
484
485   let community_id = res.community.id;
486
487   chat_server.do_send(SendCommunityRoomMessage {
488     op: UserOperation::EditCommunity,
489     response: res,
490     community_id,
491     my_id: None,
492   });
493
494   announce_if_community_is_local(undo, &mod_, client, pool).await?;
495   Ok(HttpResponse::Ok().finish())
496 }
497
498 async fn receive_undo_like_comment(
499   undo: Undo,
500   like: &Like,
501   client: &Client,
502   pool: &DbPool,
503   chat_server: ChatServerParam,
504 ) -> Result<HttpResponse, LemmyError> {
505   let user = get_user_from_activity(like, client, pool).await?;
506   let note = Note::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
507
508   let comment = CommentForm::from_apub(&note, client, pool, None).await?;
509
510   let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, client, pool)
511     .await?
512     .id;
513
514   let like_form = CommentLikeForm {
515     comment_id,
516     post_id: comment.post_id,
517     user_id: user.id,
518     score: 0,
519   };
520   blocking(pool, move |conn| CommentLike::remove(conn, &like_form)).await??;
521
522   // Refetch the view
523   let comment_view =
524     blocking(pool, move |conn| CommentView::read(conn, comment_id, None)).await??;
525
526   // TODO get those recipient actor ids from somewhere
527   let recipient_ids = vec![];
528   let res = CommentResponse {
529     comment: comment_view,
530     recipient_ids,
531     form_id: None,
532   };
533
534   chat_server.do_send(SendComment {
535     op: UserOperation::CreateCommentLike,
536     comment: res,
537     my_id: None,
538   });
539
540   announce_if_community_is_local(undo, &user, client, pool).await?;
541   Ok(HttpResponse::Ok().finish())
542 }
543
544 async fn receive_undo_like_post(
545   undo: Undo,
546   like: &Like,
547   client: &Client,
548   pool: &DbPool,
549   chat_server: ChatServerParam,
550 ) -> Result<HttpResponse, LemmyError> {
551   let user = get_user_from_activity(like, client, pool).await?;
552   let page = PageExt::from_any_base(like.object().to_owned().one().unwrap())?.unwrap();
553
554   let post = PostForm::from_apub(&page, client, pool, None).await?;
555
556   let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, client, pool)
557     .await?
558     .id;
559
560   let like_form = PostLikeForm {
561     post_id,
562     user_id: user.id,
563     score: 1,
564   };
565   blocking(pool, move |conn| PostLike::remove(conn, &like_form)).await??;
566
567   // Refetch the view
568   let post_view = blocking(pool, move |conn| PostView::read(conn, post_id, None)).await??;
569
570   let res = PostResponse { post: post_view };
571
572   chat_server.do_send(SendPost {
573     op: UserOperation::CreatePostLike,
574     post: res,
575     my_id: None,
576   });
577
578   announce_if_community_is_local(undo, &user, client, pool).await?;
579   Ok(HttpResponse::Ok().finish())
580 }