Apub local object handling (#1297)
authorNutomic <me@nutomic.com>
Tue, 8 Dec 2020 17:38:48 +0000 (17:38 +0000)
committerGitHub <noreply@github.com>
Tue, 8 Dec 2020 17:38:48 +0000 (12:38 -0500)
* Limit visibility of some traits and methods

* WIP: alternative way to handle non-local object parsing

* finish this

* cleanup

* Move check for locked post into Comment::from_apub()

* Mark user as updated after fetching

* Should set last_refreshed_at, not updated

* Add ApubObject trait in DB, with method read_from_apub_id()

* Create shared, generic implementation for `FromApub`, prefer local data

* Check for community ban when parsing post/comment (fixes #1287)

* Fix tests (changes in get_object_from_apub() prevented `Update` from working)

* Support parsing `like.object` either as URL or object

* Send out like.object as URL, instead of full object (fixes #1283)

* add todo

37 files changed:
docs/src/contributing_apub_api_outline.md
lemmy_api/src/community.rs
lemmy_apub/src/activities/receive/comment.rs
lemmy_apub/src/activities/receive/comment_undo.rs
lemmy_apub/src/activities/receive/community.rs
lemmy_apub/src/activities/receive/post.rs
lemmy_apub/src/activities/receive/post_undo.rs
lemmy_apub/src/activities/receive/private_message.rs
lemmy_apub/src/activities/send/comment.rs
lemmy_apub/src/activities/send/post.rs
lemmy_apub/src/activities/send/private_message.rs
lemmy_apub/src/activities/send/user.rs
lemmy_apub/src/activity_queue.rs
lemmy_apub/src/extensions/signatures.rs
lemmy_apub/src/fetcher.rs
lemmy_apub/src/http/comment.rs
lemmy_apub/src/http/community.rs
lemmy_apub/src/http/post.rs
lemmy_apub/src/http/user.rs
lemmy_apub/src/inbox/community_inbox.rs
lemmy_apub/src/inbox/mod.rs
lemmy_apub/src/inbox/receive_for_community.rs
lemmy_apub/src/inbox/shared_inbox.rs
lemmy_apub/src/inbox/user_inbox.rs
lemmy_apub/src/lib.rs
lemmy_apub/src/objects/comment.rs
lemmy_apub/src/objects/community.rs
lemmy_apub/src/objects/mod.rs
lemmy_apub/src/objects/post.rs
lemmy_apub/src/objects/private_message.rs
lemmy_apub/src/objects/user.rs
lemmy_db/src/comment.rs
lemmy_db/src/community.rs
lemmy_db/src/lib.rs
lemmy_db/src/post.rs
lemmy_db/src/private_message.rs
lemmy_db/src/user.rs

index 92cdf9c0e7fff32fc85e3674b946287aa955e620..74280bd5f9afcaef1c9c245304ab50064ae52d8c 100644 (file)
@@ -438,7 +438,7 @@ Sent to: Community
     "cc": [
         "https://ds9.lemmy.ml/c/main/"
     ],
-    "object": ...
+    "object": "https://enterprise.lemmy.ml/p/123"
 }
 ```
 
@@ -465,7 +465,7 @@ Sent to: Community
     "cc": [
       "https://ds9.lemmy.ml/c/main/"
     ],
-    "object": ...
+    "object": "https://enterprise.lemmy.ml/p/123"
 }
 ```
 
index 76242020256e286d211ed180b70ee95fcf2ae4fb..d7de0e6bd717a3bab6ad626599c846c08e5dfe26 100644 (file)
@@ -20,6 +20,7 @@ use lemmy_db::{
   post::Post,
   site::*,
   user_view::*,
+  ApubObject,
   Bannable,
   Crud,
   Followable,
@@ -129,7 +130,7 @@ impl Perform for CreateCommunity {
     let actor_id = make_apub_endpoint(EndpointType::Community, &data.name).to_string();
     let actor_id_cloned = actor_id.to_owned();
     let community_dupe = blocking(context.pool(), move |conn| {
-      Community::read_from_actor_id(conn, &actor_id_cloned)
+      Community::read_from_apub_id(conn, &actor_id_cloned)
     })
     .await?;
     if community_dupe.is_ok() {
index bf8de1ebd82f84acbcdf55983ba7d68f772fd7db..ff0fb8199f96bd8ab436f088462dee2de9154084 100644 (file)
@@ -1,20 +1,13 @@
-use crate::{
-  activities::receive::get_actor_as_user,
-  fetcher::get_or_fetch_and_insert_comment,
-  ActorType,
-  FromApub,
-  NoteExt,
-};
+use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, NoteExt};
 use activitystreams::{
   activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update},
   base::ExtendsExt,
 };
-use anyhow::{anyhow, Context};
+use anyhow::Context;
 use lemmy_db::{
-  comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
+  comment::{Comment, CommentLike, CommentLikeForm},
   comment_view::CommentView,
   post::Post,
-  Crud,
   Likeable,
 };
 use lemmy_structs::{blocking, comment::CommentResponse, send_local_notifs};
@@ -30,36 +23,22 @@ pub(crate) async fn receive_create_comment(
   let note = NoteExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
     .context(location_info!())?;
 
-  let comment =
-    CommentForm::from_apub(&note, context, Some(user.actor_id()?), request_counter).await?;
+  let comment = Comment::from_apub(&note, context, user.actor_id()?, request_counter).await?;
 
   let post_id = comment.post_id;
   let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-  if post.locked {
-    return Err(anyhow!("Post is locked").into());
-  }
-
-  let inserted_comment =
-    blocking(context.pool(), move |conn| Comment::upsert(conn, &comment)).await??;
 
   // Note:
   // Although mentions could be gotten from the post tags (they are included there), or the ccs,
   // Its much easier to scrape them from the comment body, since the API has to do that
   // anyway.
-  let mentions = scrape_text_for_mentions(&inserted_comment.content);
-  let recipient_ids = send_local_notifs(
-    mentions,
-    inserted_comment.clone(),
-    &user,
-    post,
-    context.pool(),
-    true,
-  )
-  .await?;
+  let mentions = scrape_text_for_mentions(&comment.content);
+  let recipient_ids =
+    send_local_notifs(mentions, comment.clone(), &user, post, context.pool(), true).await?;
 
   // Refetch the view
   let comment_view = blocking(context.pool(), move |conn| {
-    CommentView::read(conn, inserted_comment.id, None)
+    CommentView::read(conn, comment.id, None)
   })
   .await??;
 
@@ -87,36 +66,19 @@ pub(crate) async fn receive_update_comment(
     .context(location_info!())?;
   let user = get_actor_as_user(&update, context, request_counter).await?;
 
-  let comment =
-    CommentForm::from_apub(&note, context, Some(user.actor_id()?), request_counter).await?;
-
-  let original_comment_id =
-    get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
-      .await?
-      .id;
-
-  let updated_comment = blocking(context.pool(), move |conn| {
-    Comment::update(conn, original_comment_id, &comment)
-  })
-  .await??;
+  let comment = Comment::from_apub(&note, context, user.actor_id()?, request_counter).await?;
 
-  let post_id = updated_comment.post_id;
+  let comment_id = comment.id;
+  let post_id = comment.post_id;
   let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
 
-  let mentions = scrape_text_for_mentions(&updated_comment.content);
-  let recipient_ids = send_local_notifs(
-    mentions,
-    updated_comment,
-    &user,
-    post,
-    context.pool(),
-    false,
-  )
-  .await?;
+  let mentions = scrape_text_for_mentions(&comment.content);
+  let recipient_ids =
+    send_local_notifs(mentions, comment, &user, post, context.pool(), false).await?;
 
   // Refetch the view
   let comment_view = blocking(context.pool(), move |conn| {
-    CommentView::read(conn, original_comment_id, None)
+    CommentView::read(conn, comment_id, None)
   })
   .await??;
 
@@ -137,19 +99,13 @@ pub(crate) async fn receive_update_comment(
 
 pub(crate) async fn receive_like_comment(
   like: Like,
+  comment: Comment,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
-  let note = NoteExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
-    .context(location_info!())?;
   let user = get_actor_as_user(&like, context, request_counter).await?;
 
-  let comment = CommentForm::from_apub(&note, context, None, request_counter).await?;
-
-  let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
-
+  let comment_id = comment.id;
   let like_form = CommentLikeForm {
     comment_id,
     post_id: comment.post_id,
@@ -188,25 +144,13 @@ pub(crate) async fn receive_like_comment(
 
 pub(crate) async fn receive_dislike_comment(
   dislike: Dislike,
+  comment: Comment,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
-  let note = NoteExt::from_any_base(
-    dislike
-      .object()
-      .to_owned()
-      .one()
-      .context(location_info!())?,
-  )?
-  .context(location_info!())?;
   let user = get_actor_as_user(&dislike, context, request_counter).await?;
 
-  let comment = CommentForm::from_apub(&note, context, None, request_counter).await?;
-
-  let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
-
+  let comment_id = comment.id;
   let like_form = CommentLikeForm {
     comment_id,
     post_id: comment.post_id,
index f44604cc1e100d9a6344316be14817188e8bec99..2ee8c6ea7bb529cb40131293da8612267a515829 100644 (file)
@@ -1,35 +1,23 @@
-use crate::{
-  activities::receive::get_actor_as_user,
-  fetcher::get_or_fetch_and_insert_comment,
-  FromApub,
-  NoteExt,
-};
-use activitystreams::{activity::*, prelude::*};
-use anyhow::Context;
+use crate::activities::receive::get_actor_as_user;
+use activitystreams::activity::{Dislike, Like};
 use lemmy_db::{
-  comment::{Comment, CommentForm, CommentLike},
+  comment::{Comment, CommentLike},
   comment_view::CommentView,
   Likeable,
 };
 use lemmy_structs::{blocking, comment::CommentResponse};
-use lemmy_utils::{location_info, LemmyError};
+use lemmy_utils::LemmyError;
 use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation};
 
 pub(crate) async fn receive_undo_like_comment(
   like: &Like,
+  comment: Comment,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
   let user = get_actor_as_user(like, context, request_counter).await?;
-  let note = NoteExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
-    .context(location_info!())?;
-
-  let comment = CommentForm::from_apub(&note, context, None, request_counter).await?;
-
-  let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
 
+  let comment_id = comment.id;
   let user_id = user.id;
   blocking(context.pool(), move |conn| {
     CommentLike::remove(conn, user_id, comment_id)
@@ -61,25 +49,13 @@ pub(crate) async fn receive_undo_like_comment(
 
 pub(crate) async fn receive_undo_dislike_comment(
   dislike: &Dislike,
+  comment: Comment,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
   let user = get_actor_as_user(dislike, context, request_counter).await?;
-  let note = NoteExt::from_any_base(
-    dislike
-      .object()
-      .to_owned()
-      .one()
-      .context(location_info!())?,
-  )?
-  .context(location_info!())?;
-
-  let comment = CommentForm::from_apub(&note, context, None, request_counter).await?;
-
-  let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
 
+  let comment_id = comment.id;
   let user_id = user.id;
   blocking(context.pool(), move |conn| {
     CommentLike::remove(conn, user_id, comment_id)
index ed43b33e301c627817206ce440c6f8d99a392a61..b1866283b64a538558f51ab83deb08479378740f 100644 (file)
@@ -4,7 +4,7 @@ use activitystreams::{
   base::{AnyBase, ExtendsExt},
 };
 use anyhow::Context;
-use lemmy_db::{community::Community, community_view::CommunityView};
+use lemmy_db::{community::Community, community_view::CommunityView, ApubObject};
 use lemmy_structs::{blocking, community::CommunityResponse};
 use lemmy_utils::{location_info, LemmyError};
 use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
@@ -53,7 +53,7 @@ pub(crate) async fn receive_remove_community(
     .single_xsd_any_uri()
     .context(location_info!())?;
   let community = blocking(context.pool(), move |conn| {
-    Community::read_from_actor_id(conn, community_uri.as_str())
+    Community::read_from_apub_id(conn, community_uri.as_str())
   })
   .await??;
 
@@ -135,7 +135,7 @@ pub(crate) async fn receive_undo_remove_community(
     .single_xsd_any_uri()
     .context(location_info!())?;
   let community = blocking(context.pool(), move |conn| {
-    Community::read_from_actor_id(conn, community_uri.as_str())
+    Community::read_from_apub_id(conn, community_uri.as_str())
   })
   .await??;
 
index 80044237f65448a56f497cc08a79e5b51c33fccd..0bbf1eaf52b78288709d153a73d2dbfbd1b2b6a1 100644 (file)
@@ -1,19 +1,12 @@
-use crate::{
-  activities::receive::get_actor_as_user,
-  fetcher::get_or_fetch_and_insert_post,
-  ActorType,
-  FromApub,
-  PageExt,
-};
+use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, PageExt};
 use activitystreams::{
   activity::{Create, Dislike, Like, Remove, Update},
   prelude::*,
 };
 use anyhow::Context;
 use lemmy_db::{
-  post::{Post, PostForm, PostLike, PostLikeForm},
+  post::{Post, PostLike, PostLikeForm},
   post_view::PostView,
-  Crud,
   Likeable,
 };
 use lemmy_structs::{blocking, post::PostResponse};
@@ -29,16 +22,12 @@ pub(crate) async fn receive_create_post(
   let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
     .context(location_info!())?;
 
-  let post = PostForm::from_apub(&page, context, Some(user.actor_id()?), request_counter).await?;
-
-  // Using an upsert, since likes (which fetch the post), sometimes come in before the create
-  // resulting in double posts.
-  let inserted_post = blocking(context.pool(), move |conn| Post::upsert(conn, &post)).await??;
+  let post = Post::from_apub(&page, context, user.actor_id()?, request_counter).await?;
 
   // Refetch the view
-  let inserted_post_id = inserted_post.id;
+  let post_id = post.id;
   let post_view = blocking(context.pool(), move |conn| {
-    PostView::read(conn, inserted_post_id, None)
+    PostView::read(conn, post_id, None)
   })
   .await??;
 
@@ -62,20 +51,12 @@ pub(crate) async fn receive_update_post(
   let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
     .context(location_info!())?;
 
-  let post = PostForm::from_apub(&page, context, Some(user.actor_id()?), request_counter).await?;
-
-  let original_post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
-
-  blocking(context.pool(), move |conn| {
-    Post::update(conn, original_post_id, &post)
-  })
-  .await??;
+  let post = Post::from_apub(&page, context, user.actor_id()?, request_counter).await?;
 
+  let post_id = post.id;
   // Refetch the view
   let post_view = blocking(context.pool(), move |conn| {
-    PostView::read(conn, original_post_id, None)
+    PostView::read(conn, post_id, None)
   })
   .await??;
 
@@ -92,19 +73,13 @@ pub(crate) async fn receive_update_post(
 
 pub(crate) async fn receive_like_post(
   like: Like,
+  post: Post,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
   let user = get_actor_as_user(&like, context, request_counter).await?;
-  let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
-    .context(location_info!())?;
-
-  let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
-  let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
 
+  let post_id = post.id;
   let like_form = PostLikeForm {
     post_id,
     user_id: user.id,
@@ -136,25 +111,13 @@ pub(crate) async fn receive_like_post(
 
 pub(crate) async fn receive_dislike_post(
   dislike: Dislike,
+  post: Post,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
   let user = get_actor_as_user(&dislike, context, request_counter).await?;
-  let page = PageExt::from_any_base(
-    dislike
-      .object()
-      .to_owned()
-      .one()
-      .context(location_info!())?,
-  )?
-  .context(location_info!())?;
-
-  let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
-  let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
 
+  let post_id = post.id;
   let like_form = PostLikeForm {
     post_id,
     user_id: user.id,
index 99d0ed1d98b718c8dabbc5f567d47bfd10a76fbb..bcbb7fee9806859c875072295b05037e81d03225 100644 (file)
@@ -1,35 +1,23 @@
-use crate::{
-  activities::receive::get_actor_as_user,
-  fetcher::get_or_fetch_and_insert_post,
-  FromApub,
-  PageExt,
-};
-use activitystreams::{activity::*, prelude::*};
-use anyhow::Context;
+use crate::activities::receive::get_actor_as_user;
+use activitystreams::activity::{Dislike, Like};
 use lemmy_db::{
-  post::{Post, PostForm, PostLike},
+  post::{Post, PostLike},
   post_view::PostView,
   Likeable,
 };
 use lemmy_structs::{blocking, post::PostResponse};
-use lemmy_utils::{location_info, LemmyError};
+use lemmy_utils::LemmyError;
 use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
 
 pub(crate) async fn receive_undo_like_post(
   like: &Like,
+  post: Post,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
   let user = get_actor_as_user(like, context, request_counter).await?;
-  let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
-    .context(location_info!())?;
-
-  let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
-  let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
 
+  let post_id = post.id;
   let user_id = user.id;
   blocking(context.pool(), move |conn| {
     PostLike::remove(conn, user_id, post_id)
@@ -55,25 +43,13 @@ pub(crate) async fn receive_undo_like_post(
 
 pub(crate) async fn receive_undo_dislike_post(
   dislike: &Dislike,
+  post: Post,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
   let user = get_actor_as_user(dislike, context, request_counter).await?;
-  let page = PageExt::from_any_base(
-    dislike
-      .object()
-      .to_owned()
-      .one()
-      .context(location_info!())?,
-  )?
-  .context(location_info!())?;
-
-  let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
-  let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
-    .await?
-    .id;
 
+  let post_id = post.id;
   let user_id = user.id;
   blocking(context.pool(), move |conn| {
     PostLike::remove(conn, user_id, post_id)
index 8f1c95b9d027b1e759a254c9b73d4178db736bf0..25ccc520d1eecfe674d7cf34d141b48630d822eb 100644 (file)
@@ -3,7 +3,7 @@ use crate::{
   check_is_apub_id_valid,
   fetcher::get_or_fetch_and_upsert_user,
   inbox::get_activity_to_and_cc,
-  FromApub,
+  objects::FromApub,
   NoteExt,
 };
 use activitystreams::{
@@ -13,11 +13,7 @@ use activitystreams::{
   public,
 };
 use anyhow::{anyhow, Context};
-use lemmy_db::{
-  private_message::{PrivateMessage, PrivateMessageForm},
-  private_message_view::PrivateMessageView,
-  Crud,
-};
+use lemmy_db::{private_message::PrivateMessage, private_message_view::PrivateMessageView};
 use lemmy_structs::{blocking, user::PrivateMessageResponse};
 use lemmy_utils::{location_info, LemmyError};
 use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperation};
@@ -41,15 +37,10 @@ pub(crate) async fn receive_create_private_message(
   .context(location_info!())?;
 
   let private_message =
-    PrivateMessageForm::from_apub(&note, context, Some(expected_domain), request_counter).await?;
-
-  let inserted_private_message = blocking(&context.pool(), move |conn| {
-    PrivateMessage::create(conn, &private_message)
-  })
-  .await??;
+    PrivateMessage::from_apub(&note, context, expected_domain, request_counter).await?;
 
   let message = blocking(&context.pool(), move |conn| {
-    PrivateMessageView::read(conn, inserted_private_message.id)
+    PrivateMessageView::read(conn, private_message.id)
   })
   .await??;
 
@@ -82,24 +73,8 @@ pub(crate) async fn receive_update_private_message(
     .to_owned();
   let note = NoteExt::from_any_base(object)?.context(location_info!())?;
 
-  let private_message_form =
-    PrivateMessageForm::from_apub(&note, context, Some(expected_domain), request_counter).await?;
-
-  let private_message_ap_id = private_message_form
-    .ap_id
-    .as_ref()
-    .context(location_info!())?
-    .clone();
-  let private_message = blocking(&context.pool(), move |conn| {
-    PrivateMessage::read_from_apub_id(conn, &private_message_ap_id)
-  })
-  .await??;
-
-  let private_message_id = private_message.id;
-  blocking(&context.pool(), move |conn| {
-    PrivateMessage::update(conn, private_message_id, &private_message_form)
-  })
-  .await??;
+  let private_message =
+    PrivateMessage::from_apub(&note, context, expected_domain, request_counter).await?;
 
   let private_message_id = private_message.id;
   let message = blocking(&context.pool(), move |conn| {
index 2aee7b6e30c5ef28d90ef28854d8c2285054ec6e..744a1cddbfe33dedcf55aeefed56321c0629f12e 100644 (file)
@@ -3,10 +3,10 @@ use crate::{
   activity_queue::{send_comment_mentions, send_to_community},
   extensions::context::lemmy_context,
   fetcher::get_or_fetch_and_upsert_user,
+  objects::ToApub,
   ActorType,
   ApubLikeableType,
   ApubObjectType,
-  ToApub,
 };
 use activitystreams::{
   activity::{
@@ -218,8 +218,6 @@ impl ApubObjectType for Comment {
 #[async_trait::async_trait(?Send)]
 impl ApubLikeableType for Comment {
   async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
-    let note = self.to_apub(context.pool()).await?;
-
     let post_id = self.post_id;
     let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
 
@@ -229,7 +227,7 @@ impl ApubLikeableType for Comment {
     })
     .await??;
 
-    let mut like = Like::new(creator.actor_id.to_owned(), note.into_any_base()?);
+    let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
     like
       .set_many_contexts(lemmy_context()?)
       .set_id(generate_activity_id(LikeType::Like)?)
@@ -241,8 +239,6 @@ impl ApubLikeableType for Comment {
   }
 
   async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
-    let note = self.to_apub(context.pool()).await?;
-
     let post_id = self.post_id;
     let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
 
@@ -252,7 +248,7 @@ impl ApubLikeableType for Comment {
     })
     .await??;
 
-    let mut dislike = Dislike::new(creator.actor_id.to_owned(), note.into_any_base()?);
+    let mut dislike = Dislike::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
     dislike
       .set_many_contexts(lemmy_context()?)
       .set_id(generate_activity_id(DislikeType::Dislike)?)
@@ -268,8 +264,6 @@ impl ApubLikeableType for Comment {
     creator: &User_,
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
-    let note = self.to_apub(context.pool()).await?;
-
     let post_id = self.post_id;
     let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
 
@@ -279,7 +273,7 @@ impl ApubLikeableType for Comment {
     })
     .await??;
 
-    let mut like = Like::new(creator.actor_id.to_owned(), note.into_any_base()?);
+    let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
     like
       .set_many_contexts(lemmy_context()?)
       .set_id(generate_activity_id(DislikeType::Dislike)?)
index da78667f0c824941ecacb56e402d8fd485b7c222..f6eabb0485655fe3a4cff2147241a9f6e103b989 100644 (file)
@@ -2,10 +2,10 @@ use crate::{
   activities::send::generate_activity_id,
   activity_queue::send_to_community,
   extensions::context::lemmy_context,
+  objects::ToApub,
   ActorType,
   ApubLikeableType,
   ApubObjectType,
-  ToApub,
 };
 use activitystreams::{
   activity::{
@@ -167,15 +167,13 @@ impl ApubObjectType for Post {
 #[async_trait::async_trait(?Send)]
 impl ApubLikeableType for Post {
   async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
-    let page = self.to_apub(context.pool()).await?;
-
     let community_id = self.community_id;
     let community = blocking(context.pool(), move |conn| {
       Community::read(conn, community_id)
     })
     .await??;
 
-    let mut like = Like::new(creator.actor_id.to_owned(), page.into_any_base()?);
+    let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
     like
       .set_many_contexts(lemmy_context()?)
       .set_id(generate_activity_id(LikeType::Like)?)
@@ -187,15 +185,13 @@ impl ApubLikeableType for Post {
   }
 
   async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
-    let page = self.to_apub(context.pool()).await?;
-
     let community_id = self.community_id;
     let community = blocking(context.pool(), move |conn| {
       Community::read(conn, community_id)
     })
     .await??;
 
-    let mut dislike = Dislike::new(creator.actor_id.to_owned(), page.into_any_base()?);
+    let mut dislike = Dislike::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
     dislike
       .set_many_contexts(lemmy_context()?)
       .set_id(generate_activity_id(DislikeType::Dislike)?)
@@ -211,15 +207,13 @@ impl ApubLikeableType for Post {
     creator: &User_,
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
-    let page = self.to_apub(context.pool()).await?;
-
     let community_id = self.community_id;
     let community = blocking(context.pool(), move |conn| {
       Community::read(conn, community_id)
     })
     .await??;
 
-    let mut like = Like::new(creator.actor_id.to_owned(), page.into_any_base()?);
+    let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
     like
       .set_many_contexts(lemmy_context()?)
       .set_id(generate_activity_id(LikeType::Like)?)
index 23528b5c7eaa36bfffac7c2516f6f458b491befa..e8bc979a7917c674326d360fd37db0bf4cf39fd7 100644 (file)
@@ -2,9 +2,9 @@ use crate::{
   activities::send::generate_activity_id,
   activity_queue::send_activity_single_dest,
   extensions::context::lemmy_context,
+  objects::ToApub,
   ActorType,
   ApubObjectType,
-  ToApub,
 };
 use activitystreams::{
   activity::{
index a94f241dac715411cecc0bd7184833f1e7e9c37a..8c539c4b0ceb5ec8eecfe0dcbc5f6ef06fc9f4c8 100644 (file)
@@ -16,6 +16,7 @@ use activitystreams::{
 use lemmy_db::{
   community::{Community, CommunityFollower, CommunityFollowerForm},
   user::User_,
+  ApubObject,
   DbPool,
   Followable,
 };
@@ -46,7 +47,7 @@ impl ActorType for User_ {
   ) -> Result<(), LemmyError> {
     let follow_actor_id = follow_actor_id.to_string();
     let community = blocking(context.pool(), move |conn| {
-      Community::read_from_actor_id(conn, &follow_actor_id)
+      Community::read_from_apub_id(conn, &follow_actor_id)
     })
     .await??;
 
@@ -77,7 +78,7 @@ impl ActorType for User_ {
   ) -> Result<(), LemmyError> {
     let follow_actor_id = follow_actor_id.to_string();
     let community = blocking(context.pool(), move |conn| {
-      Community::read_from_actor_id(conn, &follow_actor_id)
+      Community::read_from_apub_id(conn, &follow_actor_id)
     })
     .await??;
 
index 5e4f113b5dc0fd9c5c98386c8b9676bb2a2af095..467802794f606f4c16b600257fead7a7e92f9fec 100644 (file)
@@ -33,7 +33,7 @@ use url::Url;
 /// * `activity` the apub activity to be sent
 /// * `creator` the local actor which created the activity
 /// * `inbox` the inbox url where the activity should be delivered to
-pub async fn send_activity_single_dest<T, Kind>(
+pub(crate) async fn send_activity_single_dest<T, Kind>(
   activity: T,
   creator: &dyn ActorType,
   inbox: Url,
@@ -71,7 +71,7 @@ where
 /// * `community` the sending community
 /// * `sender_shared_inbox` in case of an announce, this should be the shared inbox of the inner
 ///                         activities creator, as receiving a known activity will cause an error
-pub async fn send_to_community_followers<T, Kind>(
+pub(crate) async fn send_to_community_followers<T, Kind>(
   activity: T,
   community: &Community,
   context: &LemmyContext,
@@ -116,7 +116,7 @@ where
 /// * `creator` the creator of the activity
 /// * `community` the destination community
 ///
-pub async fn send_to_community<T, Kind>(
+pub(crate) async fn send_to_community<T, Kind>(
   activity: T,
   creator: &User_,
   community: &Community,
@@ -160,7 +160,7 @@ where
 /// * `creator` user who created the comment
 /// * `mentions` list of inboxes of users which are mentioned in the comment
 /// * `activity` either a `Create/Note` or `Update/Note`
-pub async fn send_comment_mentions<T, Kind>(
+pub(crate) async fn send_comment_mentions<T, Kind>(
   creator: &User_,
   mentions: Vec<Url>,
   activity: T,
index 6ff61df4598f20b669a217b4367cb9f45d524ada..67fe4b1cd3a9c910cecf461ee536476973044e94 100644 (file)
@@ -65,7 +65,10 @@ pub async fn sign_and_send(
 }
 
 /// Verifies the HTTP signature on an incoming inbox request.
-pub fn verify_signature(request: &HttpRequest, actor: &dyn ActorType) -> Result<(), LemmyError> {
+pub(crate) fn verify_signature(
+  request: &HttpRequest,
+  actor: &dyn ActorType,
+) -> Result<(), LemmyError> {
   let public_key = actor.public_key().context(location_info!())?;
   let verified = CONFIG2
     .begin_verify(
index ec44bce17f0f11099e31d2e8c2510539482738a8..ad977f5b1b3a371ed9fb1d6aa6bffcf3eead9360 100644 (file)
@@ -1,7 +1,7 @@
 use crate::{
   check_is_apub_id_valid,
+  objects::FromApub,
   ActorType,
-  FromApub,
   GroupExt,
   NoteExt,
   PageExt,
@@ -13,16 +13,16 @@ use anyhow::{anyhow, Context};
 use chrono::NaiveDateTime;
 use diesel::result::Error::NotFound;
 use lemmy_db::{
-  comment::{Comment, CommentForm},
+  comment::Comment,
   comment_view::CommentView,
-  community::{Community, CommunityForm, CommunityModerator, CommunityModeratorForm},
+  community::{Community, CommunityModerator, CommunityModeratorForm},
   community_view::CommunityView,
   naive_now,
-  post::{Post, PostForm},
+  post::Post,
   post_view::PostView,
-  user::{UserForm, User_},
+  user::User_,
   user_view::UserView,
-  Crud,
+  ApubObject,
   Joinable,
   SearchType,
 };
@@ -184,22 +184,16 @@ pub async fn search_by_apub_id(
       response
     }
     SearchAcceptedObjects::Page(p) => {
-      let post_form = PostForm::from_apub(&p, context, Some(query_url), recursion_counter).await?;
+      let p = Post::from_apub(&p, context, query_url, recursion_counter).await?;
 
-      let p = blocking(context.pool(), move |conn| Post::upsert(conn, &post_form)).await??;
       response.posts =
         vec![blocking(context.pool(), move |conn| PostView::read(conn, p.id, None)).await??];
 
       response
     }
     SearchAcceptedObjects::Comment(c) => {
-      let comment_form =
-        CommentForm::from_apub(&c, context, Some(query_url), recursion_counter).await?;
+      let c = Comment::from_apub(&c, context, query_url, recursion_counter).await?;
 
-      let c = blocking(context.pool(), move |conn| {
-        Comment::upsert(conn, &comment_form)
-      })
-      .await??;
       response.comments = vec![
         blocking(context.pool(), move |conn| {
           CommentView::read(conn, c.id, None)
@@ -243,7 +237,7 @@ pub(crate) async fn get_or_fetch_and_upsert_user(
 ) -> Result<User_, LemmyError> {
   let apub_id_owned = apub_id.to_owned();
   let user = blocking(context.pool(), move |conn| {
-    User_::read_from_actor_id(conn, apub_id_owned.as_ref())
+    User_::read_from_apub_id(conn, apub_id_owned.as_ref())
   })
   .await?;
 
@@ -258,15 +252,13 @@ pub(crate) async fn get_or_fetch_and_upsert_user(
         return Ok(u);
       }
 
-      let mut uf = UserForm::from_apub(
-        &person?,
-        context,
-        Some(apub_id.to_owned()),
-        recursion_counter,
-      )
-      .await?;
-      uf.last_refreshed_at = Some(naive_now());
-      let user = blocking(context.pool(), move |conn| User_::update(conn, u.id, &uf)).await??;
+      let user = User_::from_apub(&person?, context, apub_id.to_owned(), recursion_counter).await?;
+
+      let user_id = user.id;
+      blocking(context.pool(), move |conn| {
+        User_::mark_as_updated(conn, user_id)
+      })
+      .await??;
 
       Ok(user)
     }
@@ -276,14 +268,7 @@ pub(crate) async fn get_or_fetch_and_upsert_user(
       let person =
         fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
 
-      let uf = UserForm::from_apub(
-        &person,
-        context,
-        Some(apub_id.to_owned()),
-        recursion_counter,
-      )
-      .await?;
-      let user = blocking(context.pool(), move |conn| User_::upsert(conn, &uf)).await??;
+      let user = User_::from_apub(&person, context, apub_id.to_owned(), recursion_counter).await?;
 
       Ok(user)
     }
@@ -318,7 +303,7 @@ pub(crate) async fn get_or_fetch_and_upsert_community(
 ) -> Result<Community, LemmyError> {
   let apub_id_owned = apub_id.to_owned();
   let community = blocking(context.pool(), move |conn| {
-    Community::read_from_actor_id(conn, apub_id_owned.as_str())
+    Community::read_from_apub_id(conn, apub_id_owned.as_str())
   })
   .await?;
 
@@ -354,9 +339,8 @@ async fn fetch_remote_community(
   }
 
   let group = group?;
-  let cf =
-    CommunityForm::from_apub(&group, context, Some(apub_id.to_owned()), recursion_counter).await?;
-  let community = blocking(context.pool(), move |conn| Community::upsert(conn, &cf)).await??;
+  let community =
+    Community::from_apub(&group, context, apub_id.to_owned(), recursion_counter).await?;
 
   // Also add the community moderators too
   let attributed_to = group.inner.attributed_to().context(location_info!())?;
@@ -406,23 +390,13 @@ async fn fetch_remote_community(
   }
   for o in outbox_items {
     let page = PageExt::from_any_base(o)?.context(location_info!())?;
+    let page_id = page.id_unchecked().context(location_info!())?;
 
-    // The post creator may be from a blocked instance,
-    // if it errors, then continue
-    let post = match PostForm::from_apub(&page, context, None, recursion_counter).await {
-      Ok(post) => post,
-      Err(_) => continue,
-    };
-    let post_ap_id = post.ap_id.as_ref().context(location_info!())?.clone();
-    // Check whether the post already exists in the local db
-    let existing = blocking(context.pool(), move |conn| {
-      Post::read_from_apub_id(conn, &post_ap_id)
-    })
-    .await?;
-    match existing {
-      Ok(e) => blocking(context.pool(), move |conn| Post::update(conn, e.id, &post)).await??,
-      Err(_) => blocking(context.pool(), move |conn| Post::upsert(conn, &post)).await??,
-    };
+    // The post creator may be from a blocked instance, if it errors, then skip it
+    if check_is_apub_id_valid(page_id).is_err() {
+      continue;
+    }
+    Post::from_apub(&page, context, page_id.to_owned(), recursion_counter).await?;
     // TODO: we need to send a websocket update here
   }
 
@@ -448,17 +422,9 @@ pub(crate) async fn get_or_fetch_and_insert_post(
     Ok(p) => Ok(p),
     Err(NotFound {}) => {
       debug!("Fetching and creating remote post: {}", post_ap_id);
-      let post =
+      let page =
         fetch_remote_object::<PageExt>(context.client(), post_ap_id, recursion_counter).await?;
-      let post_form = PostForm::from_apub(
-        &post,
-        context,
-        Some(post_ap_id.to_owned()),
-        recursion_counter,
-      )
-      .await?;
-
-      let post = blocking(context.pool(), move |conn| Post::upsert(conn, &post_form)).await??;
+      let post = Post::from_apub(&page, context, post_ap_id.to_owned(), recursion_counter).await?;
 
       Ok(post)
     }
@@ -490,25 +456,20 @@ pub(crate) async fn get_or_fetch_and_insert_comment(
       );
       let comment =
         fetch_remote_object::<NoteExt>(context.client(), comment_ap_id, recursion_counter).await?;
-      let comment_form = CommentForm::from_apub(
+      let comment = Comment::from_apub(
         &comment,
         context,
-        Some(comment_ap_id.to_owned()),
+        comment_ap_id.to_owned(),
         recursion_counter,
       )
       .await?;
 
-      let post_id = comment_form.post_id;
+      let post_id = comment.post_id;
       let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
       if post.locked {
         return Err(anyhow!("Post is locked").into());
       }
 
-      let comment = blocking(context.pool(), move |conn| {
-        Comment::upsert(conn, &comment_form)
-      })
-      .await??;
-
       Ok(comment)
     }
     Err(e) => Err(e.into()),
index 936cd98138160dd653e3f67fccb1615c9ed1d011..bb3d13aceb1db8d7db6ce6baed577f16cc82c138 100644 (file)
@@ -1,6 +1,6 @@
 use crate::{
   http::{create_apub_response, create_apub_tombstone_response},
-  ToApub,
+  objects::ToApub,
 };
 use actix_web::{body::Body, web, web::Path, HttpResponse};
 use diesel::result::Error::NotFound;
index 0e2f2802e98e2a28f5e203879f91adc36d4cfac2..0a90571ff0287bfbb00b8eceb5cd2e60875b2f15 100644 (file)
@@ -1,8 +1,8 @@
 use crate::{
   extensions::context::lemmy_context,
   http::{create_apub_response, create_apub_tombstone_response},
+  objects::ToApub,
   ActorType,
-  ToApub,
 };
 use activitystreams::{
   base::{AnyBase, BaseExt, ExtendsExt},
index 7f4bb447cc915221700a2055093e3e026de53976..1d25ed958ecb15107a5d830613152f5c1019b285 100644 (file)
@@ -1,6 +1,6 @@
 use crate::{
   http::{create_apub_response, create_apub_tombstone_response},
-  ToApub,
+  objects::ToApub,
 };
 use actix_web::{body::Body, web, HttpResponse};
 use diesel::result::Error::NotFound;
index 0f2e1a71cc36898c59b0c4446f592076c8bec11e..1e546d953dd47026116b00b29c22f97b57a98ea5 100644 (file)
@@ -1,4 +1,9 @@
-use crate::{extensions::context::lemmy_context, http::create_apub_response, ActorType, ToApub};
+use crate::{
+  extensions::context::lemmy_context,
+  http::create_apub_response,
+  objects::ToApub,
+  ActorType,
+};
 use activitystreams::{
   base::BaseExt,
   collection::{CollectionExt, OrderedCollection},
index 7c144a00d5a7decafd377977144c693c527ba25a..4bdad2fadcc433dd273459be7b63e69cfc404e7f 100644 (file)
@@ -30,6 +30,7 @@ use lemmy_db::{
   community::{Community, CommunityFollower, CommunityFollowerForm},
   community_view::CommunityUserBanView,
   user::User_,
+  ApubObject,
   DbPool,
   Followable,
 };
@@ -118,7 +119,7 @@ pub(crate) async fn community_receive_message(
   // unconditionally.
   let actor_id = actor.actor_id_str();
   let user = blocking(&context.pool(), move |conn| {
-    User_::read_from_actor_id(&conn, &actor_id)
+    User_::read_from_apub_id(&conn, &actor_id)
   })
   .await??;
   check_community_or_site_ban(&user, &to_community, context.pool()).await?;
@@ -242,7 +243,7 @@ async fn handle_undo_follow(
   verify_activity_domains_valid(&follow, &user_url, false)?;
 
   let user = blocking(&context.pool(), move |conn| {
-    User_::read_from_actor_id(&conn, user_url.as_str())
+    User_::read_from_apub_id(&conn, user_url.as_str())
   })
   .await??;
   let community_follower_form = CommunityFollowerForm {
@@ -260,7 +261,7 @@ async fn handle_undo_follow(
   Ok(())
 }
 
-async fn check_community_or_site_ban(
+pub(crate) async fn check_community_or_site_ban(
   user: &User_,
   community: &Community,
   pool: &DbPool,
index ce6c7eded500cf8da924d935f091be6cf80c55b2..e04fdd0ffd032e3ffa429e6392b101a7ad3587a7 100644 (file)
@@ -12,7 +12,7 @@ use activitystreams::{
 };
 use actix_web::HttpRequest;
 use anyhow::{anyhow, Context};
-use lemmy_db::{activity::Activity, community::Community, user::User_, DbPool};
+use lemmy_db::{activity::Activity, community::Community, user::User_, ApubObject, DbPool};
 use lemmy_structs::blocking;
 use lemmy_utils::{location_info, settings::Settings, LemmyError};
 use lemmy_websocket::LemmyContext;
@@ -119,7 +119,7 @@ pub(crate) async fn is_addressed_to_local_user(
 ) -> Result<bool, LemmyError> {
   for url in to_and_cc {
     let url = url.to_string();
-    let user = blocking(&pool, move |conn| User_::read_from_actor_id(&conn, &url)).await?;
+    let user = blocking(&pool, move |conn| User_::read_from_apub_id(&conn, &url)).await?;
     if let Ok(u) = user {
       if u.local {
         return Ok(true);
@@ -141,7 +141,7 @@ pub(crate) async fn is_addressed_to_community_followers(
     if url.ends_with("/followers") {
       let community_url = url.replace("/followers", "");
       let community = blocking(&pool, move |conn| {
-        Community::read_from_actor_id(&conn, &community_url)
+        Community::read_from_apub_id(&conn, &community_url)
       })
       .await??;
       if !community.local {
index b6dfa1e423683e942dc78290d512b272f6731b42..eaad6b1ccbf2e0301c574f231e63c8a335268286 100644 (file)
@@ -31,6 +31,7 @@ use crate::{
     receive_unhandled_activity,
     verify_activity_domains_valid,
   },
+  fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
   inbox::is_addressed_to_public,
 };
 use activitystreams::{
@@ -40,7 +41,7 @@ use activitystreams::{
 };
 use anyhow::Context;
 use diesel::result::Error::NotFound;
-use lemmy_db::{comment::Comment, post::Post, site::Site, Crud};
+use lemmy_db::{comment::Comment, post::Post, site::Site, ApubObject, Crud};
 use lemmy_structs::blocking;
 use lemmy_utils::{location_info, LemmyError};
 use lemmy_websocket::LemmyContext;
@@ -96,10 +97,12 @@ pub(in crate::inbox) async fn receive_like_for_community(
   verify_activity_domains_valid(&like, &expected_domain, false)?;
   is_addressed_to_public(&like)?;
 
-  match like.object().as_single_kind_str() {
-    Some("Page") => receive_like_post(like, context, request_counter).await,
-    Some("Note") => receive_like_comment(like, context, request_counter).await,
-    _ => receive_unhandled_activity(like),
+  let object_id = get_like_object_id(&like)?;
+  match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+    PostOrComment::Post(post) => receive_like_post(like, post, context, request_counter).await,
+    PostOrComment::Comment(comment) => {
+      receive_like_comment(like, comment, context, request_counter).await
+    }
   }
 }
 
@@ -122,10 +125,14 @@ pub(in crate::inbox) async fn receive_dislike_for_community(
   verify_activity_domains_valid(&dislike, &expected_domain, false)?;
   is_addressed_to_public(&dislike)?;
 
-  match dislike.object().as_single_kind_str() {
-    Some("Page") => receive_dislike_post(dislike, context, request_counter).await,
-    Some("Note") => receive_dislike_comment(dislike, context, request_counter).await,
-    _ => receive_unhandled_activity(dislike),
+  let object_id = get_like_object_id(&dislike)?;
+  match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+    PostOrComment::Post(post) => {
+      receive_dislike_post(dislike, post, context, request_counter).await
+    }
+    PostOrComment::Comment(comment) => {
+      receive_dislike_comment(dislike, comment, context, request_counter).await
+    }
   }
 }
 
@@ -275,14 +282,14 @@ pub(in crate::inbox) async fn receive_undo_like_for_community(
   verify_activity_domains_valid(&like, &expected_domain, false)?;
   is_addressed_to_public(&like)?;
 
-  let type_ = like
-    .object()
-    .as_single_kind_str()
-    .context(location_info!())?;
-  match type_ {
-    "Note" => receive_undo_like_comment(&like, context, request_counter).await,
-    "Page" => receive_undo_like_post(&like, context, request_counter).await,
-    _ => receive_unhandled_activity(like),
+  let object_id = get_like_object_id(&like)?;
+  match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+    PostOrComment::Post(post) => {
+      receive_undo_like_post(&like, post, context, request_counter).await
+    }
+    PostOrComment::Comment(comment) => {
+      receive_undo_like_comment(&like, comment, context, request_counter).await
+    }
   }
 }
 
@@ -298,14 +305,14 @@ pub(in crate::inbox) async fn receive_undo_dislike_for_community(
   verify_activity_domains_valid(&dislike, &expected_domain, false)?;
   is_addressed_to_public(&dislike)?;
 
-  let type_ = dislike
-    .object()
-    .as_single_kind_str()
-    .context(location_info!())?;
-  match type_ {
-    "Note" => receive_undo_dislike_comment(&dislike, context, request_counter).await,
-    "Page" => receive_undo_dislike_post(&dislike, context, request_counter).await,
-    _ => receive_unhandled_activity(dislike),
+  let object_id = get_like_object_id(&dislike)?;
+  match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+    PostOrComment::Post(post) => {
+      receive_undo_dislike_post(&dislike, post, context, request_counter).await
+    }
+    PostOrComment::Comment(comment) => {
+      receive_undo_dislike_comment(&dislike, comment, context, request_counter).await
+    }
   }
 }
 
@@ -341,3 +348,42 @@ async fn find_post_or_comment_by_id(
 
   return Err(NotFound.into());
 }
+
+async fn fetch_post_or_comment_by_id(
+  apub_id: &Url,
+  context: &LemmyContext,
+  request_counter: &mut i32,
+) -> Result<PostOrComment, LemmyError> {
+  if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await {
+    return Ok(PostOrComment::Post(post));
+  }
+
+  if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await {
+    return Ok(PostOrComment::Comment(comment));
+  }
+
+  return Err(NotFound.into());
+}
+
+fn get_like_object_id<Activity>(like_or_dislike: &Activity) -> Result<Url, LemmyError>
+where
+  Activity: ActorAndObjectRefExt,
+{
+  // TODO: For backwards compatibility with older Lemmy versions where like.object contains a full
+  //       post/comment. This can be removed after some time, using
+  //       `activity.oject().as_single_xsd_any_uri()` instead.
+  let object = like_or_dislike.object();
+  if let Some(xsd_uri) = object.as_single_xsd_any_uri() {
+    Ok(xsd_uri.to_owned())
+  } else {
+    Ok(
+      object
+        .to_owned()
+        .one()
+        .context(location_info!())?
+        .id()
+        .context(location_info!())?
+        .to_owned(),
+    )
+  }
+}
index 2875696e250d66d914b9dab7ade19479dc67d62c..826038bf09ccbd37ee5f406dd1c0acb20acd225c 100644 (file)
@@ -15,7 +15,7 @@ use crate::{
 use activitystreams::{activity::ActorAndObject, prelude::*};
 use actix_web::{web, HttpRequest, HttpResponse};
 use anyhow::Context;
-use lemmy_db::{community::Community, DbPool};
+use lemmy_db::{community::Community, ApubObject, DbPool};
 use lemmy_structs::blocking;
 use lemmy_utils::{location_info, LemmyError};
 use lemmy_websocket::LemmyContext;
@@ -137,10 +137,7 @@ async fn extract_local_community_from_destinations(
 ) -> Result<Option<Community>, LemmyError> {
   for url in to_and_cc {
     let url = url.to_string();
-    let community = blocking(&pool, move |conn| {
-      Community::read_from_actor_id(&conn, &url)
-    })
-    .await?;
+    let community = blocking(&pool, move |conn| Community::read_from_apub_id(&conn, &url)).await?;
     if let Ok(c) = community {
       if c.local {
         return Ok(Some(c));
index 2f847a5cd8c5643b5553227c83f54c77c25fec3b..81b9f185378266af85c5fa0c6f4b1027a4c0af28 100644 (file)
@@ -52,6 +52,7 @@ use lemmy_db::{
   community::{Community, CommunityFollower},
   private_message::PrivateMessage,
   user::User_,
+  ApubObject,
   Followable,
 };
 use lemmy_structs::blocking;
@@ -377,7 +378,7 @@ async fn find_community_or_private_message_by_id(
 ) -> Result<CommunityOrPrivateMessage, LemmyError> {
   let ap_id = apub_id.to_string();
   let community = blocking(context.pool(), move |conn| {
-    Community::read_from_actor_id(conn, &ap_id)
+    Community::read_from_apub_id(conn, &ap_id)
   })
   .await?;
   if let Ok(c) = community {
index 2e3f7bfc680e83c6468d57b7d927af3abf1ec5ed..9b933b6e0c8b2cd61078985adb9baa12e09b74a7 100644 (file)
@@ -18,7 +18,7 @@ use activitystreams::{
   activity::Follow,
   actor::{ApActor, Group, Person},
   base::AnyBase,
-  object::{ApObject, Note, Page, Tombstone},
+  object::{ApObject, Note, Page},
 };
 use activitystreams_ext::{Ext1, Ext2};
 use anyhow::{anyhow, Context};
@@ -109,33 +109,6 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
   }
 }
 
-/// Trait for converting an object or actor into the respective ActivityPub type.
-#[async_trait::async_trait(?Send)]
-pub trait ToApub {
-  type ApubType;
-  async fn to_apub(&self, pool: &DbPool) -> Result<Self::ApubType, LemmyError>;
-  fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
-}
-
-#[async_trait::async_trait(?Send)]
-pub trait FromApub {
-  type ApubType;
-  /// Converts an object from ActivityPub type to Lemmy internal type.
-  ///
-  /// * `apub` The object to read from
-  /// * `context` LemmyContext which holds DB pool, HTTP client etc
-  /// * `expected_domain` If present, ensure that the domains of this and of the apub object ID are
-  ///                     identical
-  async fn from_apub(
-    apub: &Self::ApubType,
-    context: &LemmyContext,
-    expected_domain: Option<Url>,
-    request_counter: &mut i32,
-  ) -> Result<Self, LemmyError>
-  where
-    Self: Sized;
-}
-
 /// Common functions for ActivityPub objects, which are implemented by most (but not all) objects
 /// and actors in Lemmy.
 #[async_trait::async_trait(?Send)]
@@ -248,7 +221,7 @@ pub trait ActorType {
 
 /// Store a sent or received activity in the database, for logging purposes. These records are not
 /// persistent.
-pub async fn insert_activity<T>(
+pub(crate) async fn insert_activity<T>(
   ap_id: &Url,
   activity: T,
   local: bool,
index 277a55d06b8426ce704f24dab50a04e53c131898..56d75a404d691c095ab17175c72ab6a610e19ead 100644 (file)
@@ -7,19 +7,22 @@ use crate::{
   },
   objects::{
     check_object_domain,
+    check_object_for_community_or_site_ban,
     create_tombstone,
+    get_object_from_apub,
     get_source_markdown_value,
     set_content_and_source,
+    FromApub,
+    FromApubToForm,
+    ToApub,
   },
-  FromApub,
   NoteExt,
-  ToApub,
 };
 use activitystreams::{
   object::{kind::NoteType, ApObject, Note, Tombstone},
   prelude::*,
 };
-use anyhow::Context;
+use anyhow::{anyhow, Context};
 use lemmy_db::{
   comment::{Comment, CommentForm},
   community::Community,
@@ -87,16 +90,45 @@ impl ToApub for Comment {
 }
 
 #[async_trait::async_trait(?Send)]
-impl FromApub for CommentForm {
+impl FromApub for Comment {
   type ApubType = NoteExt;
 
-  /// Converts a `Note` to `CommentForm`.
+  /// Converts a `Note` to `Comment`.
   ///
   /// If the parent community, post and comment(s) are not known locally, these are also fetched.
   async fn from_apub(
     note: &NoteExt,
     context: &LemmyContext,
-    expected_domain: Option<Url>,
+    expected_domain: Url,
+    request_counter: &mut i32,
+  ) -> Result<Comment, LemmyError> {
+    check_object_for_community_or_site_ban(note, context, request_counter).await?;
+
+    let comment: Comment =
+      get_object_from_apub(note, context, expected_domain, request_counter).await?;
+
+    let post_id = comment.post_id;
+    let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+    if post.locked {
+      // This is not very efficient because a comment gets inserted just to be deleted right
+      // afterwards, but it seems to be the easiest way to implement it.
+      blocking(context.pool(), move |conn| {
+        Comment::delete(conn, comment.id)
+      })
+      .await??;
+      return Err(anyhow!("Post is locked").into());
+    } else {
+      Ok(comment)
+    }
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<NoteExt> for CommentForm {
+  async fn from_apub(
+    note: &NoteExt,
+    context: &LemmyContext,
+    expected_domain: Url,
     request_counter: &mut i32,
   ) -> Result<CommentForm, LemmyError> {
     let creator_actor_id = &note
index 2b383ba5b818623c848cdc64063e0d5f1d3748ac..594a5b5ee03956a1a574be545e8c8f1e88f89c12 100644 (file)
@@ -4,13 +4,15 @@ use crate::{
   objects::{
     check_object_domain,
     create_tombstone,
+    get_object_from_apub,
     get_source_markdown_value,
     set_content_and_source,
+    FromApub,
+    FromApubToForm,
+    ToApub,
   },
   ActorType,
-  FromApub,
   GroupExt,
-  ToApub,
 };
 use activitystreams::{
   actor::{kind::GroupType, ApActor, Endpoints, Group},
@@ -106,14 +108,28 @@ impl ToApub for Community {
     create_tombstone(self.deleted, &self.actor_id, self.updated, GroupType::Group)
   }
 }
+
 #[async_trait::async_trait(?Send)]
-impl FromApub for CommunityForm {
+impl FromApub for Community {
   type ApubType = GroupExt;
 
+  /// Converts a `Group` to `Community`.
+  async fn from_apub(
+    group: &GroupExt,
+    context: &LemmyContext,
+    expected_domain: Url,
+    request_counter: &mut i32,
+  ) -> Result<Community, LemmyError> {
+    get_object_from_apub(group, context, expected_domain, request_counter).await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<GroupExt> for CommunityForm {
   async fn from_apub(
     group: &GroupExt,
     context: &LemmyContext,
-    expected_domain: Option<Url>,
+    expected_domain: Url,
     request_counter: &mut i32,
   ) -> Result<Self, LemmyError> {
     let creator_and_moderator_uris = group.inner.attributed_to().context(location_info!())?;
index a162c165b4f0125870f036885c4997afb4592b4a..898c50f31421f122095e7875af7243d49789a4e9 100644 (file)
@@ -1,4 +1,8 @@
-use crate::check_is_apub_id_valid;
+use crate::{
+  check_is_apub_id_valid,
+  fetcher::{get_or_fetch_and_upsert_community, get_or_fetch_and_upsert_user},
+  inbox::community_inbox::check_community_or_site_ban,
+};
 use activitystreams::{
   base::{AsBase, BaseExt, ExtendsExt},
   markers::Base,
@@ -7,7 +11,10 @@ use activitystreams::{
 };
 use anyhow::{anyhow, Context};
 use chrono::NaiveDateTime;
-use lemmy_utils::{location_info, utils::convert_datetime, LemmyError};
+use lemmy_db::{ApubObject, Crud, DbPool};
+use lemmy_structs::blocking;
+use lemmy_utils::{location_info, settings::Settings, utils::convert_datetime, LemmyError};
+use lemmy_websocket::LemmyContext;
 use url::Url;
 
 pub(crate) mod comment;
@@ -16,6 +23,44 @@ pub(crate) mod post;
 pub(crate) mod private_message;
 pub(crate) mod user;
 
+/// Trait for converting an object or actor into the respective ActivityPub type.
+#[async_trait::async_trait(?Send)]
+pub(crate) trait ToApub {
+  type ApubType;
+  async fn to_apub(&self, pool: &DbPool) -> Result<Self::ApubType, LemmyError>;
+  fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
+}
+
+#[async_trait::async_trait(?Send)]
+pub(crate) trait FromApub {
+  type ApubType;
+  /// Converts an object from ActivityPub type to Lemmy internal type.
+  ///
+  /// * `apub` The object to read from
+  /// * `context` LemmyContext which holds DB pool, HTTP client etc
+  /// * `expected_domain` Domain where the object was received from
+  async fn from_apub(
+    apub: &Self::ApubType,
+    context: &LemmyContext,
+    expected_domain: Url,
+    request_counter: &mut i32,
+  ) -> Result<Self, LemmyError>
+  where
+    Self: Sized;
+}
+
+#[async_trait::async_trait(?Send)]
+pub(in crate::objects) trait FromApubToForm<ApubType> {
+  async fn from_apub(
+    apub: &ApubType,
+    context: &LemmyContext,
+    expected_domain: Url,
+    request_counter: &mut i32,
+  ) -> Result<Self, LemmyError>
+  where
+    Self: Sized;
+}
+
 /// Updated is actually the deletion time
 fn create_tombstone<T>(
   deleted: bool,
@@ -43,17 +88,13 @@ where
 
 pub(in crate::objects) fn check_object_domain<T, Kind>(
   apub: &T,
-  expected_domain: Option<Url>,
+  expected_domain: Url,
 ) -> Result<String, LemmyError>
 where
   T: Base + AsBase<Kind>,
 {
-  let object_id = if let Some(url) = expected_domain {
-    let domain = url.domain().context(location_info!())?;
-    apub.id(domain)?.context(location_info!())?
-  } else {
-    apub.id_unchecked().context(location_info!())?
-  };
+  let domain = expected_domain.domain().context(location_info!())?;
+  let object_id = apub.id(domain)?.context(location_info!())?;
   check_is_apub_id_valid(&object_id)?;
   Ok(object_id.to_string())
 }
@@ -127,3 +168,60 @@ pub(in crate::objects) fn check_is_markdown(mime: Option<&Mime>) -> Result<(), L
     Ok(())
   }
 }
+
+/// Converts an ActivityPub object (eg `Note`) to a database object (eg `Comment`). If an object
+/// with the same ActivityPub ID already exists in the database, it is returned directly. Otherwise
+/// the apub object is parsed, inserted and returned.
+pub(in crate::objects) async fn get_object_from_apub<From, Kind, To, ToForm>(
+  from: &From,
+  context: &LemmyContext,
+  expected_domain: Url,
+  request_counter: &mut i32,
+) -> Result<To, LemmyError>
+where
+  From: BaseExt<Kind>,
+  To: ApubObject<ToForm> + Crud<ToForm> + Send + 'static,
+  ToForm: FromApubToForm<From> + Send + 'static,
+{
+  let object_id = from.id_unchecked().context(location_info!())?.to_owned();
+  let domain = object_id.domain().context(location_info!())?;
+
+  // if its a local object, return it directly from the database
+  if Settings::get().hostname == domain {
+    let object = blocking(context.pool(), move |conn| {
+      To::read_from_apub_id(conn, object_id.as_str())
+    })
+    .await??;
+    Ok(object)
+  }
+  // otherwise parse and insert, assuring that it comes from the right domain
+  else {
+    let to_form = ToForm::from_apub(&from, context, expected_domain, request_counter).await?;
+
+    let to = blocking(context.pool(), move |conn| To::upsert(conn, &to_form)).await??;
+    Ok(to)
+  }
+}
+
+pub(in crate::objects) async fn check_object_for_community_or_site_ban<T, Kind>(
+  object: &T,
+  context: &LemmyContext,
+  request_counter: &mut i32,
+) -> Result<(), LemmyError>
+where
+  T: ObjectExt<Kind>,
+{
+  let user_id = object
+    .attributed_to()
+    .context(location_info!())?
+    .as_single_xsd_any_uri()
+    .context(location_info!())?;
+  let user = get_or_fetch_and_upsert_user(user_id, context, request_counter).await?;
+  let community_id = object
+    .to()
+    .context(location_info!())?
+    .as_single_xsd_any_uri()
+    .context(location_info!())?;
+  let community = get_or_fetch_and_upsert_community(community_id, context, request_counter).await?;
+  check_community_or_site_ban(&user, &community, context.pool()).await
+}
index a058d8b7ccbfeca48b7ab5adb112f4b8ae4cb638..d34098c5b0c7b137ffbad27c0e45a1d1ea0ebd7d 100644 (file)
@@ -3,13 +3,16 @@ use crate::{
   fetcher::{get_or_fetch_and_upsert_community, get_or_fetch_and_upsert_user},
   objects::{
     check_object_domain,
+    check_object_for_community_or_site_ban,
     create_tombstone,
+    get_object_from_apub,
     get_source_markdown_value,
     set_content_and_source,
+    FromApub,
+    FromApubToForm,
+    ToApub,
   },
-  FromApub,
   PageExt,
-  ToApub,
 };
 use activitystreams::{
   object::{kind::PageType, ApObject, Image, Page, Tombstone},
@@ -98,7 +101,7 @@ impl ToApub for Post {
 }
 
 #[async_trait::async_trait(?Send)]
-impl FromApub for PostForm {
+impl FromApub for Post {
   type ApubType = PageExt;
 
   /// Converts a `PageExt` to `PostForm`.
@@ -107,7 +110,20 @@ impl FromApub for PostForm {
   async fn from_apub(
     page: &PageExt,
     context: &LemmyContext,
-    expected_domain: Option<Url>,
+    expected_domain: Url,
+    request_counter: &mut i32,
+  ) -> Result<Post, LemmyError> {
+    check_object_for_community_or_site_ban(page, context, request_counter).await?;
+    get_object_from_apub(page, context, expected_domain, request_counter).await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<PageExt> for PostForm {
+  async fn from_apub(
+    page: &PageExt,
+    context: &LemmyContext,
+    expected_domain: Url,
     request_counter: &mut i32,
   ) -> Result<PostForm, LemmyError> {
     let ext = &page.ext_one;
index 4e9af09433754a93f02b0dc2965db0d0b3e8a255..ec8b55e7e76a49ce4a9c31a1dc1909487b1b4b6d 100644 (file)
@@ -5,12 +5,14 @@ use crate::{
   objects::{
     check_object_domain,
     create_tombstone,
+    get_object_from_apub,
     get_source_markdown_value,
     set_content_and_source,
+    FromApub,
+    FromApubToForm,
+    ToApub,
   },
-  FromApub,
   NoteExt,
-  ToApub,
 };
 use activitystreams::{
   object::{kind::NoteType, ApObject, Note, Tombstone},
@@ -63,13 +65,25 @@ impl ToApub for PrivateMessage {
 }
 
 #[async_trait::async_trait(?Send)]
-impl FromApub for PrivateMessageForm {
+impl FromApub for PrivateMessage {
   type ApubType = NoteExt;
 
   async fn from_apub(
     note: &NoteExt,
     context: &LemmyContext,
-    expected_domain: Option<Url>,
+    expected_domain: Url,
+    request_counter: &mut i32,
+  ) -> Result<PrivateMessage, LemmyError> {
+    get_object_from_apub(note, context, expected_domain, request_counter).await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<NoteExt> for PrivateMessageForm {
+  async fn from_apub(
+    note: &NoteExt,
+    context: &LemmyContext,
+    expected_domain: Url,
     request_counter: &mut i32,
   ) -> Result<PrivateMessageForm, LemmyError> {
     let creator_actor_id = note
index ddf33656c7f7ddc0eee00cac5fbb4380d099875c..18490796aec12e9930ac1ad1ac99af473dd04b87 100644 (file)
@@ -1,10 +1,15 @@
 use crate::{
   extensions::context::lemmy_context,
-  objects::{check_object_domain, get_source_markdown_value, set_content_and_source},
+  objects::{
+    check_object_domain,
+    get_source_markdown_value,
+    set_content_and_source,
+    FromApub,
+    FromApubToForm,
+    ToApub,
+  },
   ActorType,
-  FromApub,
   PersonExt,
-  ToApub,
 };
 use activitystreams::{
   actor::{ApActor, Endpoints, Person},
@@ -16,10 +21,13 @@ use anyhow::Context;
 use lemmy_db::{
   naive_now,
   user::{UserForm, User_},
+  ApubObject,
   DbPool,
 };
+use lemmy_structs::blocking;
 use lemmy_utils::{
   location_info,
+  settings::Settings,
   utils::{check_slurs, check_slurs_opt, convert_datetime},
   LemmyError,
 };
@@ -81,13 +89,38 @@ impl ToApub for User_ {
 }
 
 #[async_trait::async_trait(?Send)]
-impl FromApub for UserForm {
+impl FromApub for User_ {
   type ApubType = PersonExt;
 
+  async fn from_apub(
+    person: &PersonExt,
+    context: &LemmyContext,
+    expected_domain: Url,
+    request_counter: &mut i32,
+  ) -> Result<User_, LemmyError> {
+    let user_id = person.id_unchecked().context(location_info!())?.to_owned();
+    let domain = user_id.domain().context(location_info!())?;
+    if domain == Settings::get().hostname {
+      let user = blocking(context.pool(), move |conn| {
+        User_::read_from_apub_id(conn, user_id.as_str())
+      })
+      .await??;
+      Ok(user)
+    } else {
+      let user_form =
+        UserForm::from_apub(person, context, expected_domain, request_counter).await?;
+      let user = blocking(context.pool(), move |conn| User_::upsert(conn, &user_form)).await??;
+      Ok(user)
+    }
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<PersonExt> for UserForm {
   async fn from_apub(
     person: &PersonExt,
     _context: &LemmyContext,
-    expected_domain: Option<Url>,
+    expected_domain: Url,
     _request_counter: &mut i32,
   ) -> Result<Self, LemmyError> {
     let avatar = match person.icon() {
index 9b0928257a34e86fa4585c4ec647a605efdc4018..f54ddd10f203def7fc3820ec4887a1196a379edb 100644 (file)
@@ -2,6 +2,7 @@ use super::post::Post;
 use crate::{
   naive_now,
   schema::{comment, comment_like, comment_saved},
+  ApubObject,
   Crud,
   Likeable,
   Saveable,
@@ -86,6 +87,23 @@ impl Crud<CommentForm> for Comment {
   }
 }
 
+impl ApubObject<CommentForm> for Comment {
+  fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+    use crate::schema::comment::dsl::*;
+    comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
+  }
+
+  fn upsert(conn: &PgConnection, comment_form: &CommentForm) -> Result<Self, Error> {
+    use crate::schema::comment::dsl::*;
+    insert_into(comment)
+      .values(comment_form)
+      .on_conflict(ap_id)
+      .do_update()
+      .set(comment_form)
+      .get_result::<Self>(conn)
+  }
+}
+
 impl Comment {
   pub fn update_ap_id(
     conn: &PgConnection,
@@ -99,11 +117,6 @@ impl Comment {
       .get_result::<Self>(conn)
   }
 
-  pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
-    use crate::schema::comment::dsl::*;
-    comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
-  }
-
   pub fn permadelete_for_creator(
     conn: &PgConnection,
     for_creator_id: i32,
@@ -168,16 +181,6 @@ impl Comment {
       .set((content.eq(new_content), updated.eq(naive_now())))
       .get_result::<Self>(conn)
   }
-
-  pub fn upsert(conn: &PgConnection, comment_form: &CommentForm) -> Result<Self, Error> {
-    use crate::schema::comment::dsl::*;
-    insert_into(comment)
-      .values(comment_form)
-      .on_conflict(ap_id)
-      .do_update()
-      .set(comment_form)
-      .get_result::<Self>(conn)
-  }
 }
 
 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)]
index 5f76d5143c01019a015cb51cd109ddca28b8fd9a..be40da349246692b0e56714294e2fb9fa7e6e361 100644 (file)
@@ -1,6 +1,7 @@
 use crate::{
   naive_now,
   schema::{community, community_follower, community_moderator, community_user_ban},
+  ApubObject,
   Bannable,
   Crud,
   Followable,
@@ -83,19 +84,31 @@ impl Crud<CommunityForm> for Community {
   }
 }
 
-impl Community {
-  pub fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Self, Error> {
+impl ApubObject<CommunityForm> for Community {
+  fn read_from_apub_id(conn: &PgConnection, for_actor_id: &str) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
     community
-      .filter(local.eq(true))
-      .filter(name.eq(community_name))
+      .filter(actor_id.eq(for_actor_id))
       .first::<Self>(conn)
   }
 
-  pub fn read_from_actor_id(conn: &PgConnection, for_actor_id: &str) -> Result<Self, Error> {
+  fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
+    use crate::schema::community::dsl::*;
+    insert_into(community)
+      .values(community_form)
+      .on_conflict(actor_id)
+      .do_update()
+      .set(community_form)
+      .get_result::<Self>(conn)
+  }
+}
+
+impl Community {
+  pub fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
     community
-      .filter(actor_id.eq(for_actor_id))
+      .filter(local.eq(true))
+      .filter(name.eq(community_name))
       .first::<Self>(conn)
   }
 
@@ -165,16 +178,6 @@ impl Community {
       .unwrap_or_default()
       .contains(&user_id)
   }
-
-  pub fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
-    use crate::schema::community::dsl::*;
-    insert_into(community)
-      .values(community_form)
-      .on_conflict(actor_id)
-      .do_update()
-      .set(community_form)
-      .get_result::<Self>(conn)
-  }
 }
 
 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
index bad646d14be2b3d73dd0080da80e10b3f341932a..0ca4826a19486ebbb11cd3b9f5be4dfbab632f25 100644 (file)
@@ -124,6 +124,15 @@ pub trait Reportable<T> {
     Self: Sized;
 }
 
+pub trait ApubObject<T> {
+  fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error>
+  where
+    Self: Sized;
+  fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error>
+  where
+    Self: Sized;
+}
+
 pub trait MaybeOptional<T> {
   fn get_optional(self) -> Option<T>;
 }
index 787d5e6c418a08148fc124422e95890c3817c1d1..530f475b4c35b68299db7162b5a7052cb11402d0 100644 (file)
@@ -1,6 +1,7 @@
 use crate::{
   naive_now,
   schema::{post, post_like, post_read, post_saved},
+  ApubObject,
   Crud,
   Likeable,
   Readable,
@@ -62,6 +63,47 @@ impl PostForm {
   }
 }
 
+impl Crud<PostForm> for Post {
+  fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
+    use crate::schema::post::dsl::*;
+    post.find(post_id).first::<Self>(conn)
+  }
+
+  fn delete(conn: &PgConnection, post_id: i32) -> Result<usize, Error> {
+    use crate::schema::post::dsl::*;
+    diesel::delete(post.find(post_id)).execute(conn)
+  }
+
+  fn create(conn: &PgConnection, new_post: &PostForm) -> Result<Self, Error> {
+    use crate::schema::post::dsl::*;
+    insert_into(post).values(new_post).get_result::<Self>(conn)
+  }
+
+  fn update(conn: &PgConnection, post_id: i32, new_post: &PostForm) -> Result<Self, Error> {
+    use crate::schema::post::dsl::*;
+    diesel::update(post.find(post_id))
+      .set(new_post)
+      .get_result::<Self>(conn)
+  }
+}
+
+impl ApubObject<PostForm> for Post {
+  fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+    use crate::schema::post::dsl::*;
+    post.filter(ap_id.eq(object_id)).first::<Self>(conn)
+  }
+
+  fn upsert(conn: &PgConnection, post_form: &PostForm) -> Result<Post, Error> {
+    use crate::schema::post::dsl::*;
+    insert_into(post)
+      .values(post_form)
+      .on_conflict(ap_id)
+      .do_update()
+      .set(post_form)
+      .get_result::<Self>(conn)
+  }
+}
+
 impl Post {
   pub fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
     use crate::schema::post::dsl::*;
@@ -81,11 +123,6 @@ impl Post {
       .load::<Self>(conn)
   }
 
-  pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    post.filter(ap_id.eq(object_id)).first::<Self>(conn)
-  }
-
   pub fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: String) -> Result<Self, Error> {
     use crate::schema::post::dsl::*;
 
@@ -177,40 +214,6 @@ impl Post {
   pub fn is_post_creator(user_id: i32, post_creator_id: i32) -> bool {
     user_id == post_creator_id
   }
-
-  pub fn upsert(conn: &PgConnection, post_form: &PostForm) -> Result<Post, Error> {
-    use crate::schema::post::dsl::*;
-    insert_into(post)
-      .values(post_form)
-      .on_conflict(ap_id)
-      .do_update()
-      .set(post_form)
-      .get_result::<Self>(conn)
-  }
-}
-
-impl Crud<PostForm> for Post {
-  fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    post.find(post_id).first::<Self>(conn)
-  }
-
-  fn delete(conn: &PgConnection, post_id: i32) -> Result<usize, Error> {
-    use crate::schema::post::dsl::*;
-    diesel::delete(post.find(post_id)).execute(conn)
-  }
-
-  fn create(conn: &PgConnection, new_post: &PostForm) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    insert_into(post).values(new_post).get_result::<Self>(conn)
-  }
-
-  fn update(conn: &PgConnection, post_id: i32, new_post: &PostForm) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    diesel::update(post.find(post_id))
-      .set(new_post)
-      .get_result::<Self>(conn)
-  }
 }
 
 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
index 503a26abf748d799a4f9d38f64768bf2f36e3fa2..0e1aef10841fc580385f64f8ea44584ffa7ea0b4 100644 (file)
@@ -1,4 +1,4 @@
-use crate::{naive_now, schema::private_message, Crud};
+use crate::{naive_now, schema::private_message, ApubObject, Crud};
 use diesel::{dsl::*, result::Error, *};
 
 #[derive(Queryable, Identifiable, PartialEq, Debug)]
@@ -55,6 +55,28 @@ impl Crud<PrivateMessageForm> for PrivateMessage {
   }
 }
 
+impl ApubObject<PrivateMessageForm> for PrivateMessage {
+  fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error>
+  where
+    Self: Sized,
+  {
+    use crate::schema::private_message::dsl::*;
+    private_message
+      .filter(ap_id.eq(object_id))
+      .first::<Self>(conn)
+  }
+
+  fn upsert(conn: &PgConnection, private_message_form: &PrivateMessageForm) -> Result<Self, Error> {
+    use crate::schema::private_message::dsl::*;
+    insert_into(private_message)
+      .values(private_message_form)
+      .on_conflict(ap_id)
+      .do_update()
+      .set(private_message_form)
+      .get_result::<Self>(conn)
+  }
+}
+
 impl PrivateMessage {
   pub fn update_ap_id(
     conn: &PgConnection,
@@ -68,13 +90,6 @@ impl PrivateMessage {
       .get_result::<Self>(conn)
   }
 
-  pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
-    use crate::schema::private_message::dsl::*;
-    private_message
-      .filter(ap_id.eq(object_id))
-      .first::<Self>(conn)
-  }
-
   pub fn update_content(
     conn: &PgConnection,
     private_message_id: i32,
@@ -118,20 +133,6 @@ impl PrivateMessage {
     .set(read.eq(true))
     .get_results::<Self>(conn)
   }
-
-  // TODO use this
-  pub fn upsert(
-    conn: &PgConnection,
-    private_message_form: &PrivateMessageForm,
-  ) -> Result<Self, Error> {
-    use crate::schema::private_message::dsl::*;
-    insert_into(private_message)
-      .values(private_message_form)
-      .on_conflict(ap_id)
-      .do_update()
-      .set(private_message_form)
-      .get_result::<Self>(conn)
-  }
 }
 
 #[cfg(test)]
index 2c4c67ea2f53f51364d620ffde60100da4e1f48f..d8e833e6e5bdb2540e7d5d3777c821446edaa10e 100644 (file)
@@ -2,6 +2,7 @@ use crate::{
   is_email_regex,
   naive_now,
   schema::{user_, user_::dsl::*},
+  ApubObject,
   Crud,
 };
 use bcrypt::{hash, DEFAULT_COST};
@@ -89,6 +90,25 @@ impl Crud<UserForm> for User_ {
   }
 }
 
+impl ApubObject<UserForm> for User_ {
+  fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+    use crate::schema::user_::dsl::*;
+    user_
+      .filter(deleted.eq(false))
+      .filter(actor_id.eq(object_id))
+      .first::<Self>(conn)
+  }
+
+  fn upsert(conn: &PgConnection, user_form: &UserForm) -> Result<User_, Error> {
+    insert_into(user_)
+      .values(user_form)
+      .on_conflict(actor_id)
+      .do_update()
+      .set(user_form)
+      .get_result::<Self>(conn)
+  }
+}
+
 impl User_ {
   pub fn register(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
     let mut edited_user = form.clone();
@@ -135,14 +155,6 @@ impl User_ {
       .get_result::<Self>(conn)
   }
 
-  pub fn read_from_actor_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
-    use crate::schema::user_::dsl::*;
-    user_
-      .filter(deleted.eq(false))
-      .filter(actor_id.eq(object_id))
-      .first::<Self>(conn)
-  }
-
   pub fn find_by_email_or_username(
     conn: &PgConnection,
     username_or_email: &str,
@@ -179,12 +191,9 @@ impl User_ {
     )
   }
 
-  pub fn upsert(conn: &PgConnection, user_form: &UserForm) -> Result<User_, Error> {
-    insert_into(user_)
-      .values(user_form)
-      .on_conflict(actor_id)
-      .do_update()
-      .set(user_form)
+  pub fn mark_as_updated(conn: &PgConnection, user_id: i32) -> Result<User_, Error> {
+    diesel::update(user_.find(user_id))
+      .set((last_refreshed_at.eq(naive_now()),))
       .get_result::<Self>(conn)
   }