From: Nutomic <me@nutomic.com>
Date: Tue, 1 Aug 2023 13:53:36 +0000 (+0200)
Subject: Rewrite some federation actions to remove Perform/SendActivity (ref #3670) (#3758)
X-Git-Url: http://these/git/%22https:/lemmy.ml/post/241819/%7B%7D/inbox?a=commitdiff_plain;h=d82194cfe9f98984a64353f6511bb73b4f08baa4;p=lemmy.git

Rewrite some federation actions to remove Perform/SendActivity (ref #3670) (#3758)
---

diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs
index e84c55cb..13122b4b 100644
--- a/crates/api/src/comment/like.rs
+++ b/crates/api/src/comment/like.rs
@@ -1,9 +1,10 @@
-use crate::Perform;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
 use lemmy_api_common::{
   build_response::build_comment_response,
   comment::{CommentResponse, CreateCommentLike},
   context::LemmyContext,
+  send_activity::{ActivityChannel, SendActivityData},
   utils::{check_community_ban, check_downvotes_enabled, local_user_view_from_jwt},
 };
 use lemmy_db_schema::{
@@ -17,70 +18,80 @@ use lemmy_db_schema::{
 };
 use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
+use std::ops::Deref;
 
-#[async_trait::async_trait(?Send)]
-impl Perform for CreateCommentLike {
-  type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn like_comment(
+  data: Json<CreateCommentLike>,
+  context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+  let local_site = LocalSite::read(&mut context.pool()).await?;
+  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
 
-  #[tracing::instrument(skip(context))]
-  async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
-    let data: &CreateCommentLike = self;
-    let local_site = LocalSite::read(&mut context.pool()).await?;
-    let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
+  let mut recipient_ids = Vec::<LocalUserId>::new();
 
-    let mut recipient_ids = Vec::<LocalUserId>::new();
+  // Don't do a downvote if site has downvotes disabled
+  check_downvotes_enabled(data.score, &local_site)?;
 
-    // Don't do a downvote if site has downvotes disabled
-    check_downvotes_enabled(data.score, &local_site)?;
+  let comment_id = data.comment_id;
+  let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
 
-    let comment_id = data.comment_id;
-    let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+  check_community_ban(
+    local_user_view.person.id,
+    orig_comment.community.id,
+    &mut context.pool(),
+  )
+  .await?;
 
-    check_community_ban(
-      local_user_view.person.id,
-      orig_comment.community.id,
-      &mut context.pool(),
-    )
-    .await?;
-
-    // Add parent poster or commenter to recipients
-    let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
-    if let Ok(reply) = comment_reply {
-      let recipient_id = reply.recipient_id;
-      if let Ok(local_recipient) =
-        LocalUserView::read_person(&mut context.pool(), recipient_id).await
-      {
-        recipient_ids.push(local_recipient.local_user.id);
-      }
+  // Add parent poster or commenter to recipients
+  let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
+  if let Ok(reply) = comment_reply {
+    let recipient_id = reply.recipient_id;
+    if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await
+    {
+      recipient_ids.push(local_recipient.local_user.id);
     }
+  }
 
-    let like_form = CommentLikeForm {
-      comment_id: data.comment_id,
-      post_id: orig_comment.post.id,
-      person_id: local_user_view.person.id,
-      score: data.score,
-    };
+  let like_form = CommentLikeForm {
+    comment_id: data.comment_id,
+    post_id: orig_comment.post.id,
+    person_id: local_user_view.person.id,
+    score: data.score,
+  };
 
-    // Remove any likes first
-    let person_id = local_user_view.person.id;
+  // Remove any likes first
+  let person_id = local_user_view.person.id;
 
-    CommentLike::remove(&mut context.pool(), person_id, comment_id).await?;
+  CommentLike::remove(&mut context.pool(), person_id, comment_id).await?;
 
-    // Only add the like if the score isnt 0
-    let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
-    if do_add {
-      CommentLike::like(&mut context.pool(), &like_form)
-        .await
-        .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
-    }
+  // Only add the like if the score isnt 0
+  let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
+  if do_add {
+    CommentLike::like(&mut context.pool(), &like_form)
+      .await
+      .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
+  }
+
+  ActivityChannel::submit_activity(
+    SendActivityData::LikePostOrComment(
+      orig_comment.comment.ap_id,
+      local_user_view.person.clone(),
+      orig_comment.community,
+      data.score,
+    ),
+    &context,
+  )
+  .await?;
 
+  Ok(Json(
     build_comment_response(
-      context,
+      context.deref(),
       comment_id,
       Some(local_user_view),
       None,
       recipient_ids,
     )
-    .await
-  }
+    .await?,
+  ))
 }
diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs
index 025129d3..1ff119f0 100644
--- a/crates/api/src/post/like.rs
+++ b/crates/api/src/post/like.rs
@@ -1,9 +1,10 @@
-use crate::Perform;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
 use lemmy_api_common::{
   build_response::build_post_response,
   context::LemmyContext,
   post::{CreatePostLike, PostResponse},
+  send_activity::{ActivityChannel, SendActivityData},
   utils::{
     check_community_ban,
     check_community_deleted_or_removed,
@@ -14,66 +15,78 @@ use lemmy_api_common::{
 };
 use lemmy_db_schema::{
   source::{
+    community::Community,
     local_site::LocalSite,
     post::{Post, PostLike, PostLikeForm},
   },
   traits::{Crud, Likeable},
 };
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
+use std::ops::Deref;
 
-#[async_trait::async_trait(?Send)]
-impl Perform for CreatePostLike {
-  type Response = PostResponse;
+#[tracing::instrument(skip(context))]
+pub async fn like_post(
+  data: Json<CreatePostLike>,
+  context: Data<LemmyContext>,
+) -> Result<Json<PostResponse>, LemmyError> {
+  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
+  let local_site = LocalSite::read(&mut context.pool()).await?;
 
-  #[tracing::instrument(skip(context))]
-  async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
-    let data: &CreatePostLike = self;
-    let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
-    let local_site = LocalSite::read(&mut context.pool()).await?;
+  // Don't do a downvote if site has downvotes disabled
+  check_downvotes_enabled(data.score, &local_site)?;
 
-    // Don't do a downvote if site has downvotes disabled
-    check_downvotes_enabled(data.score, &local_site)?;
+  // Check for a community ban
+  let post_id = data.post_id;
+  let post = Post::read(&mut context.pool(), post_id).await?;
 
-    // Check for a community ban
-    let post_id = data.post_id;
-    let post = Post::read(&mut context.pool(), post_id).await?;
+  check_community_ban(
+    local_user_view.person.id,
+    post.community_id,
+    &mut context.pool(),
+  )
+  .await?;
+  check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
 
-    check_community_ban(
-      local_user_view.person.id,
-      post.community_id,
-      &mut context.pool(),
-    )
-    .await?;
-    check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
+  let like_form = PostLikeForm {
+    post_id: data.post_id,
+    person_id: local_user_view.person.id,
+    score: data.score,
+  };
 
-    let like_form = PostLikeForm {
-      post_id: data.post_id,
-      person_id: local_user_view.person.id,
-      score: data.score,
-    };
+  // Remove any likes first
+  let person_id = local_user_view.person.id;
 
-    // Remove any likes first
-    let person_id = local_user_view.person.id;
+  PostLike::remove(&mut context.pool(), person_id, post_id).await?;
 
-    PostLike::remove(&mut context.pool(), person_id, post_id).await?;
+  // Only add the like if the score isnt 0
+  let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
+  if do_add {
+    PostLike::like(&mut context.pool(), &like_form)
+      .await
+      .with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
+  }
 
-    // Only add the like if the score isnt 0
-    let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
-    if do_add {
-      PostLike::like(&mut context.pool(), &like_form)
-        .await
-        .with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
-    }
+  // Mark the post as read
+  mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
 
-    // Mark the post as read
-    mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
+  ActivityChannel::submit_activity(
+    SendActivityData::LikePostOrComment(
+      post.ap_id,
+      local_user_view.person.clone(),
+      Community::read(&mut context.pool(), post.community_id).await?,
+      data.score,
+    ),
+    &context,
+  )
+  .await?;
 
+  Ok(Json(
     build_post_response(
-      context,
+      context.deref(),
       post.community_id,
       local_user_view.person.id,
       post_id,
     )
-    .await
-  }
+    .await?,
+  ))
 }
diff --git a/crates/api/src/post/mod.rs b/crates/api/src/post/mod.rs
index 4d825135..a3b84134 100644
--- a/crates/api/src/post/mod.rs
+++ b/crates/api/src/post/mod.rs
@@ -1,6 +1,6 @@
-mod feature;
-mod get_link_metadata;
-mod like;
-mod lock;
-mod mark_read;
-mod save;
+pub mod feature;
+pub mod get_link_metadata;
+pub mod like;
+pub mod lock;
+pub mod mark_read;
+pub mod save;
diff --git a/crates/api_common/src/send_activity.rs b/crates/api_common/src/send_activity.rs
index 994aea2a..7954db28 100644
--- a/crates/api_common/src/send_activity.rs
+++ b/crates/api_common/src/send_activity.rs
@@ -1,7 +1,10 @@
 use crate::context::LemmyContext;
 use activitypub_federation::config::Data;
 use futures::future::BoxFuture;
-use lemmy_db_schema::source::{comment::Comment, post::Post};
+use lemmy_db_schema::{
+  newtypes::DbUrl,
+  source::{comment::Comment, community::Community, person::Person, post::Post},
+};
 use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION};
 use once_cell::sync::{Lazy, OnceCell};
 use tokio::{
@@ -22,7 +25,12 @@ pub static MATCH_OUTGOING_ACTIVITIES: OnceCell<MatchOutgoingActivitiesBoxed> = O
 #[derive(Debug)]
 pub enum SendActivityData {
   CreatePost(Post),
+  UpdatePost(Post),
   CreateComment(Comment),
+  DeleteComment(Comment, Person, Community),
+  RemoveComment(Comment, Person, Community, Option<String>),
+  UpdateComment(Comment),
+  LikePostOrComment(DbUrl, Person, Community, i16),
 }
 
 // TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs
index f334efe5..8b8afc8f 100644
--- a/crates/api_crud/src/comment/create.rs
+++ b/crates/api_crud/src/comment/create.rs
@@ -36,7 +36,6 @@ use lemmy_utils::{
     validation::is_valid_body_field,
   },
 };
-use std::ops::Deref;
 
 const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
 
@@ -196,7 +195,7 @@ pub async fn create_comment(
 
   Ok(Json(
     build_comment_response(
-      context.deref(),
+      &context,
       inserted_comment.id,
       Some(local_user_view),
       data.form_id.clone(),
diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs
index eba7d1de..10063c66 100644
--- a/crates/api_crud/src/comment/delete.rs
+++ b/crates/api_crud/src/comment/delete.rs
@@ -1,9 +1,10 @@
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
 use lemmy_api_common::{
   build_response::{build_comment_response, send_local_notifs},
   comment::{CommentResponse, DeleteComment},
   context::LemmyContext,
+  send_activity::{ActivityChannel, SendActivityData},
   utils::{check_community_ban, local_user_view_from_jwt},
 };
 use lemmy_db_schema::{
@@ -15,66 +16,75 @@ use lemmy_db_schema::{
 };
 use lemmy_db_views::structs::CommentView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
-use std::ops::Deref;
 
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for DeleteComment {
-  type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn delete_comment(
+  data: Json<DeleteComment>,
+  context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
 
-  #[tracing::instrument(skip(context))]
-  async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
-    let data: &DeleteComment = self;
-    let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
+  let comment_id = data.comment_id;
+  let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
 
-    let comment_id = data.comment_id;
-    let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+  // Dont delete it if its already been deleted.
+  if orig_comment.comment.deleted == data.deleted {
+    return Err(LemmyErrorType::CouldntUpdateComment)?;
+  }
 
-    // Dont delete it if its already been deleted.
-    if orig_comment.comment.deleted == data.deleted {
-      return Err(LemmyErrorType::CouldntUpdateComment)?;
-    }
+  check_community_ban(
+    local_user_view.person.id,
+    orig_comment.community.id,
+    &mut context.pool(),
+  )
+  .await?;
 
-    check_community_ban(
-      local_user_view.person.id,
-      orig_comment.community.id,
-      &mut context.pool(),
-    )
-    .await?;
+  // Verify that only the creator can delete
+  if local_user_view.person.id != orig_comment.creator.id {
+    return Err(LemmyErrorType::NoCommentEditAllowed)?;
+  }
 
-    // Verify that only the creator can delete
-    if local_user_view.person.id != orig_comment.creator.id {
-      return Err(LemmyErrorType::NoCommentEditAllowed)?;
-    }
+  // Do the delete
+  let deleted = data.deleted;
+  let updated_comment = Comment::update(
+    &mut context.pool(),
+    comment_id,
+    &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
+  )
+  .await
+  .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
 
-    // Do the delete
-    let deleted = data.deleted;
-    let updated_comment = Comment::update(
-      &mut context.pool(),
-      comment_id,
-      &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
-    )
-    .await
-    .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
+  let post_id = updated_comment.post_id;
+  let post = Post::read(&mut context.pool(), post_id).await?;
+  let recipient_ids = send_local_notifs(
+    vec![],
+    &updated_comment,
+    &local_user_view.person,
+    &post,
+    false,
+    &context,
+  )
+  .await?;
+  let updated_comment_id = updated_comment.id;
 
-    let post_id = updated_comment.post_id;
-    let post = Post::read(&mut context.pool(), post_id).await?;
-    let recipient_ids = send_local_notifs(
-      vec![],
-      &updated_comment,
-      &local_user_view.person,
-      &post,
-      false,
-      context,
-    )
-    .await?;
+  ActivityChannel::submit_activity(
+    SendActivityData::DeleteComment(
+      updated_comment,
+      local_user_view.person.clone(),
+      orig_comment.community,
+    ),
+    &context,
+  )
+  .await?;
 
+  Ok(Json(
     build_comment_response(
-      context.deref(),
-      updated_comment.id,
+      &context,
+      updated_comment_id,
       Some(local_user_view),
       None,
       recipient_ids,
     )
-    .await
-  }
+    .await?,
+  ))
 }
diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs
index 1a794dc5..4ade60df 100644
--- a/crates/api_crud/src/comment/read.rs
+++ b/crates/api_crud/src/comment/read.rs
@@ -7,7 +7,6 @@ use lemmy_api_common::{
 };
 use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_utils::error::LemmyError;
-use std::ops::Deref;
 
 #[tracing::instrument(skip(context))]
 pub async fn get_comment(
@@ -20,6 +19,6 @@ pub async fn get_comment(
   check_private_instance(&local_user_view, &local_site)?;
 
   Ok(Json(
-    build_comment_response(context.deref(), data.id, local_user_view, None, vec![]).await?,
+    build_comment_response(&context, data.id, local_user_view, None, vec![]).await?,
   ))
 }
diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs
index cfc3ccff..b2071298 100644
--- a/crates/api_crud/src/comment/remove.rs
+++ b/crates/api_crud/src/comment/remove.rs
@@ -1,9 +1,10 @@
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
 use lemmy_api_common::{
   build_response::{build_comment_response, send_local_notifs},
   comment::{CommentResponse, RemoveComment},
   context::LemmyContext,
+  send_activity::{ActivityChannel, SendActivityData},
   utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
 };
 use lemmy_db_schema::{
@@ -16,73 +17,83 @@ use lemmy_db_schema::{
 };
 use lemmy_db_views::structs::CommentView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
-use std::ops::Deref;
 
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for RemoveComment {
-  type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn remove_comment(
+  data: Json<RemoveComment>,
+  context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
 
-  #[tracing::instrument(skip(context))]
-  async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
-    let data: &RemoveComment = self;
-    let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
+  let comment_id = data.comment_id;
+  let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
 
-    let comment_id = data.comment_id;
-    let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+  check_community_ban(
+    local_user_view.person.id,
+    orig_comment.community.id,
+    &mut context.pool(),
+  )
+  .await?;
 
-    check_community_ban(
-      local_user_view.person.id,
-      orig_comment.community.id,
-      &mut context.pool(),
-    )
-    .await?;
+  // Verify that only a mod or admin can remove
+  is_mod_or_admin(
+    &mut context.pool(),
+    local_user_view.person.id,
+    orig_comment.community.id,
+  )
+  .await?;
 
-    // Verify that only a mod or admin can remove
-    is_mod_or_admin(
-      &mut context.pool(),
-      local_user_view.person.id,
-      orig_comment.community.id,
-    )
-    .await?;
+  // Do the remove
+  let removed = data.removed;
+  let updated_comment = Comment::update(
+    &mut context.pool(),
+    comment_id,
+    &CommentUpdateForm::builder().removed(Some(removed)).build(),
+  )
+  .await
+  .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
 
-    // Do the remove
-    let removed = data.removed;
-    let updated_comment = Comment::update(
-      &mut context.pool(),
-      comment_id,
-      &CommentUpdateForm::builder().removed(Some(removed)).build(),
-    )
-    .await
-    .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
+  // Mod tables
+  let form = ModRemoveCommentForm {
+    mod_person_id: local_user_view.person.id,
+    comment_id: data.comment_id,
+    removed: Some(removed),
+    reason: data.reason.clone(),
+  };
+  ModRemoveComment::create(&mut context.pool(), &form).await?;
 
-    // Mod tables
-    let form = ModRemoveCommentForm {
-      mod_person_id: local_user_view.person.id,
-      comment_id: data.comment_id,
-      removed: Some(removed),
-      reason: data.reason.clone(),
-    };
-    ModRemoveComment::create(&mut context.pool(), &form).await?;
+  let post_id = updated_comment.post_id;
+  let post = Post::read(&mut context.pool(), post_id).await?;
+  let recipient_ids = send_local_notifs(
+    vec![],
+    &updated_comment,
+    &local_user_view.person.clone(),
+    &post,
+    false,
+    &context,
+  )
+  .await?;
+  let updated_comment_id = updated_comment.id;
 
-    let post_id = updated_comment.post_id;
-    let post = Post::read(&mut context.pool(), post_id).await?;
-    let recipient_ids = send_local_notifs(
-      vec![],
-      &updated_comment,
-      &local_user_view.person.clone(),
-      &post,
-      false,
-      context,
-    )
-    .await?;
+  ActivityChannel::submit_activity(
+    SendActivityData::RemoveComment(
+      updated_comment,
+      local_user_view.person.clone(),
+      orig_comment.community,
+      data.reason.clone(),
+    ),
+    &context,
+  )
+  .await?;
 
+  Ok(Json(
     build_comment_response(
-      context.deref(),
-      updated_comment.id,
+      &context,
+      updated_comment_id,
       Some(local_user_view),
       None,
       recipient_ids,
     )
-    .await
-  }
+    .await?,
+  ))
 }
diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs
index 5d4d75a3..c7b82ba9 100644
--- a/crates/api_crud/src/comment/update.rs
+++ b/crates/api_crud/src/comment/update.rs
@@ -1,9 +1,10 @@
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
 use lemmy_api_common::{
   build_response::{build_comment_response, send_local_notifs},
   comment::{CommentResponse, EditComment},
   context::LemmyContext,
+  send_activity::{ActivityChannel, SendActivityData},
   utils::{
     check_community_ban,
     local_site_to_slur_regex,
@@ -29,79 +30,83 @@ use lemmy_utils::{
     validation::is_valid_body_field,
   },
 };
-use std::ops::Deref;
 
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for EditComment {
-  type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn update_comment(
+  data: Json<EditComment>,
+  context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
+  let local_site = LocalSite::read(&mut context.pool()).await?;
 
-  #[tracing::instrument(skip(context))]
-  async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
-    let data: &EditComment = self;
-    let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
-    let local_site = LocalSite::read(&mut context.pool()).await?;
+  let comment_id = data.comment_id;
+  let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
 
-    let comment_id = data.comment_id;
-    let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+  check_community_ban(
+    local_user_view.person.id,
+    orig_comment.community.id,
+    &mut context.pool(),
+  )
+  .await?;
 
-    check_community_ban(
-      local_user_view.person.id,
-      orig_comment.community.id,
-      &mut context.pool(),
-    )
-    .await?;
+  // Verify that only the creator can edit
+  if local_user_view.person.id != orig_comment.creator.id {
+    return Err(LemmyErrorType::NoCommentEditAllowed)?;
+  }
 
-    // Verify that only the creator can edit
-    if local_user_view.person.id != orig_comment.creator.id {
-      return Err(LemmyErrorType::NoCommentEditAllowed)?;
-    }
+  let language_id = data.language_id;
+  CommunityLanguage::is_allowed_community_language(
+    &mut context.pool(),
+    language_id,
+    orig_comment.community.id,
+  )
+  .await?;
 
-    let language_id = self.language_id;
-    CommunityLanguage::is_allowed_community_language(
-      &mut context.pool(),
-      language_id,
-      orig_comment.community.id,
-    )
-    .await?;
+  // Update the Content
+  let content = data
+    .content
+    .as_ref()
+    .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
+  is_valid_body_field(&content, false)?;
+  let content = sanitize_html_opt(&content);
 
-    // Update the Content
-    let content = data
-      .content
-      .as_ref()
-      .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
-    is_valid_body_field(&content, false)?;
-    let content = sanitize_html_opt(&content);
+  let comment_id = data.comment_id;
+  let form = CommentUpdateForm::builder()
+    .content(content)
+    .language_id(data.language_id)
+    .updated(Some(Some(naive_now())))
+    .build();
+  let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
+    .await
+    .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
 
-    let comment_id = data.comment_id;
-    let form = CommentUpdateForm::builder()
-      .content(content)
-      .language_id(data.language_id)
-      .updated(Some(Some(naive_now())))
-      .build();
-    let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
-      .await
-      .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
+  // Do the mentions / recipients
+  let updated_comment_content = updated_comment.content.clone();
+  let mentions = scrape_text_for_mentions(&updated_comment_content);
+  let recipient_ids = send_local_notifs(
+    mentions,
+    &updated_comment,
+    &local_user_view.person,
+    &orig_comment.post,
+    false,
+    &context,
+  )
+  .await?;
 
-    // Do the mentions / recipients
-    let updated_comment_content = updated_comment.content.clone();
-    let mentions = scrape_text_for_mentions(&updated_comment_content);
-    let recipient_ids = send_local_notifs(
-      mentions,
-      &updated_comment,
-      &local_user_view.person,
-      &orig_comment.post,
-      false,
-      context,
-    )
-    .await?;
+  ActivityChannel::submit_activity(
+    SendActivityData::UpdateComment(updated_comment.clone()),
+    &context,
+  )
+  .await?;
 
+  Ok(Json(
     build_comment_response(
-      context.deref(),
+      &context,
       updated_comment.id,
       Some(local_user_view),
-      self.form_id.clone(),
+      data.form_id.clone(),
       recipient_ids,
     )
-    .await
-  }
+    .await?,
+  ))
 }
diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs
index f3be5f6a..3341392b 100644
--- a/crates/api_crud/src/post/update.rs
+++ b/crates/api_crud/src/post/update.rs
@@ -1,10 +1,11 @@
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
 use lemmy_api_common::{
   build_response::build_post_response,
   context::LemmyContext,
   post::{EditPost, PostResponse},
   request::fetch_site_data,
+  send_activity::{ActivityChannel, SendActivityData},
   utils::{
     check_community_ban,
     local_site_to_slur_regex,
@@ -28,95 +29,97 @@ use lemmy_utils::{
     validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
   },
 };
+use std::ops::Deref;
 
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for EditPost {
-  type Response = PostResponse;
+#[tracing::instrument(skip(context))]
+pub async fn update_post(
+  data: Json<EditPost>,
+  context: Data<LemmyContext>,
+) -> Result<Json<PostResponse>, LemmyError> {
+  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
+  let local_site = LocalSite::read(&mut context.pool()).await?;
 
-  #[tracing::instrument(skip(context))]
-  async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
-    let data: &EditPost = self;
-    let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
-    let local_site = LocalSite::read(&mut context.pool()).await?;
+  let data_url = data.url.as_ref();
 
-    let data_url = data.url.as_ref();
+  // TODO No good way to handle a clear.
+  // Issue link: https://github.com/LemmyNet/lemmy/issues/2287
+  let url = Some(data_url.map(clean_url_params).map(Into::into));
 
-    // TODO No good way to handle a clear.
-    // Issue link: https://github.com/LemmyNet/lemmy/issues/2287
-    let url = Some(data_url.map(clean_url_params).map(Into::into));
+  let slur_regex = local_site_to_slur_regex(&local_site);
+  check_slurs_opt(&data.name, &slur_regex)?;
+  check_slurs_opt(&data.body, &slur_regex)?;
 
-    let slur_regex = local_site_to_slur_regex(&local_site);
-    check_slurs_opt(&data.name, &slur_regex)?;
-    check_slurs_opt(&data.body, &slur_regex)?;
+  if let Some(name) = &data.name {
+    is_valid_post_title(name)?;
+  }
 
-    if let Some(name) = &data.name {
-      is_valid_post_title(name)?;
-    }
+  is_valid_body_field(&data.body, true)?;
+  check_url_scheme(&data.url)?;
 
-    is_valid_body_field(&data.body, true)?;
-    check_url_scheme(&data.url)?;
+  let post_id = data.post_id;
+  let orig_post = Post::read(&mut context.pool(), post_id).await?;
 
-    let post_id = data.post_id;
-    let orig_post = Post::read(&mut context.pool(), post_id).await?;
+  check_community_ban(
+    local_user_view.person.id,
+    orig_post.community_id,
+    &mut context.pool(),
+  )
+  .await?;
 
-    check_community_ban(
-      local_user_view.person.id,
-      orig_post.community_id,
-      &mut context.pool(),
-    )
-    .await?;
-
-    // Verify that only the creator can edit
-    if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
-      return Err(LemmyErrorType::NoPostEditAllowed)?;
-    }
-
-    // Fetch post links and Pictrs cached image
-    let data_url = data.url.as_ref();
-    let (metadata_res, thumbnail_url) =
-      fetch_site_data(context.client(), context.settings(), data_url, true).await;
-    let (embed_title, embed_description, embed_video_url) = metadata_res
-      .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
-      .unwrap_or_default();
-
-    let name = sanitize_html_opt(&data.name);
-    let body = sanitize_html_opt(&data.body);
-    let body = diesel_option_overwrite(body);
-    let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
-    let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
-
-    let language_id = self.language_id;
-    CommunityLanguage::is_allowed_community_language(
-      &mut context.pool(),
-      language_id,
-      orig_post.community_id,
-    )
-    .await?;
-
-    let post_form = PostUpdateForm::builder()
-      .name(name)
-      .url(url)
-      .body(body)
-      .nsfw(data.nsfw)
-      .embed_title(embed_title)
-      .embed_description(embed_description)
-      .embed_video_url(embed_video_url)
-      .language_id(data.language_id)
-      .thumbnail_url(Some(thumbnail_url))
-      .updated(Some(Some(naive_now())))
-      .build();
-
-    let post_id = data.post_id;
-    Post::update(&mut context.pool(), post_id, &post_form)
-      .await
-      .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
+  // Verify that only the creator can edit
+  if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
+    return Err(LemmyErrorType::NoPostEditAllowed)?;
+  }
+
+  // Fetch post links and Pictrs cached image
+  let data_url = data.url.as_ref();
+  let (metadata_res, thumbnail_url) =
+    fetch_site_data(context.client(), context.settings(), data_url, true).await;
+  let (embed_title, embed_description, embed_video_url) = metadata_res
+    .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
+    .unwrap_or_default();
+
+  let name = sanitize_html_opt(&data.name);
+  let body = sanitize_html_opt(&data.body);
+  let body = diesel_option_overwrite(body);
+  let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
+  let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
+
+  let language_id = data.language_id;
+  CommunityLanguage::is_allowed_community_language(
+    &mut context.pool(),
+    language_id,
+    orig_post.community_id,
+  )
+  .await?;
+
+  let post_form = PostUpdateForm::builder()
+    .name(name)
+    .url(url)
+    .body(body)
+    .nsfw(data.nsfw)
+    .embed_title(embed_title)
+    .embed_description(embed_description)
+    .embed_video_url(embed_video_url)
+    .language_id(data.language_id)
+    .thumbnail_url(Some(thumbnail_url))
+    .updated(Some(Some(naive_now())))
+    .build();
+
+  let post_id = data.post_id;
+  let updated_post = Post::update(&mut context.pool(), post_id, &post_form)
+    .await
+    .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
 
+  ActivityChannel::submit_activity(SendActivityData::UpdatePost(updated_post), &context).await?;
+
+  Ok(Json(
     build_post_response(
-      context,
+      context.deref(),
       orig_post.community_id,
       local_user_view.person.id,
       post_id,
     )
-    .await
-  }
+    .await?,
+  ))
 }
diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs
index fa235a7f..d54ca309 100644
--- a/crates/apub/src/activities/create_or_update/comment.rs
+++ b/crates/apub/src/activities/create_or_update/comment.rs
@@ -14,7 +14,6 @@ use crate::{
     activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
     InCommunity,
   },
-  SendActivity,
 };
 use activitypub_federation::{
   config::Data,
@@ -25,7 +24,6 @@ use activitypub_federation::{
 };
 use lemmy_api_common::{
   build_response::send_local_notifs,
-  comment::{CommentResponse, EditComment},
   context::LemmyContext,
   utils::{check_post_deleted_or_removed, is_mod_or_admin},
 };
@@ -43,25 +41,6 @@ use lemmy_db_schema::{
 use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions};
 use url::Url;
 
-#[async_trait::async_trait]
-impl SendActivity for EditComment {
-  type Response = CommentResponse;
-
-  async fn send_activity(
-    _request: &Self,
-    response: &Self::Response,
-    context: &Data<LemmyContext>,
-  ) -> Result<(), LemmyError> {
-    CreateOrUpdateNote::send(
-      response.comment_view.comment.clone(),
-      response.comment_view.creator.id,
-      CreateOrUpdateType::Update,
-      context.reset_request_count(),
-    )
-    .await
-  }
-}
-
 impl CreateOrUpdateNote {
   #[tracing::instrument(skip(comment, person_id, kind, context))]
   pub(crate) async fn send(
diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs
index 4767114f..a9ac7936 100644
--- a/crates/apub/src/activities/create_or_update/post.rs
+++ b/crates/apub/src/activities/create_or_update/post.rs
@@ -14,7 +14,6 @@ use crate::{
     activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
     InCommunity,
   },
-  SendActivity,
 };
 use activitypub_federation::{
   config::Data,
@@ -22,10 +21,7 @@ use activitypub_federation::{
   protocol::verification::{verify_domains_match, verify_urls_match},
   traits::{ActivityHandler, Actor, Object},
 };
-use lemmy_api_common::{
-  context::LemmyContext,
-  post::{EditPost, PostResponse},
-};
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   aggregates::structs::PostAggregates,
   newtypes::PersonId,
@@ -39,25 +35,6 @@ use lemmy_db_schema::{
 use lemmy_utils::error::{LemmyError, LemmyErrorType};
 use url::Url;
 
-#[async_trait::async_trait]
-impl SendActivity for EditPost {
-  type Response = PostResponse;
-
-  async fn send_activity(
-    _request: &Self,
-    response: &Self::Response,
-    context: &Data<LemmyContext>,
-  ) -> Result<(), LemmyError> {
-    CreateOrUpdatePage::send(
-      response.post_view.post.clone(),
-      response.post_view.creator.id,
-      CreateOrUpdateType::Update,
-      context.reset_request_count(),
-    )
-    .await
-  }
-}
-
 impl CreateOrUpdatePage {
   pub(crate) async fn new(
     post: ApubPost,
diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs
index 3b8c8b53..c571ac22 100644
--- a/crates/apub/src/activities/deletion/mod.rs
+++ b/crates/apub/src/activities/deletion/mod.rs
@@ -29,7 +29,6 @@ use activitypub_federation::{
   traits::{Actor, Object},
 };
 use lemmy_api_common::{
-  comment::{CommentResponse, DeleteComment, RemoveComment},
   community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
   context::LemmyContext,
   post::{DeletePost, PostResponse, RemovePost},
@@ -102,50 +101,6 @@ impl SendActivity for RemovePost {
   }
 }
 
-#[async_trait::async_trait]
-impl SendActivity for DeleteComment {
-  type Response = CommentResponse;
-
-  async fn send_activity(
-    request: &Self,
-    response: &Self::Response,
-    context: &Data<LemmyContext>,
-  ) -> Result<(), LemmyError> {
-    let community_id = response.comment_view.community.id;
-    let community = Community::read(&mut context.pool(), community_id).await?;
-    let person = Person::read(&mut context.pool(), response.comment_view.creator.id).await?;
-    let deletable = DeletableObjects::Comment(response.comment_view.comment.clone().into());
-    send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
-      .await
-  }
-}
-
-#[async_trait::async_trait]
-impl SendActivity for RemoveComment {
-  type Response = CommentResponse;
-
-  async fn send_activity(
-    request: &Self,
-    response: &Self::Response,
-    context: &Data<LemmyContext>,
-  ) -> Result<(), LemmyError> {
-    let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
-    let comment = Comment::read(&mut context.pool(), request.comment_id).await?;
-    let community =
-      Community::read(&mut context.pool(), response.comment_view.community.id).await?;
-    let deletable = DeletableObjects::Comment(comment.into());
-    send_apub_delete_in_community(
-      local_user_view.person,
-      community,
-      deletable,
-      request.reason.clone().or_else(|| Some(String::new())),
-      request.removed,
-      context,
-    )
-    .await
-  }
-}
-
 #[async_trait::async_trait]
 impl SendActivity for DeletePrivateMessage {
   type Response = PrivateMessageResponse;
@@ -217,7 +172,7 @@ impl SendActivity for RemoveCommunity {
 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
 /// action was done by a normal user.
 #[tracing::instrument(skip_all)]
-async fn send_apub_delete_in_community(
+pub(crate) async fn send_apub_delete_in_community(
   actor: Person,
   community: Community,
   object: DeletableObjects,
diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs
index c7d19e37..c577f8bc 100644
--- a/crates/apub/src/activities/mod.rs
+++ b/crates/apub/src/activities/mod.rs
@@ -1,4 +1,8 @@
 use crate::{
+  activities::{
+    deletion::{send_apub_delete_in_community, DeletableObjects},
+    voting::send_like_activity,
+  },
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::{
     create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
@@ -222,15 +226,30 @@ pub async fn match_outgoing_activities(
 ) -> LemmyResult<()> {
   let context = context.reset_request_count();
   let fed_task = async {
+    use SendActivityData::*;
     match data {
-      SendActivityData::CreatePost(post) => {
+      CreatePost(post) | UpdatePost(post) => {
         let creator_id = post.creator_id;
         CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await
       }
-      SendActivityData::CreateComment(comment) => {
+      CreateComment(comment) | UpdateComment(comment) => {
         let creator_id = comment.creator_id;
         CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await
       }
+      DeleteComment(comment, actor, community) => {
+        let is_deleted = comment.deleted;
+        let deletable = DeletableObjects::Comment(comment.into());
+        send_apub_delete_in_community(actor, community, deletable, None, is_deleted, &context).await
+      }
+      RemoveComment(comment, actor, community, reason) => {
+        let is_removed = comment.removed;
+        let deletable = DeletableObjects::Comment(comment.into());
+        send_apub_delete_in_community(actor, community, deletable, reason, is_removed, &context)
+          .await
+      }
+      LikePostOrComment(object_id, person, community, score) => {
+        send_like_activity(object_id, person, community, score, context).await
+      }
     }
   };
   if *SYNCHRONOUS_FEDERATION {
diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs
index 24250c50..742a4407 100644
--- a/crates/apub/src/activities/voting/mod.rs
+++ b/crates/apub/src/activities/voting/mod.rs
@@ -2,106 +2,51 @@ use crate::{
   activities::community::send_activity_in_community,
   activity_lists::AnnouncableActivities,
   fetcher::post_or_comment::PostOrComment,
-  objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
+  objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
   protocol::activities::voting::{
     undo_vote::UndoVote,
     vote::{Vote, VoteType},
   },
-  SendActivity,
 };
 use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
-use lemmy_api_common::{
-  comment::{CommentResponse, CreateCommentLike},
-  context::LemmyContext,
-  post::{CreatePostLike, PostResponse},
-  sensitive::Sensitive,
-  utils::local_user_view_from_jwt,
-};
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
-  newtypes::CommunityId,
+  newtypes::DbUrl,
   source::{
     comment::{CommentLike, CommentLikeForm},
     community::Community,
     person::Person,
     post::{PostLike, PostLikeForm},
   },
-  traits::{Crud, Likeable},
+  traits::Likeable,
 };
 use lemmy_utils::error::LemmyError;
 
 pub mod undo_vote;
 pub mod vote;
 
-#[async_trait::async_trait]
-impl SendActivity for CreatePostLike {
-  type Response = PostResponse;
-
-  async fn send_activity(
-    request: &Self,
-    response: &Self::Response,
-    context: &Data<LemmyContext>,
-  ) -> Result<(), LemmyError> {
-    let object_id = ObjectId::from(response.post_view.post.ap_id.clone());
-    let community_id = response.post_view.community.id;
-    send_activity(
-      object_id,
-      community_id,
-      request.score,
-      &request.auth,
-      context,
-    )
-    .await
-  }
-}
-
-#[async_trait::async_trait]
-impl SendActivity for CreateCommentLike {
-  type Response = CommentResponse;
-
-  async fn send_activity(
-    request: &Self,
-    response: &Self::Response,
-    context: &Data<LemmyContext>,
-  ) -> Result<(), LemmyError> {
-    let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone());
-    let community_id = response.comment_view.community.id;
-    send_activity(
-      object_id,
-      community_id,
-      request.score,
-      &request.auth,
-      context,
-    )
-    .await
-  }
-}
-
-async fn send_activity(
-  object_id: ObjectId<PostOrComment>,
-  community_id: CommunityId,
+pub(crate) async fn send_like_activity(
+  object_id: DbUrl,
+  actor: Person,
+  community: Community,
   score: i16,
-  jwt: &Sensitive<String>,
-  context: &Data<LemmyContext>,
+  context: Data<LemmyContext>,
 ) -> Result<(), LemmyError> {
-  let community = Community::read(&mut context.pool(), community_id)
-    .await?
-    .into();
-  let local_user_view = local_user_view_from_jwt(jwt, context).await?;
-  let actor = Person::read(&mut context.pool(), local_user_view.person.id)
-    .await?
-    .into();
+  let object_id: ObjectId<PostOrComment> = object_id.try_into()?;
+  let actor: ApubPerson = actor.into();
+  let community: ApubCommunity = community.into();
 
   // score of 1 means upvote, -1 downvote, 0 undo a previous vote
   if score != 0 {
-    let vote = Vote::new(object_id, &actor, &community, score.try_into()?, context)?;
+    let vote = Vote::new(object_id, &actor, &community, score.try_into()?, &context)?;
     let activity = AnnouncableActivities::Vote(vote);
-    send_activity_in_community(activity, &actor, &community, vec![], false, context).await
+    send_activity_in_community(activity, &actor, &community, vec![], false, &context).await
   } else {
     // Lemmy API doesnt distinguish between Undo/Like and Undo/Dislike, so we hardcode it here.
-    let vote = Vote::new(object_id, &actor, &community, VoteType::Like, context)?;
-    let undo_vote = UndoVote::new(vote, &actor, &community, context)?;
+    let vote = Vote::new(object_id, &actor, &community, VoteType::Like, &context)?;
+    let undo_vote = UndoVote::new(vote, &actor, &community, &context)?;
     let activity = AnnouncableActivities::UndoVote(undo_vote);
-    send_activity_in_community(activity, &actor, &community, vec![], false, context).await
+    send_activity_in_community(activity, &actor, &community, vec![], false, &context).await
   }
 }
 
diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs
index e372e340..6d538e63 100644
--- a/src/api_routes_http.rs
+++ b/src/api_routes_http.rs
@@ -1,12 +1,13 @@
 use actix_web::{guard, web, Error, HttpResponse, Result};
 use lemmy_api::{
-  comment::{distinguish::distinguish_comment, save::save_comment},
+  comment::{distinguish::distinguish_comment, like::like_comment, save::save_comment},
   comment_report::{list::list_comment_reports, resolve::resolve_comment_report},
   local_user::notifications::mark_reply_read::mark_reply_as_read,
+  post::like::like_post,
   Perform,
 };
 use lemmy_api_common::{
-  comment::{CreateCommentLike, CreateCommentReport, DeleteComment, EditComment, RemoveComment},
+  comment::CreateCommentReport,
   community::{
     AddModToCommunity,
     BanFromCommunity,
@@ -43,10 +44,8 @@ use lemmy_api_common::{
     VerifyEmail,
   },
   post::{
-    CreatePostLike,
     CreatePostReport,
     DeletePost,
-    EditPost,
     FeaturePost,
     GetSiteMetadata,
     ListPostReports,
@@ -79,9 +78,15 @@ use lemmy_api_common::{
   },
 };
 use lemmy_api_crud::{
-  comment::{create::create_comment, read::get_comment},
+  comment::{
+    create::create_comment,
+    delete::delete_comment,
+    read::get_comment,
+    remove::remove_comment,
+    update::update_comment,
+  },
   community::list::list_communities,
-  post::{create::create_post, read::get_post},
+  post::{create::create_post, read::get_post, update::update_post},
   private_message::read::get_private_message,
   site::{create::create_site, read::get_site, update::update_site},
   PerformCrud,
@@ -173,7 +178,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
         web::scope("/post")
           .wrap(rate_limit.message())
           .route("", web::get().to(get_post))
-          .route("", web::put().to(route_post_crud::<EditPost>))
+          .route("", web::put().to(update_post))
           .route("/delete", web::post().to(route_post_crud::<DeletePost>))
           .route("/remove", web::post().to(route_post_crud::<RemovePost>))
           .route(
@@ -183,7 +188,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
           .route("/lock", web::post().to(route_post::<LockPost>))
           .route("/feature", web::post().to(route_post::<FeaturePost>))
           .route("/list", web::get().to(list_posts))
-          .route("/like", web::post().to(route_post::<CreatePostLike>))
+          .route("/like", web::post().to(like_post))
           .route("/save", web::put().to(route_post::<SavePost>))
           .route("/report", web::post().to(route_post::<CreatePostReport>))
           .route(
@@ -208,12 +213,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
         web::scope("/comment")
           .wrap(rate_limit.message())
           .route("", web::get().to(get_comment))
-          .route("", web::put().to(route_post_crud::<EditComment>))
-          .route("/delete", web::post().to(route_post_crud::<DeleteComment>))
-          .route("/remove", web::post().to(route_post_crud::<RemoveComment>))
+          .route("", web::put().to(update_comment))
+          .route("/delete", web::post().to(delete_comment))
+          .route("/remove", web::post().to(remove_comment))
           .route("/mark_as_read", web::post().to(mark_reply_as_read))
           .route("/distinguish", web::post().to(distinguish_comment))
-          .route("/like", web::post().to(route_post::<CreateCommentLike>))
+          .route("/like", web::post().to(like_comment))
           .route("/save", web::put().to(save_comment))
           .route("/list", web::get().to(list_comments))
           .route("/report", web::post().to(route_post::<CreateCommentReport>))