From c6c52ab9ccde330b5012f8d0ce4fc8f26628d5cc Mon Sep 17 00:00:00 2001
From: Felix Ableitner <me@nutomic.com>
Date: Mon, 28 Nov 2022 15:29:33 +0100
Subject: [PATCH] Add SendActivity trait so that api crates compile in parallel
 with lemmy_apub

---
 Cargo.lock                                    |   4 +-
 Cargo.toml                                    |   1 +
 api_tests/src/community.spec.ts               |   7 +-
 api_tests/src/follow.spec.ts                  |   9 +-
 crates/api/Cargo.toml                         |   2 -
 crates/api/src/comment/like.rs                |  31 +-
 crates/api/src/comment/save.rs                |   2 +-
 crates/api/src/comment_report/create.rs       |  13 +-
 crates/api/src/comment_report/list.rs         |   2 +-
 crates/api/src/comment_report/resolve.rs      |   2 +-
 crates/api/src/community/add_mod.rs           |  29 +-
 crates/api/src/community/ban.rs               |  31 +-
 crates/api/src/community/block.rs             |   9 +-
 crates/api/src/community/follow.rs            |  38 +-
 crates/api/src/community/hide.rs              |  12 +-
 crates/api/src/community/transfer.rs          |   2 +-
 crates/api/src/lib.rs                         | 253 +---------
 crates/api/src/local_user/add_admin.rs        |   2 +-
 crates/api/src/local_user/ban_person.rs       |  33 +-
 crates/api/src/local_user/block.rs            |   2 +-
 crates/api/src/local_user/change_password.rs  |   2 +-
 .../local_user/change_password_after_reset.rs |   2 +-
 crates/api/src/local_user/get_captcha.rs      |   2 +-
 crates/api/src/local_user/list_banned.rs      |   2 +-
 crates/api/src/local_user/login.rs            |   2 +-
 .../local_user/notifications/list_mentions.rs |   2 +-
 .../local_user/notifications/list_replies.rs  |   2 +-
 .../local_user/notifications/mark_all_read.rs |   2 +-
 .../notifications/mark_mention_read.rs        |   2 +-
 .../notifications/mark_reply_read.rs          |   2 +-
 .../local_user/notifications/unread_count.rs  |   2 +-
 crates/api/src/local_user/report_count.rs     |   2 +-
 crates/api/src/local_user/reset_password.rs   |   2 +-
 crates/api/src/local_user/save_settings.rs    |   2 +-
 crates/api/src/local_user/verify_email.rs     |   2 +-
 crates/api/src/post/get_link_metadata.rs      |   2 +-
 crates/api/src/post/like.rs                   |  34 +-
 crates/api/src/post/lock.rs                   |  20 +-
 crates/api/src/post/mark_read.rs              |   2 +-
 crates/api/src/post/save.rs                   |   2 +-
 crates/api/src/post/sticky.rs                 |  21 +-
 crates/api/src/post_report/create.rs          |  13 +-
 crates/api/src/post_report/list.rs            |   2 +-
 crates/api/src/post_report/resolve.rs         |   2 +-
 crates/api/src/private_message/mark_read.rs   |   2 +-
 .../api/src/private_message_report/create.rs  |   2 +-
 crates/api/src/private_message_report/list.rs |   2 +-
 .../api/src/private_message_report/resolve.rs |   2 +-
 crates/api/src/site/leave_admin.rs            |   2 +-
 crates/api/src/site/mod.rs                    |   2 -
 crates/api/src/site/mod_log.rs                |   2 +-
 crates/api/src/site/purge/comment.rs          |   2 +-
 crates/api/src/site/purge/community.rs        |   2 +-
 crates/api/src/site/purge/person.rs           |   2 +-
 crates/api/src/site/purge/post.rs             |   2 +-
 .../site/registration_applications/approve.rs |   2 +-
 .../site/registration_applications/list.rs    |   2 +-
 .../registration_applications/unread_count.rs |   2 +-
 crates/api/src/websocket.rs                   |   2 +-
 crates/api_common/src/context.rs              |  68 +++
 crates/api_common/src/lib.rs                  | 138 +-----
 crates/api_common/src/person.rs               |   2 +-
 crates/api_common/src/utils.rs                |  67 ++-
 .../api_common/src/websocket/chat_server.rs   |  42 +-
 crates/api_common/src/websocket/mod.rs        |  18 +-
 crates/api_common/src/websocket/routes.rs     |   2 +-
 crates/api_common/src/websocket/send.rs       |   2 +-
 crates/api_crud/Cargo.toml                    |   1 -
 crates/api_crud/src/comment/create.rs         |  20 +-
 crates/api_crud/src/comment/delete.rs         |  17 +-
 crates/api_crud/src/comment/mod.rs            |   1 -
 crates/api_crud/src/comment/read.rs           |   2 +-
 crates/api_crud/src/comment/remove.rs         |  17 +-
 crates/api_crud/src/comment/update.rs         |  16 +-
 crates/api_crud/src/community/create.rs       |  28 +-
 crates/api_crud/src/community/delete.rs       |  17 +-
 crates/api_crud/src/community/list.rs         |   2 +-
 crates/api_crud/src/community/mod.rs          |   1 -
 crates/api_crud/src/community/remove.rs       |  16 +-
 crates/api_crud/src/community/update.rs       |  12 +-
 crates/api_crud/src/lib.rs                    | 138 +-----
 crates/api_crud/src/post/create.rs            |  19 +-
 crates/api_crud/src/post/delete.rs            |  22 +-
 crates/api_crud/src/post/mod.rs               |   1 -
 crates/api_crud/src/post/read.rs              |   2 +-
 crates/api_crud/src/post/remove.rs            |  18 +-
 crates/api_crud/src/post/update.rs            |  36 +-
 crates/api_crud/src/private_message/create.rs |  20 +-
 crates/api_crud/src/private_message/delete.rs |  14 +-
 crates/api_crud/src/private_message/read.rs   |   2 +-
 crates/api_crud/src/private_message/update.rs |  17 +-
 crates/api_crud/src/site/create.rs            |   4 +-
 crates/api_crud/src/site/read.rs              |   2 +-
 crates/api_crud/src/site/update.rs            |   2 +-
 crates/api_crud/src/user/create.rs            |  10 +-
 crates/api_crud/src/user/delete.rs            |  14 +-
 crates/api_crud/src/user/mod.rs               |   1 -
 .../apub/src/activities/block/block_user.rs   |   2 +-
 crates/apub/src/activities/block/mod.rs       | 111 ++++-
 .../src/activities/block/undo_block_user.rs   |   2 +-
 .../apub/src/activities/community/add_mod.rs  |  52 ++-
 .../apub/src/activities/community/announce.rs |   2 +-
 crates/apub/src/activities/community/mod.rs   |   2 +-
 .../src/activities/community/remove_mod.rs    |   2 +-
 .../apub/src/activities/community/report.rs   |  54 ++-
 .../apub/src/activities/community/update.rs   |  37 +-
 .../activities/create_or_update/comment.rs    |  64 ++-
 .../src/activities/create_or_update/mod.rs    |   2 +-
 .../src/activities/create_or_update/post.rs   | 110 ++++-
 .../create_or_update/private_message.rs       |  62 ++-
 crates/apub/src/activities/deletion/delete.rs |   2 +-
 .../src/activities/deletion/delete_user.rs    |  70 ++-
 crates/apub/src/activities/deletion/mod.rs    | 178 ++++++-
 .../src/activities/deletion/undo_delete.rs    |   2 +-
 .../apub/src/activities/following/accept.rs   |   2 +-
 .../apub/src/activities/following/follow.rs   |  33 +-
 crates/apub/src/activities/following/mod.rs   |  38 ++
 .../src/activities/following/undo_follow.rs   |   2 +-
 crates/apub/src/activities/mod.rs             |   3 +-
 crates/apub/src/activities/unfederated.rs     | 351 ++++++++++++++
 crates/apub/src/activities/voting/mod.rs      |  92 +++-
 .../apub/src/activities/voting/undo_vote.rs   |  43 +-
 crates/apub/src/activities/voting/vote.rs     |  31 +-
 crates/apub/src/activity_lists.rs             |   2 +-
 .../list.rs => apub/src/api/list_comments.rs} |  11 +-
 .../list.rs => apub/src/api/list_posts.rs}    |  11 +-
 crates/apub/src/api/mod.rs                    |  21 +
 .../src/api/read_community.rs}                |  17 +-
 .../read.rs => apub/src/api/read_person.rs}   |   7 +-
 .../site => apub/src/api}/resolve_object.rs   |  10 +-
 .../{api/src/site => apub/src/api}/search.rs  |  11 +-
 .../src/collections/community_moderators.rs   |   2 +-
 .../apub/src/collections/community_outbox.rs  |   2 +-
 crates/apub/src/collections/mod.rs            |   2 +-
 crates/apub/src/fetcher/mod.rs                |   2 +-
 crates/apub/src/fetcher/post_or_comment.rs    |  12 +-
 crates/apub/src/fetcher/search.rs             |   8 +-
 crates/apub/src/fetcher/user_or_community.rs  |   2 +-
 crates/apub/src/fetcher/webfinger.rs          |   3 +-
 crates/apub/src/http/comment.rs               |   2 +-
 crates/apub/src/http/community.rs             |   2 +-
 crates/apub/src/http/mod.rs                   |   2 +-
 crates/apub/src/http/person.rs                |   2 +-
 crates/apub/src/http/post.rs                  |   2 +-
 crates/apub/src/http/site.rs                  |   2 +-
 crates/apub/src/lib.rs                        |  16 +-
 crates/apub/src/mentions.rs                   |   2 +-
 crates/apub/src/objects/comment.rs            |   4 +-
 crates/apub/src/objects/community.rs          |   5 +-
 crates/apub/src/objects/instance.rs           |  13 +-
 crates/apub/src/objects/mod.rs                |   3 +-
 crates/apub/src/objects/person.rs             |   5 +-
 crates/apub/src/objects/post.rs               |   4 +-
 crates/apub/src/objects/private_message.rs    |   4 +-
 .../protocol/activities/block/block_user.rs   |   2 +-
 .../activities/block/undo_block_user.rs       |   2 +-
 .../protocol/activities/community/add_mod.rs  |   2 +-
 .../activities/community/remove_mod.rs        |   2 +-
 .../protocol/activities/community/report.rs   |   2 +-
 .../protocol/activities/community/update.rs   |   2 +-
 .../activities/create_or_update/note.rs       |   2 +-
 .../activities/create_or_update/page.rs       |   2 +-
 .../protocol/activities/deletion/delete.rs    |   2 +-
 .../activities/deletion/undo_delete.rs        |   2 +-
 .../protocol/activities/voting/undo_vote.rs   |   2 +-
 .../src/protocol/activities/voting/vote.rs    |   2 +-
 .../protocol/collections/group_followers.rs   |   2 +-
 crates/apub/src/protocol/mod.rs               |   2 +-
 crates/apub/src/protocol/objects/group.rs     |   2 +-
 crates/apub/src/protocol/objects/note.rs      |   2 +-
 crates/apub/src/protocol/objects/page.rs      |   2 +-
 crates/db_schema/src/impls/site.rs            |   9 +
 crates/routes/Cargo.toml                      |   2 +-
 crates/routes/src/feeds.rs                    |   2 +-
 crates/routes/src/images.rs                   |   2 +-
 crates/routes/src/nodeinfo.rs                 |   2 +-
 crates/routes/src/webfinger.rs                |   2 +-
 src/api_routes.rs                             | 435 ++++++++++++++++--
 src/code_migrations.rs                        |  14 +-
 src/main.rs                                   |  10 +-
 180 files changed, 2106 insertions(+), 1399 deletions(-)
 create mode 100644 crates/api_common/src/context.rs
 create mode 100644 crates/apub/src/activities/unfederated.rs
 rename crates/{api_crud/src/comment/list.rs => apub/src/api/list_comments.rs} (94%)
 rename crates/{api_crud/src/post/list.rs => apub/src/api/list_posts.rs} (93%)
 create mode 100644 crates/apub/src/api/mod.rs
 rename crates/{api_crud/src/community/read.rs => apub/src/api/read_community.rs} (92%)
 rename crates/{api_crud/src/user/read.rs => apub/src/api/read_person.rs} (95%)
 rename crates/{api/src/site => apub/src/api}/resolve_object.rs (93%)
 rename crates/{api/src/site => apub/src/api}/search.rs (97%)

diff --git a/Cargo.lock b/Cargo.lock
index 2d1f70bc6..5f68a511e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2034,7 +2034,6 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 name = "lemmy_api"
 version = "0.16.5"
 dependencies = [
- "activitypub_federation",
  "actix-web",
  "anyhow",
  "async-trait",
@@ -2044,7 +2043,6 @@ dependencies = [
  "chrono",
  "diesel",
  "lemmy_api_common",
- "lemmy_apub",
  "lemmy_db_schema",
  "lemmy_db_views",
  "lemmy_db_views_actor",
@@ -2103,7 +2101,6 @@ dependencies = [
  "async-trait",
  "bcrypt",
  "lemmy_api_common",
- "lemmy_apub",
  "lemmy_db_schema",
  "lemmy_db_views",
  "lemmy_db_views_actor",
@@ -2274,6 +2271,7 @@ dependencies = [
  "reqwest-retry",
  "reqwest-tracing",
  "serde",
+ "serde_json",
  "tracing",
  "tracing-actix-web",
  "tracing-error",
diff --git a/Cargo.toml b/Cargo.toml
index 16ca864f4..e83f0c837 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -135,6 +135,7 @@ clokwerk = { workspace = true }
 doku = { workspace = true }
 parking_lot = { workspace = true }
 reqwest-retry = { workspace = true }
+serde_json = { workspace = true }
 tracing-opentelemetry = { workspace = true, optional = true }
 opentelemetry = { workspace = true, optional = true }
 console-subscriber = { version = "0.1.8", optional = true }
diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts
index 0ed50ec48..453ea5660 100644
--- a/api_tests/src/community.spec.ts
+++ b/api_tests/src/community.spec.ts
@@ -187,12 +187,15 @@ test("Admin actions in remote community are not federated to origin", async () =
   let gammaCommunity = (
     await resolveCommunity(gamma, communityRes.community.actor_id)
   ).community.unwrap();
-  let gammaFollow = await followCommunity(
+  await followCommunity(
     gamma,
     true,
     gammaCommunity.community.id
   );
-  expect(gammaFollow.community_view.subscribed).toBe("Subscribed");
+  gammaCommunity = (
+    await resolveCommunity(gamma, communityRes.community.actor_id)
+  ).community.unwrap();
+  expect(gammaCommunity.subscribed).toBe("Subscribed");
   let gammaPost = (await createPost(gamma, gammaCommunity.community.id))
     .post_view;
   expect(gammaPost.post.id).toBeDefined();
diff --git a/api_tests/src/follow.spec.ts b/api_tests/src/follow.spec.ts
index f80b40de8..630e25f22 100644
--- a/api_tests/src/follow.spec.ts
+++ b/api_tests/src/follow.spec.ts
@@ -20,12 +20,13 @@ afterAll(async () => {
 
 test("Follow federated community", async () => {
   let betaCommunity = (await resolveBetaCommunity(alpha)).community.unwrap();
-  let follow = await followCommunity(alpha, true, betaCommunity.community.id);
+  await followCommunity(alpha, true, betaCommunity.community.id);
+  betaCommunity = (await resolveBetaCommunity(alpha)).community.unwrap();
 
   // Make sure the follow response went through
-  expect(follow.community_view.community.local).toBe(false);
-  expect(follow.community_view.community.name).toBe("main");
-  expect(follow.community_view.subscribed).toBe(SubscribedType.Subscribed);
+  expect(betaCommunity.community.local).toBe(false);
+  expect(betaCommunity.community.name).toBe("main");
+  expect(betaCommunity.subscribed).toBe(SubscribedType.Subscribed);
 
   // Check it from local
   let site = await getSite(alpha);
diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml
index 17c0127b4..f7f1b0bf3 100644
--- a/crates/api/Cargo.toml
+++ b/crates/api/Cargo.toml
@@ -14,14 +14,12 @@ path = "src/lib.rs"
 doctest = false
 
 [dependencies]
-lemmy_apub = { workspace = true }
 lemmy_utils = { workspace = true }
 lemmy_db_schema = { workspace = true, features = ["full"] }
 lemmy_db_views = { workspace = true, features = ["full"] }
 lemmy_db_views_moderator = { workspace = true, features = ["full"] }
 lemmy_db_views_actor = { workspace = true, features = ["full"] }
 lemmy_api_common = { workspace = true, features = ["full"] }
-activitypub_federation = { workspace = true }
 diesel = { workspace = true }
 bcrypt = { workspace = true }
 chrono = { workspace = true }
diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs
index 1ba6ec791..fba61ed13 100644
--- a/crates/api/src/comment/like.rs
+++ b/crates/api/src/comment/like.rs
@@ -2,16 +2,9 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentResponse, CreateCommentLike},
+  context::LemmyContext,
   utils::{check_community_ban, check_downvotes_enabled, get_local_user_view_from_jwt},
   websocket::{send::send_comment_ws_message, UserOperation},
-  LemmyContext,
-};
-use lemmy_apub::{
-  fetcher::post_or_comment::PostOrComment,
-  protocol::activities::voting::{
-    undo_vote::UndoVote,
-    vote::{Vote, VoteType},
-  },
 };
 use lemmy_db_schema::{
   newtypes::LocalUserId,
@@ -24,7 +17,6 @@ use lemmy_db_schema::{
 };
 use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::{error::LemmyError, ConnectionId};
-use std::convert::TryInto;
 
 #[async_trait::async_trait(?Send)]
 impl Perform for CreateCommentLike {
@@ -78,33 +70,12 @@ impl Perform for CreateCommentLike {
     CommentLike::remove(context.pool(), person_id, comment_id).await?;
 
     // Only add the like if the score isnt 0
-    let comment = orig_comment.comment;
-    let object = PostOrComment::Comment(Box::new(comment.into()));
     let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
     if do_add {
       let like_form2 = like_form.clone();
       CommentLike::like(context.pool(), &like_form2)
         .await
         .map_err(|e| LemmyError::from_error_message(e, "couldnt_like_comment"))?;
-
-      Vote::send(
-        &object,
-        &local_user_view.person.clone().into(),
-        orig_comment.community.id,
-        like_form.score.try_into()?,
-        context,
-      )
-      .await?;
-    } else {
-      // API doesn't distinguish between Undo/Like and Undo/Dislike
-      UndoVote::send(
-        &object,
-        &local_user_view.person.clone().into(),
-        orig_comment.community.id,
-        VoteType::Like,
-        context,
-      )
-      .await?;
     }
 
     send_comment_ws_message(
diff --git a/crates/api/src/comment/save.rs b/crates/api/src/comment/save.rs
index cc6955de0..03051f6cc 100644
--- a/crates/api/src/comment/save.rs
+++ b/crates/api/src/comment/save.rs
@@ -2,8 +2,8 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentResponse, SaveComment},
+  context::LemmyContext,
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::comment::{CommentSaved, CommentSavedForm},
diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs
index 99c653e23..bf3fec0a1 100644
--- a/crates/api/src/comment_report/create.rs
+++ b/crates/api/src/comment_report/create.rs
@@ -1,13 +1,11 @@
 use crate::{check_report_reason, Perform};
-use activitypub_federation::core::object_id::ObjectId;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentReportResponse, CreateCommentReport},
+  context::LemmyContext,
   utils::{check_community_ban, get_local_user_view_from_jwt},
   websocket::{messages::SendModRoomMessage, UserOperation},
-  LemmyContext,
 };
-use lemmy_apub::protocol::activities::community::report::Report;
 use lemmy_db_schema::{
   source::{
     comment_report::{CommentReport, CommentReportForm},
@@ -67,15 +65,6 @@ impl Perform for CreateCommentReport {
       websocket_id,
     });
 
-    Report::send(
-      ObjectId::new(comment_view.comment.ap_id),
-      &local_user_view.person.into(),
-      ObjectId::new(comment_view.community.actor_id),
-      reason.to_string(),
-      context,
-    )
-    .await?;
-
     Ok(res)
   }
 }
diff --git a/crates/api/src/comment_report/list.rs b/crates/api/src/comment_report/list.rs
index b99716e88..8aa3fcbcd 100644
--- a/crates/api/src/comment_report/list.rs
+++ b/crates/api/src/comment_report/list.rs
@@ -2,8 +2,8 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{ListCommentReports, ListCommentReportsResponse},
+  context::LemmyContext,
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_views::comment_report_view::CommentReportQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/comment_report/resolve.rs b/crates/api/src/comment_report/resolve.rs
index 16598ffa9..9df11fc23 100644
--- a/crates/api/src/comment_report/resolve.rs
+++ b/crates/api/src/comment_report/resolve.rs
@@ -2,9 +2,9 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentReportResponse, ResolveCommentReport},
+  context::LemmyContext,
   utils::{get_local_user_view_from_jwt, is_mod_or_admin},
   websocket::{messages::SendModRoomMessage, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
 use lemmy_db_views::structs::CommentReportView;
diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs
index d307a672d..ce3082f71 100644
--- a/crates/api/src/community/add_mod.rs
+++ b/crates/api/src/community/add_mod.rs
@@ -2,19 +2,14 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{AddModToCommunity, AddModToCommunityResponse},
+  context::LemmyContext,
   utils::{get_local_user_view_from_jwt, is_mod_or_admin},
   websocket::{messages::SendCommunityRoomMessage, UserOperation},
-  LemmyContext,
-};
-use lemmy_apub::{
-  objects::{community::ApubCommunity, person::ApubPerson},
-  protocol::activities::community::{add_mod::AddMod, remove_mod::RemoveMod},
 };
 use lemmy_db_schema::{
   source::{
     community::{Community, CommunityModerator, CommunityModeratorForm},
     moderator::{ModAddCommunity, ModAddCommunityForm},
-    person::Person,
   },
   traits::{Crud, Joinable},
 };
@@ -69,28 +64,6 @@ impl Perform for AddModToCommunity {
 
     ModAddCommunity::create(context.pool(), &form).await?;
 
-    // Send to federated instances
-    let updated_mod_id = data.person_id;
-    let updated_mod: ApubPerson = Person::read(context.pool(), updated_mod_id).await?.into();
-    let community: ApubCommunity = community.into();
-    if data.added {
-      AddMod::send(
-        &community,
-        &updated_mod,
-        &local_user_view.person.into(),
-        context,
-      )
-      .await?;
-    } else {
-      RemoveMod::send(
-        &community,
-        &updated_mod,
-        &local_user_view.person.into(),
-        context,
-      )
-      .await?;
-    }
-
     // Note: in case a remote mod is added, this returns the old moderators list, it will only get
     //       updated once we receive an activity from the community (like `Announce/Add/Moderator`)
     let community_id = data.community_id;
diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs
index 018ccd012..fb5e7fcfe 100644
--- a/crates/api/src/community/ban.rs
+++ b/crates/api/src/community/ban.rs
@@ -2,26 +2,19 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{BanFromCommunity, BanFromCommunityResponse},
+  context::LemmyContext,
   utils::{get_local_user_view_from_jwt, is_mod_or_admin, remove_user_data_in_community},
   websocket::{messages::SendCommunityRoomMessage, UserOperation},
-  LemmyContext,
-};
-use lemmy_apub::{
-  activities::block::SiteOrCommunity,
-  objects::{community::ApubCommunity, person::ApubPerson},
-  protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
 };
 use lemmy_db_schema::{
   source::{
     community::{
-      Community,
       CommunityFollower,
       CommunityFollowerForm,
       CommunityPersonBan,
       CommunityPersonBanForm,
     },
     moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
-    person::Person,
   },
   traits::{Bannable, Crud, Followable},
 };
@@ -56,9 +49,6 @@ impl Perform for BanFromCommunity {
       expires: Some(expires),
     };
 
-    let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();
-    let banned_person: ApubPerson = Person::read(context.pool(), banned_person_id).await?.into();
-
     if data.ban {
       CommunityPersonBan::ban(context.pool(), &community_user_ban_form)
         .await
@@ -74,29 +64,10 @@ impl Perform for BanFromCommunity {
       CommunityFollower::unfollow(context.pool(), &community_follower_form)
         .await
         .ok();
-
-      BlockUser::send(
-        &SiteOrCommunity::Community(community),
-        &banned_person,
-        &local_user_view.person.clone().into(),
-        remove_data,
-        data.reason.clone(),
-        expires,
-        context,
-      )
-      .await?;
     } else {
       CommunityPersonBan::unban(context.pool(), &community_user_ban_form)
         .await
         .map_err(|e| LemmyError::from_error_message(e, "community_user_already_banned"))?;
-      UndoBlockUser::send(
-        &SiteOrCommunity::Community(community),
-        &banned_person,
-        &local_user_view.person.clone().into(),
-        data.reason.clone(),
-        context,
-      )
-      .await?;
     }
 
     // Remove/Restore their data if that's desired
diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs
index 6c6efc213..914b5238e 100644
--- a/crates/api/src/community/block.rs
+++ b/crates/api/src/community/block.rs
@@ -2,16 +2,15 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{BlockCommunity, BlockCommunityResponse},
+  context::LemmyContext,
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
-use lemmy_apub::protocol::activities::following::undo_follow::UndoFollow;
 use lemmy_db_schema::{
   source::{
-    community::{Community, CommunityFollower, CommunityFollowerForm},
+    community::{CommunityFollower, CommunityFollowerForm},
     community_block::{CommunityBlock, CommunityBlockForm},
   },
-  traits::{Blockable, Crud, Followable},
+  traits::{Blockable, Followable},
 };
 use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{error::LemmyError, ConnectionId};
@@ -52,8 +51,6 @@ impl Perform for BlockCommunity {
       CommunityFollower::unfollow(context.pool(), &community_follower_form)
         .await
         .ok();
-      let community = Community::read(context.pool(), community_id).await?;
-      UndoFollow::send(&local_user_view.person.into(), &community.into(), context).await?;
     } else {
       CommunityBlock::unblock(context.pool(), &community_block_form)
         .await
diff --git a/crates/api/src/community/follow.rs b/crates/api/src/community/follow.rs
index 51d5ad9b6..dedfc9712 100644
--- a/crates/api/src/community/follow.rs
+++ b/crates/api/src/community/follow.rs
@@ -2,15 +2,8 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{CommunityResponse, FollowCommunity},
+  context::LemmyContext,
   utils::{check_community_ban, check_community_deleted_or_removed, get_local_user_view_from_jwt},
-  LemmyContext,
-};
-use lemmy_apub::{
-  objects::community::ApubCommunity,
-  protocol::activities::following::{
-    follow::Follow as FollowCommunityApub,
-    undo_follow::UndoFollow,
-  },
 };
 use lemmy_db_schema::{
   source::community::{Community, CommunityFollower, CommunityFollowerForm},
@@ -34,33 +27,22 @@ impl Perform for FollowCommunity {
       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
 
     let community_id = data.community_id;
-    let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();
+    let community = Community::read(context.pool(), community_id).await?;
     let community_follower_form = CommunityFollowerForm {
       community_id: data.community_id,
       person_id: local_user_view.person.id,
       pending: false,
     };
 
-    if community.local {
-      if data.follow {
-        check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
-        check_community_deleted_or_removed(community_id, context.pool()).await?;
+    if community.local && data.follow {
+      check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
+      check_community_deleted_or_removed(community_id, context.pool()).await?;
 
-        CommunityFollower::follow(context.pool(), &community_follower_form)
-          .await
-          .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
-      } else {
-        CommunityFollower::unfollow(context.pool(), &community_follower_form)
-          .await
-          .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
-      }
-    } else if data.follow {
-      // Dont actually add to the community followers here, because you need
-      // to wait for the accept
-      FollowCommunityApub::send(&local_user_view.person.clone().into(), &community, context)
-        .await?;
-    } else {
-      UndoFollow::send(&local_user_view.person.clone().into(), &community, context).await?;
+      CommunityFollower::follow(context.pool(), &community_follower_form)
+        .await
+        .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
+    }
+    if !data.follow {
       CommunityFollower::unfollow(context.pool(), &community_follower_form)
         .await
         .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
diff --git a/crates/api/src/community/hide.rs b/crates/api/src/community/hide.rs
index 54a08135a..94ce7d745 100644
--- a/crates/api/src/community/hide.rs
+++ b/crates/api/src/community/hide.rs
@@ -2,11 +2,10 @@ use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{CommunityResponse, HideCommunity},
+  context::LemmyContext,
   utils::{get_local_user_view_from_jwt, is_admin},
   websocket::{send::send_community_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
 use lemmy_db_schema::{
   source::{
     community::{Community, CommunityUpdateForm},
@@ -45,19 +44,12 @@ impl Perform for HideCommunity {
     };
 
     let community_id = data.community_id;
-    let updated_community = Community::update(context.pool(), community_id, &community_form)
+    Community::update(context.pool(), community_id, &community_form)
       .await
       .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community_hidden_status"))?;
 
     ModHideCommunity::create(context.pool(), &mod_hide_community_form).await?;
 
-    UpdateCommunity::send(
-      updated_community.into(),
-      &local_user_view.person.into(),
-      context,
-    )
-    .await?;
-
     let op = UserOperationCrud::EditCommunity;
     send_community_ws_message(data.community_id, op, websocket_id, None, context).await
   }
diff --git a/crates/api/src/community/transfer.rs b/crates/api/src/community/transfer.rs
index bb69de092..7ca174e4c 100644
--- a/crates/api/src/community/transfer.rs
+++ b/crates/api/src/community/transfer.rs
@@ -3,8 +3,8 @@ use actix_web::web::Data;
 use anyhow::Context;
 use lemmy_api_common::{
   community::{GetCommunityResponse, TransferCommunity},
+  context::LemmyContext,
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs
index 0473f1e88..5904a9b9b 100644
--- a/crates/api/src/lib.rs
+++ b/crates/api/src/lib.rs
@@ -1,81 +1,8 @@
-use actix_web::{web, web::Data};
+use actix_web::web::Data;
 use captcha::Captcha;
-use lemmy_api_common::{
-  comment::{
-    CreateCommentLike,
-    CreateCommentReport,
-    ListCommentReports,
-    ResolveCommentReport,
-    SaveComment,
-  },
-  community::{
-    AddModToCommunity,
-    BanFromCommunity,
-    BlockCommunity,
-    FollowCommunity,
-    TransferCommunity,
-  },
-  person::{
-    AddAdmin,
-    BanPerson,
-    BlockPerson,
-    ChangePassword,
-    GetBannedPersons,
-    GetCaptcha,
-    GetPersonMentions,
-    GetReplies,
-    GetReportCount,
-    GetUnreadCount,
-    Login,
-    MarkAllAsRead,
-    MarkCommentReplyAsRead,
-    MarkPersonMentionAsRead,
-    PasswordChangeAfterReset,
-    PasswordReset,
-    SaveUserSettings,
-    VerifyEmail,
-  },
-  post::{
-    CreatePostLike,
-    CreatePostReport,
-    GetSiteMetadata,
-    ListPostReports,
-    LockPost,
-    MarkPostAsRead,
-    ResolvePostReport,
-    SavePost,
-    StickyPost,
-  },
-  private_message::{
-    CreatePrivateMessageReport,
-    ListPrivateMessageReports,
-    MarkPrivateMessageAsRead,
-    ResolvePrivateMessageReport,
-  },
-  site::{
-    ApproveRegistrationApplication,
-    GetModlog,
-    GetUnreadRegistrationApplicationCount,
-    LeaveAdmin,
-    ListRegistrationApplications,
-    PurgeComment,
-    PurgeCommunity,
-    PurgePerson,
-    PurgePost,
-    ResolveObject,
-    Search,
-  },
-  utils::local_site_to_slur_regex,
-  websocket::{
-    serialize_websocket_message,
-    structs::{CommunityJoin, ModJoin, PostJoin, UserJoin},
-    UserOperation,
-  },
-  LemmyContext,
-};
+use lemmy_api_common::{context::LemmyContext, utils::local_site_to_slur_regex};
 use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId};
-use serde::Deserialize;
 
 mod comment;
 mod comment_report;
@@ -99,182 +26,6 @@ pub trait Perform {
   ) -> Result<Self::Response, LemmyError>;
 }
 
-pub async fn match_websocket_operation(
-  context: LemmyContext,
-  id: ConnectionId,
-  op: UserOperation,
-  data: &str,
-) -> Result<String, LemmyError> {
-  match op {
-    // User ops
-    UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
-    UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
-    UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
-    UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
-    UserOperation::GetUnreadRegistrationApplicationCount => {
-      do_websocket_operation::<GetUnreadRegistrationApplicationCount>(context, id, op, data).await
-    }
-    UserOperation::ListRegistrationApplications => {
-      do_websocket_operation::<ListRegistrationApplications>(context, id, op, data).await
-    }
-    UserOperation::ApproveRegistrationApplication => {
-      do_websocket_operation::<ApproveRegistrationApplication>(context, id, op, data).await
-    }
-    UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
-    UserOperation::GetBannedPersons => {
-      do_websocket_operation::<GetBannedPersons>(context, id, op, data).await
-    }
-    UserOperation::BlockPerson => {
-      do_websocket_operation::<BlockPerson>(context, id, op, data).await
-    }
-    UserOperation::GetPersonMentions => {
-      do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
-    }
-    UserOperation::MarkPersonMentionAsRead => {
-      do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
-    }
-    UserOperation::MarkCommentReplyAsRead => {
-      do_websocket_operation::<MarkCommentReplyAsRead>(context, id, op, data).await
-    }
-    UserOperation::MarkAllAsRead => {
-      do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
-    }
-    UserOperation::PasswordReset => {
-      do_websocket_operation::<PasswordReset>(context, id, op, data).await
-    }
-    UserOperation::PasswordChange => {
-      do_websocket_operation::<PasswordChangeAfterReset>(context, id, op, data).await
-    }
-    UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
-    UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
-    UserOperation::CommunityJoin => {
-      do_websocket_operation::<CommunityJoin>(context, id, op, data).await
-    }
-    UserOperation::ModJoin => do_websocket_operation::<ModJoin>(context, id, op, data).await,
-    UserOperation::SaveUserSettings => {
-      do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
-    }
-    UserOperation::ChangePassword => {
-      do_websocket_operation::<ChangePassword>(context, id, op, data).await
-    }
-    UserOperation::GetReportCount => {
-      do_websocket_operation::<GetReportCount>(context, id, op, data).await
-    }
-    UserOperation::GetUnreadCount => {
-      do_websocket_operation::<GetUnreadCount>(context, id, op, data).await
-    }
-    UserOperation::VerifyEmail => {
-      do_websocket_operation::<VerifyEmail>(context, id, op, data).await
-    }
-
-    // Private Message ops
-    UserOperation::MarkPrivateMessageAsRead => {
-      do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
-    }
-    UserOperation::CreatePrivateMessageReport => {
-      do_websocket_operation::<CreatePrivateMessageReport>(context, id, op, data).await
-    }
-    UserOperation::ResolvePrivateMessageReport => {
-      do_websocket_operation::<ResolvePrivateMessageReport>(context, id, op, data).await
-    }
-    UserOperation::ListPrivateMessageReports => {
-      do_websocket_operation::<ListPrivateMessageReports>(context, id, op, data).await
-    }
-
-    // Site ops
-    UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
-    UserOperation::PurgePerson => {
-      do_websocket_operation::<PurgePerson>(context, id, op, data).await
-    }
-    UserOperation::PurgeCommunity => {
-      do_websocket_operation::<PurgeCommunity>(context, id, op, data).await
-    }
-    UserOperation::PurgePost => do_websocket_operation::<PurgePost>(context, id, op, data).await,
-    UserOperation::PurgeComment => {
-      do_websocket_operation::<PurgeComment>(context, id, op, data).await
-    }
-    UserOperation::Search => do_websocket_operation::<Search>(context, id, op, data).await,
-    UserOperation::ResolveObject => {
-      do_websocket_operation::<ResolveObject>(context, id, op, data).await
-    }
-    UserOperation::TransferCommunity => {
-      do_websocket_operation::<TransferCommunity>(context, id, op, data).await
-    }
-    UserOperation::LeaveAdmin => do_websocket_operation::<LeaveAdmin>(context, id, op, data).await,
-
-    // Community ops
-    UserOperation::FollowCommunity => {
-      do_websocket_operation::<FollowCommunity>(context, id, op, data).await
-    }
-    UserOperation::BlockCommunity => {
-      do_websocket_operation::<BlockCommunity>(context, id, op, data).await
-    }
-    UserOperation::BanFromCommunity => {
-      do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
-    }
-    UserOperation::AddModToCommunity => {
-      do_websocket_operation::<AddModToCommunity>(context, id, op, data).await
-    }
-
-    // Post ops
-    UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
-    UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
-    UserOperation::CreatePostLike => {
-      do_websocket_operation::<CreatePostLike>(context, id, op, data).await
-    }
-    UserOperation::MarkPostAsRead => {
-      do_websocket_operation::<MarkPostAsRead>(context, id, op, data).await
-    }
-    UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
-    UserOperation::CreatePostReport => {
-      do_websocket_operation::<CreatePostReport>(context, id, op, data).await
-    }
-    UserOperation::ListPostReports => {
-      do_websocket_operation::<ListPostReports>(context, id, op, data).await
-    }
-    UserOperation::ResolvePostReport => {
-      do_websocket_operation::<ResolvePostReport>(context, id, op, data).await
-    }
-    UserOperation::GetSiteMetadata => {
-      do_websocket_operation::<GetSiteMetadata>(context, id, op, data).await
-    }
-
-    // Comment ops
-    UserOperation::SaveComment => {
-      do_websocket_operation::<SaveComment>(context, id, op, data).await
-    }
-    UserOperation::CreateCommentLike => {
-      do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
-    }
-    UserOperation::CreateCommentReport => {
-      do_websocket_operation::<CreateCommentReport>(context, id, op, data).await
-    }
-    UserOperation::ListCommentReports => {
-      do_websocket_operation::<ListCommentReports>(context, id, op, data).await
-    }
-    UserOperation::ResolveCommentReport => {
-      do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await
-    }
-  }
-}
-
-async fn do_websocket_operation<'a, 'b, Data>(
-  context: LemmyContext,
-  id: ConnectionId,
-  op: UserOperation,
-  data: &str,
-) -> Result<String, LemmyError>
-where
-  for<'de> Data: Deserialize<'de> + 'a,
-  Data: Perform,
-{
-  let parsed_data: Data = serde_json::from_str(data)?;
-  let res = parsed_data
-    .perform(&web::Data::new(context), Some(id))
-    .await?;
-  serialize_websocket_message(&op, &res)
-}
-
 /// Converts the captcha to a base64 encoded wav audio file
 pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
   let letters = captcha.as_wav();
diff --git a/crates/api/src/local_user/add_admin.rs b/crates/api/src/local_user/add_admin.rs
index 8091f97e4..78357f0c5 100644
--- a/crates/api/src/local_user/add_admin.rs
+++ b/crates/api/src/local_user/add_admin.rs
@@ -1,10 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{AddAdmin, AddAdminResponse},
   utils::{get_local_user_view_from_jwt, is_admin},
   websocket::{messages::SendAllMessage, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs
index a848175e4..0bb252349 100644
--- a/crates/api/src/local_user/ban_person.rs
+++ b/crates/api/src/local_user/ban_person.rs
@@ -1,14 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{BanPerson, BanPersonResponse},
   utils::{get_local_user_view_from_jwt, is_admin, remove_user_data},
   websocket::{messages::SendAllMessage, UserOperation},
-  LemmyContext,
-};
-use lemmy_apub::{
-  activities::block::SiteOrCommunity,
-  protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
 };
 use lemmy_db_schema::{
   source::{
@@ -17,7 +13,6 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views::structs::SiteView;
 use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
 
@@ -79,32 +74,6 @@ impl Perform for BanPerson {
     let person_id = data.person_id;
     let person_view = PersonViewSafe::read(context.pool(), person_id).await?;
 
-    let site = SiteOrCommunity::Site(SiteView::read_local(context.pool()).await?.site.into());
-    // if the action affects a local user, federate to other instances
-    if person.local {
-      if ban {
-        BlockUser::send(
-          &site,
-          &person.into(),
-          &local_user_view.person.into(),
-          remove_data,
-          data.reason.clone(),
-          expires,
-          context,
-        )
-        .await?;
-      } else {
-        UndoBlockUser::send(
-          &site,
-          &person.into(),
-          &local_user_view.person.into(),
-          data.reason.clone(),
-          context,
-        )
-        .await?;
-      }
-    }
-
     let res = BanPersonResponse {
       person_view,
       banned: data.ban,
diff --git a/crates/api/src/local_user/block.rs b/crates/api/src/local_user/block.rs
index 798d36774..b57ecd551 100644
--- a/crates/api/src/local_user/block.rs
+++ b/crates/api/src/local_user/block.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{BlockPerson, BlockPersonResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::person_block::{PersonBlock, PersonBlockForm},
diff --git a/crates/api/src/local_user/change_password.rs b/crates/api/src/local_user/change_password.rs
index e4e9004ea..2d0fd30e5 100644
--- a/crates/api/src/local_user/change_password.rs
+++ b/crates/api/src/local_user/change_password.rs
@@ -2,9 +2,9 @@ use crate::Perform;
 use actix_web::web::Data;
 use bcrypt::verify;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{ChangePassword, LoginResponse},
   utils::{get_local_user_view_from_jwt, password_length_check},
-  LemmyContext,
 };
 use lemmy_db_schema::source::local_user::LocalUser;
 use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId};
diff --git a/crates/api/src/local_user/change_password_after_reset.rs b/crates/api/src/local_user/change_password_after_reset.rs
index f9556b533..c6de10d7a 100644
--- a/crates/api/src/local_user/change_password_after_reset.rs
+++ b/crates/api/src/local_user/change_password_after_reset.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{LoginResponse, PasswordChangeAfterReset},
   utils::password_length_check,
-  LemmyContext,
 };
 use lemmy_db_schema::source::{
   local_user::LocalUser,
diff --git a/crates/api/src/local_user/get_captcha.rs b/crates/api/src/local_user/get_captcha.rs
index 5eacc4fd8..50a2bdba2 100644
--- a/crates/api/src/local_user/get_captcha.rs
+++ b/crates/api/src/local_user/get_captcha.rs
@@ -3,9 +3,9 @@ use actix_web::web::Data;
 use captcha::{gen, Difficulty};
 use chrono::Duration;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse},
   websocket::messages::CaptchaItem,
-  LemmyContext,
 };
 use lemmy_db_schema::{source::local_site::LocalSite, utils::naive_now};
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/local_user/list_banned.rs b/crates/api/src/local_user/list_banned.rs
index 31d4e3be6..60eb32b35 100644
--- a/crates/api/src/local_user/list_banned.rs
+++ b/crates/api/src/local_user/list_banned.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{BannedPersonsResponse, GetBannedPersons},
   utils::{get_local_user_view_from_jwt, is_admin},
-  LemmyContext,
 };
 use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/local_user/login.rs b/crates/api/src/local_user/login.rs
index dac0d9bf5..c60c0dcdf 100644
--- a/crates/api/src/local_user/login.rs
+++ b/crates/api/src/local_user/login.rs
@@ -2,9 +2,9 @@ use crate::Perform;
 use actix_web::web::Data;
 use bcrypt::verify;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{Login, LoginResponse},
   utils::{check_registration_application, check_user_valid},
-  LemmyContext,
 };
 use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_db_views::structs::LocalUserView;
diff --git a/crates/api/src/local_user/notifications/list_mentions.rs b/crates/api/src/local_user/notifications/list_mentions.rs
index 5e3b63e7a..c03e9d167 100644
--- a/crates/api/src/local_user/notifications/list_mentions.rs
+++ b/crates/api/src/local_user/notifications/list_mentions.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{GetPersonMentions, GetPersonMentionsResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_views_actor::person_mention_view::PersonMentionQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/local_user/notifications/list_replies.rs b/crates/api/src/local_user/notifications/list_replies.rs
index 375864b58..585db5e95 100644
--- a/crates/api/src/local_user/notifications/list_replies.rs
+++ b/crates/api/src/local_user/notifications/list_replies.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{GetReplies, GetRepliesResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_views_actor::comment_reply_view::CommentReplyQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/local_user/notifications/mark_all_read.rs b/crates/api/src/local_user/notifications/mark_all_read.rs
index 9f8926dc3..2515715bd 100644
--- a/crates/api/src/local_user/notifications/mark_all_read.rs
+++ b/crates/api/src/local_user/notifications/mark_all_read.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{GetRepliesResponse, MarkAllAsRead},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_schema::source::{
   comment_reply::CommentReply,
diff --git a/crates/api/src/local_user/notifications/mark_mention_read.rs b/crates/api/src/local_user/notifications/mark_mention_read.rs
index 655f78846..a3a75d929 100644
--- a/crates/api/src/local_user/notifications/mark_mention_read.rs
+++ b/crates/api/src/local_user/notifications/mark_mention_read.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{MarkPersonMentionAsRead, PersonMentionResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::person_mention::{PersonMention, PersonMentionUpdateForm},
diff --git a/crates/api/src/local_user/notifications/mark_reply_read.rs b/crates/api/src/local_user/notifications/mark_reply_read.rs
index fa3367e33..3921e769d 100644
--- a/crates/api/src/local_user/notifications/mark_reply_read.rs
+++ b/crates/api/src/local_user/notifications/mark_reply_read.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{CommentReplyResponse, MarkCommentReplyAsRead},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::comment_reply::{CommentReply, CommentReplyUpdateForm},
diff --git a/crates/api/src/local_user/notifications/unread_count.rs b/crates/api/src/local_user/notifications/unread_count.rs
index 442977a2c..715ccd286 100644
--- a/crates/api/src/local_user/notifications/unread_count.rs
+++ b/crates/api/src/local_user/notifications/unread_count.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{GetUnreadCount, GetUnreadCountResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_views::structs::PrivateMessageView;
 use lemmy_db_views_actor::structs::{CommentReplyView, PersonMentionView};
diff --git a/crates/api/src/local_user/report_count.rs b/crates/api/src/local_user/report_count.rs
index 7dd63b7ad..b59a06490 100644
--- a/crates/api/src/local_user/report_count.rs
+++ b/crates/api/src/local_user/report_count.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{GetReportCount, GetReportCountResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_views::structs::{CommentReportView, PostReportView, PrivateMessageReportView};
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/local_user/reset_password.rs b/crates/api/src/local_user/reset_password.rs
index 8def6d3c6..5d0e9d88b 100644
--- a/crates/api/src/local_user/reset_password.rs
+++ b/crates/api/src/local_user/reset_password.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{PasswordReset, PasswordResetResponse},
   utils::send_password_reset_email,
-  LemmyContext,
 };
 use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs
index 807e8262d..6672a67b0 100644
--- a/crates/api/src/local_user/save_settings.rs
+++ b/crates/api/src/local_user/save_settings.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{LoginResponse, SaveUserSettings},
   utils::{get_local_user_view_from_jwt, send_verification_email},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/local_user/verify_email.rs b/crates/api/src/local_user/verify_email.rs
index 238692ff0..fb82b716b 100644
--- a/crates/api/src/local_user/verify_email.rs
+++ b/crates/api/src/local_user/verify_email.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{VerifyEmail, VerifyEmailResponse},
   utils::send_email_verification_success,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/post/get_link_metadata.rs b/crates/api/src/post/get_link_metadata.rs
index aae211a50..eaf111eba 100644
--- a/crates/api/src/post/get_link_metadata.rs
+++ b/crates/api/src/post/get_link_metadata.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{GetSiteMetadata, GetSiteMetadataResponse},
   request::fetch_site_metadata,
-  LemmyContext,
 };
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs
index d15e649b9..59135ff21 100644
--- a/crates/api/src/post/like.rs
+++ b/crates/api/src/post/like.rs
@@ -1,6 +1,7 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{CreatePostLike, PostResponse},
   utils::{
     check_community_ban,
@@ -10,15 +11,6 @@ use lemmy_api_common::{
     mark_post_as_read,
   },
   websocket::{send::send_post_ws_message, UserOperation},
-  LemmyContext,
-};
-use lemmy_apub::{
-  fetcher::post_or_comment::PostOrComment,
-  objects::post::ApubPost,
-  protocol::activities::voting::{
-    undo_vote::UndoVote,
-    vote::{Vote, VoteType},
-  },
 };
 use lemmy_db_schema::{
   source::{
@@ -49,7 +41,7 @@ impl Perform for CreatePostLike {
 
     // Check for a community ban
     let post_id = data.post_id;
-    let post: ApubPost = Post::read(context.pool(), post_id).await?.into();
+    let post = Post::read(context.pool(), post_id).await?;
 
     check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
     check_community_deleted_or_removed(post.community_id, context.pool()).await?;
@@ -65,9 +57,6 @@ impl Perform for CreatePostLike {
 
     PostLike::remove(context.pool(), person_id, post_id).await?;
 
-    let community_id = post.community_id;
-    let object = PostOrComment::Post(Box::new(post));
-
     // 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 {
@@ -75,25 +64,6 @@ impl Perform for CreatePostLike {
       PostLike::like(context.pool(), &like_form2)
         .await
         .map_err(|e| LemmyError::from_error_message(e, "couldnt_like_post"))?;
-
-      Vote::send(
-        &object,
-        &local_user_view.person.clone().into(),
-        community_id,
-        like_form.score.try_into()?,
-        context,
-      )
-      .await?;
-    } else {
-      // API doesn't distinguish between Undo/Like and Undo/Dislike
-      UndoVote::send(
-        &object,
-        &local_user_view.person.clone().into(),
-        community_id,
-        VoteType::Like,
-        context,
-      )
-      .await?;
     }
 
     // Mark the post as read
diff --git a/crates/api/src/post/lock.rs b/crates/api/src/post/lock.rs
index 4348971fe..c5fa2a0f5 100644
--- a/crates/api/src/post/lock.rs
+++ b/crates/api/src/post/lock.rs
@@ -1,6 +1,7 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{LockPost, PostResponse},
   utils::{
     check_community_ban,
@@ -9,11 +10,6 @@ use lemmy_api_common::{
     is_mod_or_admin,
   },
   websocket::{send::send_post_ws_message, UserOperation},
-  LemmyContext,
-};
-use lemmy_apub::{
-  objects::post::ApubPost,
-  protocol::activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
 };
 use lemmy_db_schema::{
   source::{
@@ -60,13 +56,12 @@ impl Perform for LockPost {
     // Update the post
     let post_id = data.post_id;
     let locked = data.locked;
-    let updated_post: ApubPost = Post::update(
+    Post::update(
       context.pool(),
       post_id,
       &PostUpdateForm::builder().locked(Some(locked)).build(),
     )
-    .await?
-    .into();
+    .await?;
 
     // Mod tables
     let form = ModLockPostForm {
@@ -76,15 +71,6 @@ impl Perform for LockPost {
     };
     ModLockPost::create(context.pool(), &form).await?;
 
-    // apub updates
-    CreateOrUpdatePage::send(
-      updated_post,
-      &local_user_view.person.clone().into(),
-      CreateOrUpdateType::Update,
-      context,
-    )
-    .await?;
-
     send_post_ws_message(
       data.post_id,
       UserOperation::LockPost,
diff --git a/crates/api/src/post/mark_read.rs b/crates/api/src/post/mark_read.rs
index 228fdac0d..9fdf01b12 100644
--- a/crates/api/src/post/mark_read.rs
+++ b/crates/api/src/post/mark_read.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{MarkPostAsRead, PostResponse},
   utils::{get_local_user_view_from_jwt, mark_post_as_read, mark_post_as_unread},
-  LemmyContext,
 };
 use lemmy_db_views::structs::PostView;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/post/save.rs b/crates/api/src/post/save.rs
index 9e8ca9ce1..b36f844b6 100644
--- a/crates/api/src/post/save.rs
+++ b/crates/api/src/post/save.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{PostResponse, SavePost},
   utils::{get_local_user_view_from_jwt, mark_post_as_read},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::post::{PostSaved, PostSavedForm},
diff --git a/crates/api/src/post/sticky.rs b/crates/api/src/post/sticky.rs
index ccba5f2c3..3a8aee7ab 100644
--- a/crates/api/src/post/sticky.rs
+++ b/crates/api/src/post/sticky.rs
@@ -1,6 +1,7 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{PostResponse, StickyPost},
   utils::{
     check_community_ban,
@@ -9,11 +10,6 @@ use lemmy_api_common::{
     is_mod_or_admin,
   },
   websocket::{send::send_post_ws_message, UserOperation},
-  LemmyContext,
-};
-use lemmy_apub::{
-  objects::post::ApubPost,
-  protocol::activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
 };
 use lemmy_db_schema::{
   source::{
@@ -60,13 +56,12 @@ impl Perform for StickyPost {
     // Update the post
     let post_id = data.post_id;
     let stickied = data.stickied;
-    let updated_post: ApubPost = Post::update(
+    Post::update(
       context.pool(),
       post_id,
       &PostUpdateForm::builder().stickied(Some(stickied)).build(),
     )
-    .await?
-    .into();
+    .await?;
 
     // Mod tables
     let form = ModStickyPostForm {
@@ -77,16 +72,6 @@ impl Perform for StickyPost {
 
     ModStickyPost::create(context.pool(), &form).await?;
 
-    // Apub updates
-    // TODO stickied should pry work like locked for ease of use
-    CreateOrUpdatePage::send(
-      updated_post,
-      &local_user_view.person.clone().into(),
-      CreateOrUpdateType::Update,
-      context,
-    )
-    .await?;
-
     send_post_ws_message(
       data.post_id,
       UserOperation::StickyPost,
diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs
index 2fd2d08be..71be43623 100644
--- a/crates/api/src/post_report/create.rs
+++ b/crates/api/src/post_report/create.rs
@@ -1,13 +1,11 @@
 use crate::{check_report_reason, Perform};
-use activitypub_federation::core::object_id::ObjectId;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{CreatePostReport, PostReportResponse},
   utils::{check_community_ban, get_local_user_view_from_jwt},
   websocket::{messages::SendModRoomMessage, UserOperation},
-  LemmyContext,
 };
-use lemmy_apub::protocol::activities::community::report::Report;
 use lemmy_db_schema::{
   source::{
     local_site::LocalSite,
@@ -67,15 +65,6 @@ impl Perform for CreatePostReport {
       websocket_id,
     });
 
-    Report::send(
-      ObjectId::new(post_view.post.ap_id),
-      &local_user_view.person.into(),
-      ObjectId::new(post_view.community.actor_id),
-      reason.to_string(),
-      context,
-    )
-    .await?;
-
     Ok(res)
   }
 }
diff --git a/crates/api/src/post_report/list.rs b/crates/api/src/post_report/list.rs
index 188a722ff..2feed7ed2 100644
--- a/crates/api/src/post_report/list.rs
+++ b/crates/api/src/post_report/list.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{ListPostReports, ListPostReportsResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_views::post_report_view::PostReportQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/post_report/resolve.rs b/crates/api/src/post_report/resolve.rs
index 6c3d62f18..0e2b9e16a 100644
--- a/crates/api/src/post_report/resolve.rs
+++ b/crates/api/src/post_report/resolve.rs
@@ -1,10 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{PostReportResponse, ResolvePostReport},
   utils::{get_local_user_view_from_jwt, is_mod_or_admin},
   websocket::{messages::SendModRoomMessage, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
 use lemmy_db_views::structs::PostReportView;
diff --git a/crates/api/src/private_message/mark_read.rs b/crates/api/src/private_message/mark_read.rs
index ba0e70d59..9c3c3d8ae 100644
--- a/crates/api/src/private_message/mark_read.rs
+++ b/crates/api/src/private_message/mark_read.rs
@@ -1,10 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   private_message::{MarkPrivateMessageAsRead, PrivateMessageResponse},
   utils::get_local_user_view_from_jwt,
   websocket::{send::send_pm_ws_message, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
diff --git a/crates/api/src/private_message_report/create.rs b/crates/api/src/private_message_report/create.rs
index 6b923c102..66875c614 100644
--- a/crates/api/src/private_message_report/create.rs
+++ b/crates/api/src/private_message_report/create.rs
@@ -1,10 +1,10 @@
 use crate::{check_report_reason, Perform};
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse},
   utils::get_local_user_view_from_jwt,
   websocket::{messages::SendModRoomMessage, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   newtypes::CommunityId,
diff --git a/crates/api/src/private_message_report/list.rs b/crates/api/src/private_message_report/list.rs
index 17fc438ff..98c2d9269 100644
--- a/crates/api/src/private_message_report/list.rs
+++ b/crates/api/src/private_message_report/list.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   private_message::{ListPrivateMessageReports, ListPrivateMessageReportsResponse},
   utils::{get_local_user_view_from_jwt, is_admin},
-  LemmyContext,
 };
 use lemmy_db_views::private_message_report_view::PrivateMessageReportQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
diff --git a/crates/api/src/private_message_report/resolve.rs b/crates/api/src/private_message_report/resolve.rs
index eeda4ed42..2a3f677ad 100644
--- a/crates/api/src/private_message_report/resolve.rs
+++ b/crates/api/src/private_message_report/resolve.rs
@@ -1,10 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   private_message::{PrivateMessageReportResponse, ResolvePrivateMessageReport},
   utils::{get_local_user_view_from_jwt, is_admin},
   websocket::{messages::SendModRoomMessage, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   newtypes::CommunityId,
diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs
index 4674f43bf..7a75db51d 100644
--- a/crates/api/src/site/leave_admin.rs
+++ b/crates/api/src/site/leave_admin.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{GetSiteResponse, LeaveAdmin},
   utils::{get_local_user_view_from_jwt, is_admin},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/site/mod.rs b/crates/api/src/site/mod.rs
index 531c05085..4e3694ce4 100644
--- a/crates/api/src/site/mod.rs
+++ b/crates/api/src/site/mod.rs
@@ -2,5 +2,3 @@ mod leave_admin;
 mod mod_log;
 mod purge;
 mod registration_applications;
-mod resolve_object;
-mod search;
diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs
index 85bfc40ce..5e1c71a70 100644
--- a/crates/api/src/site/mod_log.rs
+++ b/crates/api/src/site/mod_log.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{GetModlog, GetModlogResponse},
   utils::{check_private_instance, get_local_user_view_from_jwt_opt, is_admin, is_mod_or_admin},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
diff --git a/crates/api/src/site/purge/comment.rs b/crates/api/src/site/purge/comment.rs
index 54f3c0edf..71d2c7889 100644
--- a/crates/api/src/site/purge/comment.rs
+++ b/crates/api/src/site/purge/comment.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{PurgeComment, PurgeItemResponse},
   utils::{get_local_user_view_from_jwt, is_admin},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/site/purge/community.rs b/crates/api/src/site/purge/community.rs
index c70c8c0a7..e3a673b74 100644
--- a/crates/api/src/site/purge/community.rs
+++ b/crates/api/src/site/purge/community.rs
@@ -1,10 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   request::purge_image_from_pictrs,
   site::{PurgeCommunity, PurgeItemResponse},
   utils::{get_local_user_view_from_jwt, is_admin, purge_image_posts_for_community},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/site/purge/person.rs b/crates/api/src/site/purge/person.rs
index 8a5f6ac09..658e50b6d 100644
--- a/crates/api/src/site/purge/person.rs
+++ b/crates/api/src/site/purge/person.rs
@@ -1,10 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   request::purge_image_from_pictrs,
   site::{PurgeItemResponse, PurgePerson},
   utils::{get_local_user_view_from_jwt, is_admin, purge_image_posts_for_person},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/site/purge/post.rs b/crates/api/src/site/purge/post.rs
index b3ec28eac..aa2e74839 100644
--- a/crates/api/src/site/purge/post.rs
+++ b/crates/api/src/site/purge/post.rs
@@ -1,10 +1,10 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   request::purge_image_from_pictrs,
   site::{PurgeItemResponse, PurgePost},
   utils::{get_local_user_view_from_jwt, is_admin},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/site/registration_applications/approve.rs b/crates/api/src/site/registration_applications/approve.rs
index c5089b5d5..61a686899 100644
--- a/crates/api/src/site/registration_applications/approve.rs
+++ b/crates/api/src/site/registration_applications/approve.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{ApproveRegistrationApplication, RegistrationApplicationResponse},
   utils::{get_local_user_view_from_jwt, is_admin, send_application_approved_email},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api/src/site/registration_applications/list.rs b/crates/api/src/site/registration_applications/list.rs
index 1ce709b35..7be2e2e8e 100644
--- a/crates/api/src/site/registration_applications/list.rs
+++ b/crates/api/src/site/registration_applications/list.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
   utils::{get_local_user_view_from_jwt, is_admin},
-  LemmyContext,
 };
 use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_db_views::registration_application_view::RegistrationApplicationQuery;
diff --git a/crates/api/src/site/registration_applications/unread_count.rs b/crates/api/src/site/registration_applications/unread_count.rs
index a22d14e95..fe33d17c8 100644
--- a/crates/api/src/site/registration_applications/unread_count.rs
+++ b/crates/api/src/site/registration_applications/unread_count.rs
@@ -1,9 +1,9 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse},
   utils::{get_local_user_view_from_jwt, is_admin},
-  LemmyContext,
 };
 use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_db_views::structs::RegistrationApplicationView;
diff --git a/crates/api/src/websocket.rs b/crates/api/src/websocket.rs
index 0e0c015a1..ca755bde9 100644
--- a/crates/api/src/websocket.rs
+++ b/crates/api/src/websocket.rs
@@ -1,6 +1,7 @@
 use crate::Perform;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   utils::get_local_user_view_from_jwt,
   websocket::{
     messages::{JoinCommunityRoom, JoinModRoom, JoinPostRoom, JoinUserRoom},
@@ -15,7 +16,6 @@ use lemmy_api_common::{
       UserJoinResponse,
     },
   },
-  LemmyContext,
 };
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
diff --git a/crates/api_common/src/context.rs b/crates/api_common/src/context.rs
new file mode 100644
index 000000000..e552af530
--- /dev/null
+++ b/crates/api_common/src/context.rs
@@ -0,0 +1,68 @@
+use crate::websocket::chat_server::ChatServer;
+use actix::Addr;
+use lemmy_db_schema::{source::secret::Secret, utils::DbPool};
+use lemmy_utils::{
+  rate_limit::RateLimitCell,
+  settings::{structs::Settings, SETTINGS},
+};
+use reqwest_middleware::ClientWithMiddleware;
+
+pub struct LemmyContext {
+  pool: DbPool,
+  chat_server: Addr<ChatServer>,
+  client: ClientWithMiddleware,
+  settings: Settings,
+  secret: Secret,
+  rate_limit_cell: RateLimitCell,
+}
+
+impl LemmyContext {
+  pub fn create(
+    pool: DbPool,
+    chat_server: Addr<ChatServer>,
+    client: ClientWithMiddleware,
+    settings: Settings,
+    secret: Secret,
+    rate_limit_cell: RateLimitCell,
+  ) -> LemmyContext {
+    LemmyContext {
+      pool,
+      chat_server,
+      client,
+      settings,
+      secret,
+      rate_limit_cell,
+    }
+  }
+  pub fn pool(&self) -> &DbPool {
+    &self.pool
+  }
+  pub fn chat_server(&self) -> &Addr<ChatServer> {
+    &self.chat_server
+  }
+  pub fn client(&self) -> &ClientWithMiddleware {
+    &self.client
+  }
+  pub fn settings(&self) -> &'static Settings {
+    &SETTINGS
+  }
+  pub fn secret(&self) -> &Secret {
+    &self.secret
+  }
+  pub fn settings_updated_channel(&self) -> &RateLimitCell {
+    &self.rate_limit_cell
+  }
+}
+
+impl Clone for LemmyContext {
+  fn clone(&self) -> Self {
+    LemmyContext {
+      pool: self.pool.clone(),
+      chat_server: self.chat_server.clone(),
+      client: self.client.clone(),
+      settings: self.settings.clone(),
+      secret: self.secret.clone(),
+      rate_limit_cell: self.rate_limit_cell.clone(),
+    }
+  }
+}
diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs
index 990780e15..29686aba6 100644
--- a/crates/api_common/src/lib.rs
+++ b/crates/api_common/src/lib.rs
@@ -1,5 +1,7 @@
 pub mod comment;
 pub mod community;
+#[cfg(feature = "full")]
+pub mod context;
 pub mod person;
 pub mod post;
 pub mod private_message;
@@ -9,6 +11,7 @@ pub mod sensitive;
 pub mod site;
 #[cfg(feature = "full")]
 pub mod utils;
+#[cfg(feature = "full")]
 pub mod websocket;
 
 #[macro_use]
@@ -17,138 +20,3 @@ pub extern crate lemmy_db_schema;
 pub extern crate lemmy_db_views;
 pub extern crate lemmy_db_views_actor;
 pub extern crate lemmy_db_views_moderator;
-
-use crate::websocket::chat_server::ChatServer;
-use actix::Addr;
-use anyhow::Context;
-use lemmy_db_schema::{newtypes::DbUrl, source::secret::Secret, utils::DbPool};
-use lemmy_utils::{
-  error::LemmyError,
-  location_info,
-  rate_limit::RateLimitCell,
-  settings::{structs::Settings, SETTINGS},
-};
-use reqwest_middleware::ClientWithMiddleware;
-use url::{ParseError, Url};
-
-pub struct LemmyContext {
-  pool: DbPool,
-  chat_server: Addr<ChatServer>,
-  client: ClientWithMiddleware,
-  settings: Settings,
-  secret: Secret,
-  rate_limit_cell: RateLimitCell,
-}
-
-impl LemmyContext {
-  pub fn create(
-    pool: DbPool,
-    chat_server: Addr<ChatServer>,
-    client: ClientWithMiddleware,
-    settings: Settings,
-    secret: Secret,
-    settings_updated_channel: RateLimitCell,
-  ) -> LemmyContext {
-    LemmyContext {
-      pool,
-      chat_server,
-      client,
-      settings,
-      secret,
-      rate_limit_cell: settings_updated_channel,
-    }
-  }
-  pub fn pool(&self) -> &DbPool {
-    &self.pool
-  }
-  pub fn chat_server(&self) -> &Addr<ChatServer> {
-    &self.chat_server
-  }
-  pub fn client(&self) -> &ClientWithMiddleware {
-    &self.client
-  }
-  pub fn settings(&self) -> &'static Settings {
-    &SETTINGS
-  }
-  pub fn secret(&self) -> &Secret {
-    &self.secret
-  }
-  pub fn settings_updated_channel(&self) -> &RateLimitCell {
-    &self.rate_limit_cell
-  }
-}
-
-impl Clone for LemmyContext {
-  fn clone(&self) -> Self {
-    LemmyContext {
-      pool: self.pool.clone(),
-      chat_server: self.chat_server.clone(),
-      client: self.client.clone(),
-      settings: self.settings.clone(),
-      secret: self.secret.clone(),
-      rate_limit_cell: self.rate_limit_cell.clone(),
-    }
-  }
-}
-
-pub enum EndpointType {
-  Community,
-  Person,
-  Post,
-  Comment,
-  PrivateMessage,
-}
-
-/// Generates an apub endpoint for a given domain, IE xyz.tld
-pub fn generate_local_apub_endpoint(
-  endpoint_type: EndpointType,
-  name: &str,
-  domain: &str,
-) -> Result<DbUrl, ParseError> {
-  let point = match endpoint_type {
-    EndpointType::Community => "c",
-    EndpointType::Person => "u",
-    EndpointType::Post => "post",
-    EndpointType::Comment => "comment",
-    EndpointType::PrivateMessage => "private_message",
-  };
-
-  Ok(Url::parse(&format!("{}/{}/{}", domain, point, name))?.into())
-}
-
-pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
-  Ok(Url::parse(&format!("{}/followers", actor_id))?.into())
-}
-
-pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
-  Ok(Url::parse(&format!("{}/inbox", actor_id))?.into())
-}
-
-pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
-  let mut actor_id: Url = actor_id.clone().into();
-  actor_id.set_path("site_inbox");
-  Ok(actor_id.into())
-}
-
-pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
-  let actor_id: Url = actor_id.clone().into();
-  let url = format!(
-    "{}://{}{}/inbox",
-    &actor_id.scheme(),
-    &actor_id.host_str().context(location_info!())?,
-    if let Some(port) = actor_id.port() {
-      format!(":{}", port)
-    } else {
-      String::new()
-    },
-  );
-  Ok(Url::parse(&url)?.into())
-}
-
-pub fn generate_outbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
-  Ok(Url::parse(&format!("{}/outbox", actor_id))?.into())
-}
-
-pub fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
-  Ok(Url::parse(&format!("{}/moderators", community_id))?.into())
-}
diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs
index 8c4131dd6..897dd998e 100644
--- a/crates/api_common/src/person.rs
+++ b/crates/api_common/src/person.rs
@@ -268,7 +268,7 @@ pub struct GetUnreadCountResponse {
   pub private_messages: i64,
 }
 
-#[derive(Serialize, Deserialize, Clone, Default)]
+#[derive(Serialize, Deserialize, Clone, Default, Debug)]
 pub struct VerifyEmail {
   pub token: String,
 }
diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs
index 1e863e855..c85344992 100644
--- a/crates/api_common/src/utils.rs
+++ b/crates/api_common/src/utils.rs
@@ -1,8 +1,9 @@
 use crate::{request::purge_image_from_pictrs, sensitive::Sensitive, site::FederatedInstances};
+use anyhow::Context;
 use chrono::NaiveDateTime;
 use lemmy_db_schema::{
   impls::person::is_banned,
-  newtypes::{CommunityId, LocalUserId, PersonId, PostId},
+  newtypes::{CommunityId, DbUrl, LocalUserId, PersonId, PostId},
   source::{
     comment::{Comment, CommentUpdateForm},
     community::{Community, CommunityUpdateForm},
@@ -34,6 +35,7 @@ use lemmy_utils::{
   claims::Claims,
   email::{send_email, translations::Lang},
   error::LemmyError,
+  location_info,
   rate_limit::RateLimitConfig,
   settings::structs::Settings,
   utils::{build_slur_regex, generate_random_string},
@@ -43,6 +45,7 @@ use reqwest_middleware::ClientWithMiddleware;
 use rosetta_i18n::{Language, LanguageId};
 use std::str::FromStr;
 use tracing::warn;
+use url::{ParseError, Url};
 
 #[tracing::instrument(skip_all)]
 pub async fn is_mod_or_admin(
@@ -743,3 +746,65 @@ mod tests {
     assert!(honeypot_check(&Some("message".to_string())).is_err());
   }
 }
+
+pub enum EndpointType {
+  Community,
+  Person,
+  Post,
+  Comment,
+  PrivateMessage,
+}
+
+/// Generates an apub endpoint for a given domain, IE xyz.tld
+pub fn generate_local_apub_endpoint(
+  endpoint_type: EndpointType,
+  name: &str,
+  domain: &str,
+) -> Result<DbUrl, ParseError> {
+  let point = match endpoint_type {
+    EndpointType::Community => "c",
+    EndpointType::Person => "u",
+    EndpointType::Post => "post",
+    EndpointType::Comment => "comment",
+    EndpointType::PrivateMessage => "private_message",
+  };
+
+  Ok(Url::parse(&format!("{}/{}/{}", domain, point, name))?.into())
+}
+
+pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  Ok(Url::parse(&format!("{}/followers", actor_id))?.into())
+}
+
+pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  Ok(Url::parse(&format!("{}/inbox", actor_id))?.into())
+}
+
+pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  let mut actor_id: Url = actor_id.clone().into();
+  actor_id.set_path("site_inbox");
+  Ok(actor_id.into())
+}
+
+pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
+  let actor_id: Url = actor_id.clone().into();
+  let url = format!(
+    "{}://{}{}/inbox",
+    &actor_id.scheme(),
+    &actor_id.host_str().context(location_info!())?,
+    if let Some(port) = actor_id.port() {
+      format!(":{}", port)
+    } else {
+      String::new()
+    },
+  );
+  Ok(Url::parse(&url)?.into())
+}
+
+pub fn generate_outbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
+  Ok(Url::parse(&format!("{}/outbox", actor_id))?.into())
+}
+
+pub fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
+  Ok(Url::parse(&format!("{}/moderators", community_id))?.into())
+}
diff --git a/crates/api_common/src/websocket/chat_server.rs b/crates/api_common/src/websocket/chat_server.rs
index c8f59e3d2..b669a4fd3 100644
--- a/crates/api_common/src/websocket/chat_server.rs
+++ b/crates/api_common/src/websocket/chat_server.rs
@@ -1,14 +1,15 @@
 use crate::{
   comment::CommentResponse,
+  context::LemmyContext,
   post::PostResponse,
   websocket::{
     messages::{CaptchaItem, StandardMessage, WsMessage},
     serialize_websocket_message,
     OperationType,
     UserOperation,
+    UserOperationApub,
     UserOperationCrud,
   },
-  LemmyContext,
 };
 use actix::prelude::*;
 use anyhow::Context as acontext;
@@ -50,6 +51,13 @@ type MessageHandlerCrudType = fn(
   data: &str,
 ) -> Pin<Box<dyn Future<Output = Result<String, LemmyError>> + '_>>;
 
+type MessageHandlerApubType = fn(
+  context: LemmyContext,
+  id: ConnectionId,
+  op: UserOperationApub,
+  data: &str,
+) -> Pin<Box<dyn Future<Output = Result<String, LemmyError>> + '_>>;
+
 /// `ChatServer` manages chat rooms and responsible for coordinating chat
 /// session.
 pub struct ChatServer {
@@ -84,6 +92,7 @@ pub struct ChatServer {
 
   message_handler: MessageHandlerType,
   message_handler_crud: MessageHandlerCrudType,
+  message_handler_apub: MessageHandlerApubType,
 
   /// An HTTP Client
   client: ClientWithMiddleware,
@@ -105,6 +114,7 @@ impl ChatServer {
     pool: DbPool,
     message_handler: MessageHandlerType,
     message_handler_crud: MessageHandlerCrudType,
+    message_handler_apub: MessageHandlerApubType,
     client: ClientWithMiddleware,
     settings: Settings,
     secret: Secret,
@@ -121,6 +131,7 @@ impl ChatServer {
       captchas: Vec::new(),
       message_handler,
       message_handler_crud,
+      message_handler_apub,
       client,
       settings,
       secret,
@@ -453,16 +464,17 @@ impl ChatServer {
       None => IpAddr("blank_ip".to_string()),
     };
 
-    let context = LemmyContext {
-      pool: self.pool.clone(),
-      chat_server: ctx.address(),
-      client: self.client.clone(),
-      settings: self.settings.clone(),
-      secret: self.secret.clone(),
-      rate_limit_cell: self.rate_limit_cell.clone(),
-    };
+    let context = LemmyContext::create(
+      self.pool.clone(),
+      ctx.address(),
+      self.client.clone(),
+      self.settings.clone(),
+      self.secret.clone(),
+      self.rate_limit_cell.clone(),
+    );
     let message_handler_crud = self.message_handler_crud;
     let message_handler = self.message_handler;
+    let message_handler_apub = self.message_handler_apub;
     let rate_limiter = self.rate_limit_cell.clone();
     async move {
       let json: Value = serde_json::from_str(&msg.msg)?;
@@ -482,15 +494,21 @@ impl ChatServer {
         };
         let fut = (message_handler_crud)(context, msg.id, user_operation_crud, data);
         (passed, fut)
-      } else {
-        let user_operation = UserOperation::from_str(op)?;
+      } else if let Ok(user_operation) = UserOperation::from_str(op) {
         let passed = match user_operation {
           UserOperation::GetCaptcha => rate_limiter.post().check(ip),
-          UserOperation::Search => rate_limiter.search().check(ip),
           _ => rate_limiter.message().check(ip),
         };
         let fut = (message_handler)(context, msg.id, user_operation, data);
         (passed, fut)
+      } else {
+        let user_operation = UserOperationApub::from_str(op)?;
+        let passed = match user_operation {
+          UserOperationApub::Search => rate_limiter.search().check(ip),
+          _ => rate_limiter.message().check(ip),
+        };
+        let fut = (message_handler_apub)(context, msg.id, user_operation, data);
+        (passed, fut)
       };
 
       // if rate limit passed, execute api call future
diff --git a/crates/api_common/src/websocket/mod.rs b/crates/api_common/src/websocket/mod.rs
index d1e30f448..430027cfa 100644
--- a/crates/api_common/src/websocket/mod.rs
+++ b/crates/api_common/src/websocket/mod.rs
@@ -63,8 +63,6 @@ pub enum UserOperation {
   ApproveRegistrationApplication,
   BanPerson,
   GetBannedPersons,
-  Search,
-  ResolveObject,
   MarkAllAsRead,
   SaveUserSettings,
   TransferCommunity,
@@ -98,27 +96,23 @@ pub enum UserOperationCrud {
   // Community
   CreateCommunity,
   ListCommunities,
-  GetCommunity,
   EditCommunity,
   DeleteCommunity,
   RemoveCommunity,
   // Post
   CreatePost,
   GetPost,
-  GetPosts,
   EditPost,
   DeletePost,
   RemovePost,
   // Comment
   CreateComment,
   GetComment,
-  GetComments,
   EditComment,
   DeleteComment,
   RemoveComment,
   // User
   Register,
-  GetPersonDetails,
   DeleteAccount,
   // Private Message
   CreatePrivateMessage,
@@ -127,8 +121,20 @@ pub enum UserOperationCrud {
   DeletePrivateMessage,
 }
 
+#[derive(EnumString, Display, Debug, Clone)]
+pub enum UserOperationApub {
+  GetPosts,
+  GetCommunity,
+  GetComments,
+  GetPersonDetails,
+  Search,
+  ResolveObject,
+}
+
 pub trait OperationType {}
 
 impl OperationType for UserOperationCrud {}
 
 impl OperationType for UserOperation {}
+
+impl OperationType for UserOperationApub {}
diff --git a/crates/api_common/src/websocket/routes.rs b/crates/api_common/src/websocket/routes.rs
index 7b500ce4c..936dc9998 100644
--- a/crates/api_common/src/websocket/routes.rs
+++ b/crates/api_common/src/websocket/routes.rs
@@ -1,9 +1,9 @@
 use crate::{
+  context::LemmyContext,
   websocket::{
     chat_server::ChatServer,
     messages::{Connect, Disconnect, StandardMessage, WsMessage},
   },
-  LemmyContext,
 };
 use actix::prelude::*;
 use actix_web::{web, Error, HttpRequest, HttpResponse};
diff --git a/crates/api_common/src/websocket/send.rs b/crates/api_common/src/websocket/send.rs
index 6f3506aee..cd53955f9 100644
--- a/crates/api_common/src/websocket/send.rs
+++ b/crates/api_common/src/websocket/send.rs
@@ -1,6 +1,7 @@
 use crate::{
   comment::CommentResponse,
   community::CommunityResponse,
+  context::LemmyContext,
   post::PostResponse,
   private_message::PrivateMessageResponse,
   utils::{check_person_block, get_interface_language, send_email_to_user},
@@ -8,7 +9,6 @@ use crate::{
     messages::{SendComment, SendCommunityRoomMessage, SendPost, SendUserRoomMessage},
     OperationType,
   },
-  LemmyContext,
 };
 use lemmy_db_schema::{
   newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
diff --git a/crates/api_crud/Cargo.toml b/crates/api_crud/Cargo.toml
index 7a16aed5c..2d1774463 100644
--- a/crates/api_crud/Cargo.toml
+++ b/crates/api_crud/Cargo.toml
@@ -9,7 +9,6 @@ documentation.workspace = true
 repository.workspace = true
 
 [dependencies]
-lemmy_apub = { workspace = true }
 lemmy_utils = { workspace = true }
 lemmy_db_schema = { workspace = true, features = ["full"] }
 lemmy_db_views = { workspace = true, features = ["full"] }
diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs
index 5d9603ac3..8c61a0444 100644
--- a/crates/api_crud/src/comment/create.rs
+++ b/crates/api_crud/src/comment/create.rs
@@ -2,25 +2,21 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentResponse, CreateComment},
-  generate_local_apub_endpoint,
+  context::LemmyContext,
   utils::{
     check_community_ban,
     check_community_deleted_or_removed,
     check_post_deleted_or_removed,
+    generate_local_apub_endpoint,
     get_local_user_view_from_jwt,
     get_post,
     local_site_to_slur_regex,
+    EndpointType,
   },
   websocket::{
     send::{send_comment_ws_message, send_local_notifs},
     UserOperationCrud,
   },
-  EndpointType,
-  LemmyContext,
-};
-use lemmy_apub::{
-  objects::comment::ApubComment,
-  protocol::activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
 };
 use lemmy_db_schema::{
   source::{
@@ -157,16 +153,6 @@ impl PerformCrud for CreateComment {
       .await
       .map_err(|e| LemmyError::from_error_message(e, "couldnt_like_comment"))?;
 
-    let apub_comment: ApubComment = updated_comment.into();
-    CreateOrUpdateNote::send(
-      apub_comment.clone(),
-      &local_user_view.person.clone().into(),
-      CreateOrUpdateType::Create,
-      context,
-      &mut 0,
-    )
-    .await?;
-
     // If its a reply, mark the parent as read
     if let Some(parent) = parent_opt {
       let parent_id = parent.id;
diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs
index f14d0d415..211d04776 100644
--- a/crates/api_crud/src/comment/delete.rs
+++ b/crates/api_crud/src/comment/delete.rs
@@ -2,18 +2,16 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentResponse, DeleteComment},
+  context::LemmyContext,
   utils::{check_community_ban, get_local_user_view_from_jwt},
   websocket::{
     send::{send_comment_ws_message, send_local_notifs},
     UserOperationCrud,
   },
-  LemmyContext,
 };
-use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
   source::{
     comment::{Comment, CommentUpdateForm},
-    community::Community,
     post::Post,
   },
   traits::Crud,
@@ -88,19 +86,6 @@ impl PerformCrud for DeleteComment {
     )
     .await?;
 
-    // Send the apub message
-    let community = Community::read(context.pool(), orig_comment.post.community_id).await?;
-    let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      community,
-      deletable,
-      None,
-      deleted,
-      context,
-    )
-    .await?;
-
     Ok(res)
   }
 }
diff --git a/crates/api_crud/src/comment/mod.rs b/crates/api_crud/src/comment/mod.rs
index 1e8328034..d3d789a02 100644
--- a/crates/api_crud/src/comment/mod.rs
+++ b/crates/api_crud/src/comment/mod.rs
@@ -1,6 +1,5 @@
 mod create;
 mod delete;
-mod list;
 mod read;
 mod remove;
 mod update;
diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs
index 98b3431c2..c0136f06c 100644
--- a/crates/api_crud/src/comment/read.rs
+++ b/crates/api_crud/src/comment/read.rs
@@ -2,8 +2,8 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentResponse, GetComment},
+  context::LemmyContext,
   utils::{check_private_instance, get_local_user_view_from_jwt_opt},
-  LemmyContext,
 };
 use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_db_views::structs::CommentView;
diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs
index 09e5fdfca..1032f54c9 100644
--- a/crates/api_crud/src/comment/remove.rs
+++ b/crates/api_crud/src/comment/remove.rs
@@ -2,18 +2,16 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentResponse, RemoveComment},
+  context::LemmyContext,
   utils::{check_community_ban, get_local_user_view_from_jwt, is_mod_or_admin},
   websocket::{
     send::{send_comment_ws_message, send_local_notifs},
     UserOperationCrud,
   },
-  LemmyContext,
 };
-use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
   source::{
     comment::{Comment, CommentUpdateForm},
-    community::Community,
     moderator::{ModRemoveComment, ModRemoveCommentForm},
     post::Post,
   },
@@ -96,19 +94,6 @@ impl PerformCrud for RemoveComment {
     )
     .await?;
 
-    // Send the apub message
-    let community = Community::read(context.pool(), orig_comment.post.community_id).await?;
-    let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      community,
-      deletable,
-      data.reason.clone().or_else(|| Some(String::new())),
-      removed,
-      context,
-    )
-    .await?;
-
     Ok(res)
   }
 }
diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs
index d1fcccd9c..88de41158 100644
--- a/crates/api_crud/src/comment/update.rs
+++ b/crates/api_crud/src/comment/update.rs
@@ -2,6 +2,7 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{CommentResponse, EditComment},
+  context::LemmyContext,
   utils::{
     check_community_ban,
     check_community_deleted_or_removed,
@@ -14,11 +15,6 @@ use lemmy_api_common::{
     send::{send_comment_ws_message, send_local_notifs},
     UserOperationCrud,
   },
-  LemmyContext,
-};
-use lemmy_apub::protocol::activities::{
-  create_or_update::note::CreateOrUpdateNote,
-  CreateOrUpdateType,
 };
 use lemmy_db_schema::{
   source::{
@@ -114,16 +110,6 @@ impl PerformCrud for EditComment {
     )
     .await?;
 
-    // Send the apub update
-    CreateOrUpdateNote::send(
-      updated_comment.into(),
-      &local_user_view.person.into(),
-      CreateOrUpdateType::Update,
-      context,
-      &mut 0,
-    )
-    .await?;
-
     send_comment_ws_message(
       data.comment_id,
       UserOperationCrud::EditComment,
diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs
index 11a307668..cf894edbd 100644
--- a/crates/api_crud/src/community/create.rs
+++ b/crates/api_crud/src/community/create.rs
@@ -1,17 +1,20 @@
 use crate::PerformCrud;
-use activitypub_federation::core::{object_id::ObjectId, signatures::generate_actor_keypair};
+use activitypub_federation::core::signatures::generate_actor_keypair;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{CommunityResponse, CreateCommunity},
-  generate_followers_url,
-  generate_inbox_url,
-  generate_local_apub_endpoint,
-  generate_shared_inbox_url,
-  utils::{get_local_user_view_from_jwt, is_admin, local_site_to_slur_regex},
-  EndpointType,
-  LemmyContext,
+  context::LemmyContext,
+  utils::{
+    generate_followers_url,
+    generate_inbox_url,
+    generate_local_apub_endpoint,
+    generate_shared_inbox_url,
+    get_local_user_view_from_jwt,
+    is_admin,
+    local_site_to_slur_regex,
+    EndpointType,
+  },
 };
-use lemmy_apub::objects::community::ApubCommunity;
 use lemmy_db_schema::{
   source::community::{
     Community,
@@ -21,7 +24,7 @@ use lemmy_db_schema::{
     CommunityModerator,
     CommunityModeratorForm,
   },
-  traits::{Crud, Followable, Joinable},
+  traits::{ApubActor, Crud, Followable, Joinable},
   utils::diesel_option_overwrite_to_url_create,
 };
 use lemmy_db_views::structs::SiteView;
@@ -73,9 +76,8 @@ impl PerformCrud for CreateCommunity {
       &data.name,
       &context.settings().get_protocol_and_hostname(),
     )?;
-    let community_actor_id_wrapped = ObjectId::<ApubCommunity>::new(community_actor_id.clone());
-    let community_dupe = community_actor_id_wrapped.dereference_local(context).await;
-    if community_dupe.is_ok() {
+    let community_dupe = Community::read_from_apub_id(context.pool(), &community_actor_id).await?;
+    if community_dupe.is_some() {
       return Err(LemmyError::from_message("community_already_exists"));
     }
 
diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs
index 7d3122ab4..84de55477 100644
--- a/crates/api_crud/src/community/delete.rs
+++ b/crates/api_crud/src/community/delete.rs
@@ -2,11 +2,10 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{CommunityResponse, DeleteCommunity},
+  context::LemmyContext,
   utils::get_local_user_view_from_jwt,
   websocket::{send::send_community_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
   source::community::{Community, CommunityUpdateForm},
   traits::Crud,
@@ -41,7 +40,7 @@ impl PerformCrud for DeleteCommunity {
     // Do the delete
     let community_id = data.community_id;
     let deleted = data.deleted;
-    let updated_community = Community::update(
+    Community::update(
       context.pool(),
       community_id,
       &CommunityUpdateForm::builder()
@@ -60,18 +59,6 @@ impl PerformCrud for DeleteCommunity {
     )
     .await?;
 
-    // Send apub messages
-    let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      updated_community,
-      deletable,
-      None,
-      deleted,
-      context,
-    )
-    .await?;
-
     Ok(res)
   }
 }
diff --git a/crates/api_crud/src/community/list.rs b/crates/api_crud/src/community/list.rs
index f77ebce75..4d2e7046f 100644
--- a/crates/api_crud/src/community/list.rs
+++ b/crates/api_crud/src/community/list.rs
@@ -2,8 +2,8 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{ListCommunities, ListCommunitiesResponse},
+  context::LemmyContext,
   utils::{check_private_instance, get_local_user_view_from_jwt_opt},
-  LemmyContext,
 };
 use lemmy_db_schema::{source::local_site::LocalSite, traits::DeleteableOrRemoveable};
 use lemmy_db_views_actor::community_view::CommunityQuery;
diff --git a/crates/api_crud/src/community/mod.rs b/crates/api_crud/src/community/mod.rs
index 1e8328034..3fc741652 100644
--- a/crates/api_crud/src/community/mod.rs
+++ b/crates/api_crud/src/community/mod.rs
@@ -1,6 +1,5 @@
 mod create;
 mod delete;
 mod list;
-mod read;
 mod remove;
 mod update;
diff --git a/crates/api_crud/src/community/remove.rs b/crates/api_crud/src/community/remove.rs
index 77c2bde30..215b39cf4 100644
--- a/crates/api_crud/src/community/remove.rs
+++ b/crates/api_crud/src/community/remove.rs
@@ -2,11 +2,10 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{CommunityResponse, RemoveCommunity},
+  context::LemmyContext,
   utils::{get_local_user_view_from_jwt, is_admin},
   websocket::{send::send_community_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
   source::{
     community::{Community, CommunityUpdateForm},
@@ -36,7 +35,7 @@ impl PerformCrud for RemoveCommunity {
     // Do the remove
     let community_id = data.community_id;
     let removed = data.removed;
-    let updated_community = Community::update(
+    Community::update(
       context.pool(),
       community_id,
       &CommunityUpdateForm::builder()
@@ -66,17 +65,6 @@ impl PerformCrud for RemoveCommunity {
     )
     .await?;
 
-    // Apub messages
-    let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      updated_community,
-      deletable,
-      data.reason.clone().or_else(|| Some(String::new())),
-      removed,
-      context,
-    )
-    .await?;
     Ok(res)
   }
 }
diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs
index 2bde94ea6..9059a4a6f 100644
--- a/crates/api_crud/src/community/update.rs
+++ b/crates/api_crud/src/community/update.rs
@@ -2,11 +2,10 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{CommunityResponse, EditCommunity},
+  context::LemmyContext,
   utils::{get_local_user_view_from_jwt, local_site_to_slur_regex},
   websocket::{send::send_community_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
 use lemmy_db_schema::{
   newtypes::PersonId,
   source::{
@@ -74,17 +73,10 @@ impl PerformCrud for EditCommunity {
       .build();
 
     let community_id = data.community_id;
-    let updated_community = Community::update(context.pool(), community_id, &community_form)
+    Community::update(context.pool(), community_id, &community_form)
       .await
       .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?;
 
-    UpdateCommunity::send(
-      updated_community.into(),
-      &local_user_view.person.into(),
-      context,
-    )
-    .await?;
-
     let op = UserOperationCrud::EditCommunity;
     send_community_ws_message(data.community_id, op, websocket_id, None, context).await
   }
diff --git a/crates/api_crud/src/lib.rs b/crates/api_crud/src/lib.rs
index fde81eddb..d37dfbee2 100644
--- a/crates/api_crud/src/lib.rs
+++ b/crates/api_crud/src/lib.rs
@@ -1,28 +1,6 @@
-use actix_web::{web, web::Data};
-use lemmy_api_common::{
-  comment::{CreateComment, DeleteComment, EditComment, GetComment, GetComments, RemoveComment},
-  community::{
-    CreateCommunity,
-    DeleteCommunity,
-    EditCommunity,
-    GetCommunity,
-    ListCommunities,
-    RemoveCommunity,
-  },
-  person::{DeleteAccount, GetPersonDetails, Register},
-  post::{CreatePost, DeletePost, EditPost, GetPost, GetPosts, RemovePost},
-  private_message::{
-    CreatePrivateMessage,
-    DeletePrivateMessage,
-    EditPrivateMessage,
-    GetPrivateMessages,
-  },
-  site::{CreateSite, EditSite, GetSite},
-  websocket::{serialize_websocket_message, UserOperationCrud},
-  LemmyContext,
-};
+use actix_web::web::Data;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::{error::LemmyError, ConnectionId};
-use serde::Deserialize;
 
 mod comment;
 mod community;
@@ -41,115 +19,3 @@ pub trait PerformCrud {
     websocket_id: Option<ConnectionId>,
   ) -> Result<Self::Response, LemmyError>;
 }
-
-pub async fn match_websocket_operation_crud(
-  context: LemmyContext,
-  id: ConnectionId,
-  op: UserOperationCrud,
-  data: &str,
-) -> Result<String, LemmyError> {
-  //TODO: handle commented out actions in crud crate
-
-  match op {
-    // User ops
-    UserOperationCrud::Register => do_websocket_operation::<Register>(context, id, op, data).await,
-    UserOperationCrud::GetPersonDetails => {
-      do_websocket_operation::<GetPersonDetails>(context, id, op, data).await
-    }
-    UserOperationCrud::DeleteAccount => {
-      do_websocket_operation::<DeleteAccount>(context, id, op, data).await
-    }
-
-    // Private Message ops
-    UserOperationCrud::CreatePrivateMessage => {
-      do_websocket_operation::<CreatePrivateMessage>(context, id, op, data).await
-    }
-    UserOperationCrud::EditPrivateMessage => {
-      do_websocket_operation::<EditPrivateMessage>(context, id, op, data).await
-    }
-    UserOperationCrud::DeletePrivateMessage => {
-      do_websocket_operation::<DeletePrivateMessage>(context, id, op, data).await
-    }
-    UserOperationCrud::GetPrivateMessages => {
-      do_websocket_operation::<GetPrivateMessages>(context, id, op, data).await
-    }
-
-    // Site ops
-    UserOperationCrud::CreateSite => {
-      do_websocket_operation::<CreateSite>(context, id, op, data).await
-    }
-    UserOperationCrud::EditSite => do_websocket_operation::<EditSite>(context, id, op, data).await,
-    UserOperationCrud::GetSite => do_websocket_operation::<GetSite>(context, id, op, data).await,
-
-    // Community ops
-    UserOperationCrud::GetCommunity => {
-      do_websocket_operation::<GetCommunity>(context, id, op, data).await
-    }
-    UserOperationCrud::ListCommunities => {
-      do_websocket_operation::<ListCommunities>(context, id, op, data).await
-    }
-    UserOperationCrud::CreateCommunity => {
-      do_websocket_operation::<CreateCommunity>(context, id, op, data).await
-    }
-    UserOperationCrud::EditCommunity => {
-      do_websocket_operation::<EditCommunity>(context, id, op, data).await
-    }
-    UserOperationCrud::DeleteCommunity => {
-      do_websocket_operation::<DeleteCommunity>(context, id, op, data).await
-    }
-    UserOperationCrud::RemoveCommunity => {
-      do_websocket_operation::<RemoveCommunity>(context, id, op, data).await
-    }
-
-    // Post ops
-    UserOperationCrud::CreatePost => {
-      do_websocket_operation::<CreatePost>(context, id, op, data).await
-    }
-    UserOperationCrud::GetPost => do_websocket_operation::<GetPost>(context, id, op, data).await,
-    UserOperationCrud::GetPosts => do_websocket_operation::<GetPosts>(context, id, op, data).await,
-    UserOperationCrud::EditPost => do_websocket_operation::<EditPost>(context, id, op, data).await,
-    UserOperationCrud::DeletePost => {
-      do_websocket_operation::<DeletePost>(context, id, op, data).await
-    }
-    UserOperationCrud::RemovePost => {
-      do_websocket_operation::<RemovePost>(context, id, op, data).await
-    }
-
-    // Comment ops
-    UserOperationCrud::CreateComment => {
-      do_websocket_operation::<CreateComment>(context, id, op, data).await
-    }
-    UserOperationCrud::EditComment => {
-      do_websocket_operation::<EditComment>(context, id, op, data).await
-    }
-    UserOperationCrud::DeleteComment => {
-      do_websocket_operation::<DeleteComment>(context, id, op, data).await
-    }
-    UserOperationCrud::RemoveComment => {
-      do_websocket_operation::<RemoveComment>(context, id, op, data).await
-    }
-    UserOperationCrud::GetComment => {
-      do_websocket_operation::<GetComment>(context, id, op, data).await
-    }
-    UserOperationCrud::GetComments => {
-      do_websocket_operation::<GetComments>(context, id, op, data).await
-    }
-  }
-}
-
-async fn do_websocket_operation<'a, 'b, Data>(
-  context: LemmyContext,
-  id: ConnectionId,
-  op: UserOperationCrud,
-  data: &str,
-) -> Result<String, LemmyError>
-where
-  for<'de> Data: Deserialize<'de> + 'a,
-  Data: PerformCrud,
-{
-  let parsed_data: Data = serde_json::from_str(data)?;
-  let res = parsed_data
-    .perform(&web::Data::new(context), Some(id))
-    .await?;
-  serialize_websocket_message(&op, &res)
-}
diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs
index 46d4a7f64..af62135e2 100644
--- a/crates/api_crud/src/post/create.rs
+++ b/crates/api_crud/src/post/create.rs
@@ -1,24 +1,20 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  generate_local_apub_endpoint,
+  context::LemmyContext,
   post::{CreatePost, PostResponse},
   request::fetch_site_data,
   utils::{
     check_community_ban,
     check_community_deleted_or_removed,
+    generate_local_apub_endpoint,
     get_local_user_view_from_jwt,
     honeypot_check,
     local_site_to_slur_regex,
     mark_post_as_read,
+    EndpointType,
   },
   websocket::{send::send_post_ws_message, UserOperationCrud},
-  EndpointType,
-  LemmyContext,
-};
-use lemmy_apub::{
-  objects::post::ApubPost,
-  protocol::activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
 };
 use lemmy_db_schema::{
   impls::actor_language::default_post_language,
@@ -174,15 +170,6 @@ impl PerformCrud for CreatePost {
       }
     }
 
-    let apub_post: ApubPost = updated_post.into();
-    CreateOrUpdatePage::send(
-      apub_post.clone(),
-      &local_user_view.person.clone().into(),
-      CreateOrUpdateType::Create,
-      context,
-    )
-    .await?;
-
     send_post_ws_message(
       inserted_post.id,
       UserOperationCrud::CreatePost,
diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs
index de1490b1c..57e5adb15 100644
--- a/crates/api_crud/src/post/delete.rs
+++ b/crates/api_crud/src/post/delete.rs
@@ -1,17 +1,13 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{DeletePost, PostResponse},
   utils::{check_community_ban, check_community_deleted_or_removed, get_local_user_view_from_jwt},
   websocket::{send::send_post_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
-  source::{
-    community::Community,
-    post::{Post, PostUpdateForm},
-  },
+  source::post::{Post, PostUpdateForm},
   traits::Crud,
 };
 use lemmy_utils::{error::LemmyError, ConnectionId};
@@ -54,7 +50,7 @@ impl PerformCrud for DeletePost {
     // Update the post
     let post_id = data.post_id;
     let deleted = data.deleted;
-    let updated_post = Post::update(
+    Post::update(
       context.pool(),
       post_id,
       &PostUpdateForm::builder().deleted(Some(deleted)).build(),
@@ -70,18 +66,6 @@ impl PerformCrud for DeletePost {
     )
     .await?;
 
-    // apub updates
-    let community = Community::read(context.pool(), orig_post.community_id).await?;
-    let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      community,
-      deletable,
-      None,
-      deleted,
-      context,
-    )
-    .await?;
     Ok(res)
   }
 }
diff --git a/crates/api_crud/src/post/mod.rs b/crates/api_crud/src/post/mod.rs
index 1e8328034..d3d789a02 100644
--- a/crates/api_crud/src/post/mod.rs
+++ b/crates/api_crud/src/post/mod.rs
@@ -1,6 +1,5 @@
 mod create;
 mod delete;
-mod list;
 mod read;
 mod remove;
 mod update;
diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs
index 44d1be63a..c6747c5e8 100644
--- a/crates/api_crud/src/post/read.rs
+++ b/crates/api_crud/src/post/read.rs
@@ -1,10 +1,10 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{GetPost, GetPostResponse},
   utils::{check_private_instance, get_local_user_view_from_jwt_opt, mark_post_as_read},
   websocket::messages::GetPostUsersOnline,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs
index 525c08a68..66af2ca95 100644
--- a/crates/api_crud/src/post/remove.rs
+++ b/crates/api_crud/src/post/remove.rs
@@ -1,15 +1,13 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{PostResponse, RemovePost},
   utils::{check_community_ban, get_local_user_view_from_jwt, is_mod_or_admin},
   websocket::{send::send_post_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
 use lemmy_db_schema::{
   source::{
-    community::Community,
     moderator::{ModRemovePost, ModRemovePostForm},
     post::{Post, PostUpdateForm},
   },
@@ -52,7 +50,7 @@ impl PerformCrud for RemovePost {
     // Update the post
     let post_id = data.post_id;
     let removed = data.removed;
-    let updated_post = Post::update(
+    Post::update(
       context.pool(),
       post_id,
       &PostUpdateForm::builder().removed(Some(removed)).build(),
@@ -77,18 +75,6 @@ impl PerformCrud for RemovePost {
     )
     .await?;
 
-    // apub updates
-    let community = Community::read(context.pool(), orig_post.community_id).await?;
-    let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
-    send_apub_delete_in_community(
-      local_user_view.person,
-      community,
-      deletable,
-      data.reason.clone().or_else(|| Some(String::new())),
-      removed,
-      context,
-    )
-    .await?;
     Ok(res)
   }
 }
diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs
index 4c3038c87..9337c5952 100644
--- a/crates/api_crud/src/post/update.rs
+++ b/crates/api_crud/src/post/update.rs
@@ -1,6 +1,7 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{EditPost, PostResponse},
   request::fetch_site_data,
   utils::{
@@ -10,11 +11,6 @@ use lemmy_api_common::{
     local_site_to_slur_regex,
   },
   websocket::{send::send_post_ws_message, UserOperationCrud},
-  LemmyContext,
-};
-use lemmy_apub::protocol::activities::{
-  create_or_update::page::CreateOrUpdatePage,
-  CreateOrUpdateType,
 };
 use lemmy_db_schema::{
   source::{
@@ -109,27 +105,15 @@ impl PerformCrud for EditPost {
 
     let post_id = data.post_id;
     let res = Post::update(context.pool(), post_id, &post_form).await;
-    let updated_post: Post = match res {
-      Ok(post) => post,
-      Err(e) => {
-        let err_type = if e.to_string() == "value too long for type character varying(200)" {
-          "post_title_too_long"
-        } else {
-          "couldnt_update_post"
-        };
-
-        return Err(LemmyError::from_error_message(e, err_type));
-      }
-    };
-
-    // Send apub update
-    CreateOrUpdatePage::send(
-      updated_post.into(),
-      &local_user_view.person.clone().into(),
-      CreateOrUpdateType::Update,
-      context,
-    )
-    .await?;
+    if let Err(e) = res {
+      let err_type = if e.to_string() == "value too long for type character varying(200)" {
+        "post_title_too_long"
+      } else {
+        "couldnt_update_post"
+      };
+
+      return Err(LemmyError::from_error_message(e, err_type));
+    }
 
     send_post_ws_message(
       data.post_id,
diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs
index 03d994c08..25c7be467 100644
--- a/crates/api_crud/src/private_message/create.rs
+++ b/crates/api_crud/src/private_message/create.rs
@@ -1,22 +1,18 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  generate_local_apub_endpoint,
+  context::LemmyContext,
   private_message::{CreatePrivateMessage, PrivateMessageResponse},
   utils::{
     check_person_block,
+    generate_local_apub_endpoint,
     get_interface_language,
     get_local_user_view_from_jwt,
     local_site_to_slur_regex,
     send_email_to_user,
+    EndpointType,
   },
   websocket::{send::send_pm_ws_message, UserOperationCrud},
-  EndpointType,
-  LemmyContext,
-};
-use lemmy_apub::protocol::activities::{
-  create_or_update::chat_message::CreateOrUpdateChatMessage,
-  CreateOrUpdateType,
 };
 use lemmy_db_schema::{
   source::{
@@ -74,7 +70,7 @@ impl PerformCrud for CreatePrivateMessage {
       &inserted_private_message_id.to_string(),
       &protocol_and_hostname,
     )?;
-    let updated_private_message = PrivateMessage::update(
+    PrivateMessage::update(
       context.pool(),
       inserted_private_message.id,
       &PrivateMessageUpdateForm::builder()
@@ -84,14 +80,6 @@ impl PerformCrud for CreatePrivateMessage {
     .await
     .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_private_message"))?;
 
-    CreateOrUpdateChatMessage::send(
-      updated_private_message.into(),
-      &local_user_view.person.into(),
-      CreateOrUpdateType::Create,
-      context,
-    )
-    .await?;
-
     let res = send_pm_ws_message(
       inserted_private_message.id,
       UserOperationCrud::CreatePrivateMessage,
diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs
index ad6b78d3e..4f58065a3 100644
--- a/crates/api_crud/src/private_message/delete.rs
+++ b/crates/api_crud/src/private_message/delete.rs
@@ -1,12 +1,11 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   private_message::{DeletePrivateMessage, PrivateMessageResponse},
   utils::get_local_user_view_from_jwt,
   websocket::{send::send_pm_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_apub::activities::deletion::send_apub_delete_private_message;
 use lemmy_db_schema::{
   source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
   traits::Crud,
@@ -37,7 +36,7 @@ impl PerformCrud for DeletePrivateMessage {
     // Doing the update
     let private_message_id = data.private_message_id;
     let deleted = data.deleted;
-    let updated_private_message = PrivateMessage::update(
+    PrivateMessage::update(
       context.pool(),
       private_message_id,
       &PrivateMessageUpdateForm::builder()
@@ -47,15 +46,6 @@ impl PerformCrud for DeletePrivateMessage {
     .await
     .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;
 
-    // Send the apub update
-    send_apub_delete_private_message(
-      &local_user_view.person.into(),
-      updated_private_message,
-      data.deleted,
-      context,
-    )
-    .await?;
-
     let op = UserOperationCrud::DeletePrivateMessage;
     send_pm_ws_message(data.private_message_id, op, websocket_id, context).await
   }
diff --git a/crates/api_crud/src/private_message/read.rs b/crates/api_crud/src/private_message/read.rs
index 88bb08300..286802745 100644
--- a/crates/api_crud/src/private_message/read.rs
+++ b/crates/api_crud/src/private_message/read.rs
@@ -1,9 +1,9 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   private_message::{GetPrivateMessages, PrivateMessagesResponse},
   utils::get_local_user_view_from_jwt,
-  LemmyContext,
 };
 use lemmy_db_schema::traits::DeleteableOrRemoveable;
 use lemmy_db_views::private_message_view::PrivateMessageQuery;
diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs
index b6e08c9c6..f6f8c8f03 100644
--- a/crates/api_crud/src/private_message/update.rs
+++ b/crates/api_crud/src/private_message/update.rs
@@ -1,14 +1,10 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   private_message::{EditPrivateMessage, PrivateMessageResponse},
   utils::{get_local_user_view_from_jwt, local_site_to_slur_regex},
   websocket::{send::send_pm_ws_message, UserOperationCrud},
-  LemmyContext,
-};
-use lemmy_apub::protocol::activities::{
-  create_or_update::chat_message::CreateOrUpdateChatMessage,
-  CreateOrUpdateType,
 };
 use lemmy_db_schema::{
   source::{
@@ -45,7 +41,7 @@ impl PerformCrud for EditPrivateMessage {
     // Doing the update
     let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site));
     let private_message_id = data.private_message_id;
-    let updated_private_message = PrivateMessage::update(
+    PrivateMessage::update(
       context.pool(),
       private_message_id,
       &PrivateMessageUpdateForm::builder()
@@ -56,15 +52,6 @@ impl PerformCrud for EditPrivateMessage {
     .await
     .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;
 
-    // Send the apub update
-    CreateOrUpdateChatMessage::send(
-      updated_private_message.into(),
-      &local_user_view.person.into(),
-      CreateOrUpdateType::Update,
-      context,
-    )
-    .await?;
-
     let op = UserOperationCrud::EditPrivateMessage;
     send_pm_ws_message(data.private_message_id, op, websocket_id, context).await
   }
diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs
index 34ac404a9..d04343b0c 100644
--- a/crates/api_crud/src/site/create.rs
+++ b/crates/api_crud/src/site/create.rs
@@ -2,16 +2,16 @@ use crate::PerformCrud;
 use activitypub_federation::core::signatures::generate_actor_keypair;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  generate_site_inbox_url,
+  context::LemmyContext,
   site::{CreateSite, SiteResponse},
   utils::{
+    generate_site_inbox_url,
     get_local_user_view_from_jwt,
     is_admin,
     local_site_rate_limit_to_rate_limit_config,
     local_site_to_slur_regex,
     site_description_length_check,
   },
-  LemmyContext,
 };
 use lemmy_db_schema::{
   newtypes::DbUrl,
diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs
index 8f27bb2ee..55dcd8fb2 100644
--- a/crates/api_crud/src/site/read.rs
+++ b/crates/api_crud/src/site/read.rs
@@ -1,10 +1,10 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{GetSite, GetSiteResponse, MyUserInfo},
   utils::{build_federated_instances, get_local_user_settings_view_from_jwt_opt},
   websocket::messages::GetUsersOnline,
-  LemmyContext,
 };
 use lemmy_db_schema::source::{actor_language::SiteLanguage, language::Language, tagline::Tagline};
 use lemmy_db_views::structs::{LocalUserDiscussionLanguageView, SiteView};
diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs
index e738a2513..4a96925ed 100644
--- a/crates/api_crud/src/site/update.rs
+++ b/crates/api_crud/src/site/update.rs
@@ -1,6 +1,7 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{EditSite, SiteResponse},
   utils::{
     get_local_user_view_from_jwt,
@@ -10,7 +11,6 @@ use lemmy_api_common::{
     site_description_length_check,
   },
   websocket::{messages::SendAllMessage, UserOperationCrud},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs
index 0521ffe0c..34b3b69e4 100644
--- a/crates/api_crud/src/user/create.rs
+++ b/crates/api_crud/src/user/create.rs
@@ -2,20 +2,20 @@ use crate::PerformCrud;
 use activitypub_federation::core::signatures::generate_actor_keypair;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  generate_inbox_url,
-  generate_local_apub_endpoint,
-  generate_shared_inbox_url,
+  context::LemmyContext,
   person::{LoginResponse, Register},
   utils::{
+    generate_inbox_url,
+    generate_local_apub_endpoint,
+    generate_shared_inbox_url,
     honeypot_check,
     local_site_to_slur_regex,
     password_length_check,
     send_new_applicant_email_to_admins,
     send_verification_email,
+    EndpointType,
   },
   websocket::messages::CheckCaptcha,
-  EndpointType,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   aggregates::structs::PersonAggregates,
diff --git a/crates/api_crud/src/user/delete.rs b/crates/api_crud/src/user/delete.rs
index 87ad78e44..b49695eac 100644
--- a/crates/api_crud/src/user/delete.rs
+++ b/crates/api_crud/src/user/delete.rs
@@ -2,11 +2,10 @@ use crate::PerformCrud;
 use actix_web::web::Data;
 use bcrypt::verify;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{DeleteAccount, DeleteAccountResponse},
-  utils::{delete_user_account, get_local_user_view_from_jwt},
-  LemmyContext,
+  utils::get_local_user_view_from_jwt,
 };
-use lemmy_apub::protocol::activities::deletion::delete_user::DeleteUser;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
@@ -33,15 +32,6 @@ impl PerformCrud for DeleteAccount {
       return Err(LemmyError::from_message("password_incorrect"));
     }
 
-    delete_user_account(
-      local_user_view.person.id,
-      context.pool(),
-      context.settings(),
-      context.client(),
-    )
-    .await?;
-    DeleteUser::send(&local_user_view.person.into(), context).await?;
-
     Ok(DeleteAccountResponse {})
   }
 }
diff --git a/crates/api_crud/src/user/mod.rs b/crates/api_crud/src/user/mod.rs
index 843072411..aeaae9ddd 100644
--- a/crates/api_crud/src/user/mod.rs
+++ b/crates/api_crud/src/user/mod.rs
@@ -1,3 +1,2 @@
 mod create;
 mod delete;
-mod read;
diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs
index c77bfde35..b5f03eaf1 100644
--- a/crates/apub/src/activities/block/block_user.rs
+++ b/crates/apub/src/activities/block/block_user.rs
@@ -24,8 +24,8 @@ use activitystreams_kinds::{activity::BlockType, public};
 use anyhow::anyhow;
 use chrono::NaiveDateTime;
 use lemmy_api_common::{
+  context::LemmyContext,
   utils::{remove_user_data, remove_user_data_in_community},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/apub/src/activities/block/mod.rs b/crates/apub/src/activities/block/mod.rs
index 5a8c48f13..8adc25012 100644
--- a/crates/apub/src/activities/block/mod.rs
+++ b/crates/apub/src/activities/block/mod.rs
@@ -1,13 +1,27 @@
 use crate::{
-  objects::{community::ApubCommunity, instance::ApubSite},
-  protocol::objects::{group::Group, instance::Instance},
+  objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
+  protocol::{
+    activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
+    objects::{group::Group, instance::Instance},
+  },
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
 use chrono::NaiveDateTime;
-use lemmy_api_common::LemmyContext;
-use lemmy_db_schema::{source::site::Site, utils::DbPool};
-use lemmy_utils::error::LemmyError;
+use lemmy_api_common::{
+  community::{BanFromCommunity, BanFromCommunityResponse},
+  context::LemmyContext,
+  person::{BanPerson, BanPersonResponse},
+  utils::get_local_user_view_from_jwt,
+};
+use lemmy_db_schema::{
+  source::{community::Community, person::Person, site::Site},
+  traits::Crud,
+  utils::DbPool,
+};
+use lemmy_db_views::structs::SiteView;
+use lemmy_utils::{error::LemmyError, utils::naive_from_unix};
 use serde::Deserialize;
 use url::Url;
 
@@ -123,3 +137,90 @@ async fn generate_cc(target: &SiteOrCommunity, pool: &DbPool) -> Result<Vec<Url>
     SiteOrCommunity::Community(c) => vec![c.actor_id()],
   })
 }
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for BanPerson {
+  type Response = BanPersonResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let person = Person::read(context.pool(), request.person_id).await?;
+    let site = SiteOrCommunity::Site(SiteView::read_local(context.pool()).await?.site.into());
+    let expires = request.expires.map(naive_from_unix);
+
+    // if the action affects a local user, federate to other instances
+    if person.local {
+      if request.ban {
+        BlockUser::send(
+          &site,
+          &person.into(),
+          &local_user_view.person.into(),
+          request.remove_data.unwrap_or(false),
+          request.reason.clone(),
+          expires,
+          context,
+        )
+        .await
+      } else {
+        UndoBlockUser::send(
+          &site,
+          &person.into(),
+          &local_user_view.person.into(),
+          request.reason.clone(),
+          context,
+        )
+        .await
+      }
+    } else {
+      Ok(())
+    }
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for BanFromCommunity {
+  type Response = BanFromCommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community: ApubCommunity = Community::read(context.pool(), request.community_id)
+      .await?
+      .into();
+    let banned_person: ApubPerson = Person::read(context.pool(), request.person_id)
+      .await?
+      .into();
+    let expires = request.expires.map(naive_from_unix);
+
+    if request.ban {
+      BlockUser::send(
+        &SiteOrCommunity::Community(community),
+        &banned_person,
+        &local_user_view.person.clone().into(),
+        request.remove_data.unwrap_or(false),
+        request.reason.clone(),
+        expires,
+        context,
+      )
+      .await
+    } else {
+      UndoBlockUser::send(
+        &SiteOrCommunity::Community(community),
+        &banned_person,
+        &local_user_view.person.clone().into(),
+        request.reason.clone(),
+        context,
+      )
+      .await
+    }
+  }
+}
diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs
index 56f794dc1..315667ac6 100644
--- a/crates/apub/src/activities/block/undo_block_user.rs
+++ b/crates/apub/src/activities/block/undo_block_user.rs
@@ -19,7 +19,7 @@ use activitypub_federation::{
   utils::verify_domains_match,
 };
 use activitystreams_kinds::{activity::UndoType, public};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{
     community::{CommunityPersonBan, CommunityPersonBanForm},
diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs
index 3718ea680..1bc31b48c 100644
--- a/crates/apub/src/activities/community/add_mod.rs
+++ b/crates/apub/src/activities/community/add_mod.rs
@@ -10,8 +10,12 @@ use crate::{
   activity_lists::AnnouncableActivities,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
-  protocol::{activities::community::add_mod::AddMod, InCommunity},
+  protocol::{
+    activities::community::{add_mod::AddMod, remove_mod::RemoveMod},
+    InCommunity,
+  },
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -19,11 +23,16 @@ use activitypub_federation::{
   traits::{ActivityHandler, Actor},
 };
 use activitystreams_kinds::{activity::AddType, public};
-use lemmy_api_common::{generate_moderators_url, LemmyContext};
+use lemmy_api_common::{
+  community::{AddModToCommunity, AddModToCommunityResponse},
+  context::LemmyContext,
+  utils::{generate_moderators_url, get_local_user_view_from_jwt},
+};
 use lemmy_db_schema::{
   source::{
-    community::{CommunityModerator, CommunityModeratorForm},
+    community::{Community, CommunityModerator, CommunityModeratorForm},
     moderator::{ModAddCommunity, ModAddCommunityForm},
+    person::Person,
   },
   traits::{Crud, Joinable},
 };
@@ -134,3 +143,40 @@ impl ActivityHandler for AddMod {
     Ok(())
   }
 }
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for AddModToCommunity {
+  type Response = AddModToCommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community: ApubCommunity = Community::read(context.pool(), request.community_id)
+      .await?
+      .into();
+    let updated_mod: ApubPerson = Person::read(context.pool(), request.person_id)
+      .await?
+      .into();
+    if request.added {
+      AddMod::send(
+        &community,
+        &updated_mod,
+        &local_user_view.person.into(),
+        context,
+      )
+      .await
+    } else {
+      RemoveMod::send(
+        &community,
+        &updated_mod,
+        &local_user_view.person.into(),
+        context,
+      )
+      .await
+    }
+  }
+}
diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs
index 1ca1aa848..306913aa6 100644
--- a/crates/apub/src/activities/community/announce.rs
+++ b/crates/apub/src/activities/community/announce.rs
@@ -18,7 +18,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
 use activitystreams_kinds::{activity::AnnounceType, public};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde_json::Value;
 use tracing::debug;
diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs
index 3d0bb4c3d..4d35c9d56 100644
--- a/crates/apub/src/activities/community/mod.rs
+++ b/crates/apub/src/activities/community/mod.rs
@@ -6,7 +6,7 @@ use crate::{
   protocol::activities::community::announce::AnnounceActivity,
 };
 use activitypub_federation::{core::object_id::ObjectId, traits::Actor};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::source::person::PersonFollower;
 use lemmy_utils::error::LemmyError;
 use url::Url;
diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs
index 855fe7b4e..9bd8c4ec2 100644
--- a/crates/apub/src/activities/community/remove_mod.rs
+++ b/crates/apub/src/activities/community/remove_mod.rs
@@ -19,7 +19,7 @@ use activitypub_federation::{
   traits::{ActivityHandler, Actor},
 };
 use activitystreams_kinds::{activity::RemoveType, public};
-use lemmy_api_common::{generate_moderators_url, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url};
 use lemmy_db_schema::{
   source::{
     community::{CommunityModerator, CommunityModeratorForm},
diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs
index 2741af7e8..57b1244d1 100644
--- a/crates/apub/src/activities/community/report.rs
+++ b/crates/apub/src/activities/community/report.rs
@@ -5,6 +5,7 @@ use crate::{
   protocol::{activities::community::report::Report, InCommunity},
   ActorType,
   PostOrComment,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -13,10 +14,11 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::activity::FlagType;
 use lemmy_api_common::{
-  comment::CommentReportResponse,
-  post::PostReportResponse,
+  comment::{CommentReportResponse, CreateCommentReport},
+  context::LemmyContext,
+  post::{CreatePostReport, PostReportResponse},
+  utils::get_local_user_view_from_jwt,
   websocket::{messages::SendModRoomMessage, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
@@ -29,9 +31,53 @@ use lemmy_db_views::structs::{CommentReportView, PostReportView};
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for CreatePostReport {
+  type Response = PostReportResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    Report::send(
+      ObjectId::new(response.post_report_view.post.ap_id.clone()),
+      &local_user_view.person.into(),
+      ObjectId::new(response.post_report_view.community.actor_id.clone()),
+      request.reason.to_string(),
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for CreateCommentReport {
+  type Response = CommentReportResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    Report::send(
+      ObjectId::new(response.comment_report_view.comment.ap_id.clone()),
+      &local_user_view.person.into(),
+      ObjectId::new(response.comment_report_view.community.actor_id.clone()),
+      request.reason.to_string(),
+      context,
+    )
+    .await
+  }
+}
+
 impl Report {
   #[tracing::instrument(skip_all)]
-  pub async fn send(
+  async fn send(
     object_id: ObjectId<PostOrComment>,
     actor: &ApubPerson,
     community_id: ObjectId<ApubCommunity>,
diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs
index c0d1a2799..969741ef6 100644
--- a/crates/apub/src/activities/community/update.rs
+++ b/crates/apub/src/activities/community/update.rs
@@ -10,6 +10,7 @@ use crate::{
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::{activities::community::update::UpdateCommunity, InCommunity},
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -18,13 +19,31 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::{activity::UpdateType, public};
 use lemmy_api_common::{
+  community::{CommunityResponse, EditCommunity, HideCommunity},
+  context::LemmyContext,
+  utils::get_local_user_view_from_jwt,
   websocket::{send::send_community_ws_message, UserOperationCrud},
-  LemmyContext,
 };
 use lemmy_db_schema::{source::community::Community, traits::Crud};
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for EditCommunity {
+  type Response = CommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community = Community::read(context.pool(), request.community_id).await?;
+    UpdateCommunity::send(community.into(), &local_user_view.person.into(), context).await
+  }
+}
+
 impl UpdateCommunity {
   #[tracing::instrument(skip_all)]
   pub async fn send(
@@ -115,3 +134,19 @@ impl ActivityHandler for UpdateCommunity {
     Ok(())
   }
 }
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for HideCommunity {
+  type Response = CommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community = Community::read(context.pool(), request.community_id).await?;
+    UpdateCommunity::send(community.into(), &local_user_view.person.into(), context).await
+  }
+}
diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs
index 5def04604..54df09ce6 100644
--- a/crates/apub/src/activities/create_or_update/comment.rs
+++ b/crates/apub/src/activities/create_or_update/comment.rs
@@ -16,6 +16,7 @@ use crate::{
     InCommunity,
   },
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -25,14 +26,17 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::public;
 use lemmy_api_common::{
+  comment::{CommentResponse, CreateComment, EditComment},
+  context::LemmyContext,
   utils::check_post_deleted_or_removed,
   websocket::{send::send_comment_ws_message, UserOperationCrud},
-  LemmyContext,
 };
 use lemmy_db_schema::{
+  newtypes::PersonId,
   source::{
-    comment::{CommentLike, CommentLikeForm},
+    comment::{Comment, CommentLike, CommentLikeForm},
     community::Community,
+    person::Person,
     post::Post,
   },
   traits::{Crud, Likeable},
@@ -40,29 +44,67 @@ use lemmy_db_schema::{
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for CreateComment {
+  type Response = CommentResponse;
+
+  async fn send_activity(
+    _request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    CreateOrUpdateNote::send(
+      &response.comment_view.comment,
+      response.comment_view.creator.id,
+      CreateOrUpdateType::Create,
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for EditComment {
+  type Response = CommentResponse;
+
+  async fn send_activity(
+    _request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    CreateOrUpdateNote::send(
+      &response.comment_view.comment,
+      response.comment_view.creator.id,
+      CreateOrUpdateType::Update,
+      context,
+    )
+    .await
+  }
+}
+
 impl CreateOrUpdateNote {
-  #[tracing::instrument(skip(comment, actor, kind, context))]
-  pub async fn send(
-    comment: ApubComment,
-    actor: &ApubPerson,
+  #[tracing::instrument(skip(comment, person_id, kind, context))]
+  async fn send(
+    comment: &Comment,
+    person_id: PersonId,
     kind: CreateOrUpdateType,
     context: &LemmyContext,
-    request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
     // TODO: might be helpful to add a comment method to retrieve community directly
     let post_id = comment.post_id;
     let post = Post::read(context.pool(), post_id).await?;
     let community_id = post.community_id;
+    let person: ApubPerson = Person::read(context.pool(), person_id).await?.into();
     let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();
 
     let id = generate_activity_id(
       kind.clone(),
       &context.settings().get_protocol_and_hostname(),
     )?;
-    let note = comment.into_apub(context).await?;
+    let note = ApubComment(comment.clone()).into_apub(context).await?;
 
     let create_or_update = CreateOrUpdateNote {
-      actor: ObjectId::new(actor.actor_id()),
+      actor: ObjectId::new(person.actor_id()),
       to: vec![public()],
       cc: note.cc.clone(),
       tag: note.tag.clone(),
@@ -88,13 +130,13 @@ impl CreateOrUpdateNote {
     let mut inboxes = vec![];
     for t in tagged_users {
       let person = t
-        .dereference(context, local_instance(context).await, request_counter)
+        .dereference(context, local_instance(context).await, &mut 0)
         .await?;
       inboxes.push(person.shared_inbox_or_inbox());
     }
 
     let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
-    send_activity_in_community(activity, actor, &community, inboxes, false, context).await
+    send_activity_in_community(activity, &person, &community, inboxes, false, context).await
   }
 }
 
diff --git a/crates/apub/src/activities/create_or_update/mod.rs b/crates/apub/src/activities/create_or_update/mod.rs
index b53dd2eb8..60ce6ea34 100644
--- a/crates/apub/src/activities/create_or_update/mod.rs
+++ b/crates/apub/src/activities/create_or_update/mod.rs
@@ -1,6 +1,6 @@
 use crate::{local_instance, objects::person::ApubPerson};
 use activitypub_federation::core::object_id::ObjectId;
-use lemmy_api_common::{websocket::send::send_local_notifs, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, websocket::send::send_local_notifs};
 use lemmy_db_schema::{
   newtypes::LocalUserId,
   source::{comment::Comment, post::Post},
diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs
index 161b26b9d..92cc70877 100644
--- a/crates/apub/src/activities/create_or_update/post.rs
+++ b/crates/apub/src/activities/create_or_update/post.rs
@@ -14,6 +14,7 @@ use crate::{
     InCommunity,
   },
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -23,19 +24,103 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::public;
 use lemmy_api_common::{
+  context::LemmyContext,
+  post::{CreatePost, EditPost, LockPost, PostResponse, StickyPost},
+  utils::get_local_user_view_from_jwt,
   websocket::{send::send_post_ws_message, UserOperationCrud},
-  LemmyContext,
 };
 use lemmy_db_schema::{
+  newtypes::PersonId,
   source::{
     community::Community,
-    post::{PostLike, PostLikeForm},
+    person::Person,
+    post::{Post, PostLike, PostLikeForm},
   },
   traits::{Crud, Likeable},
 };
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for CreatePost {
+  type Response = PostResponse;
+
+  async fn send_activity(
+    _request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    CreateOrUpdatePage::send(
+      &response.post_view.post,
+      response.post_view.creator.id,
+      CreateOrUpdateType::Create,
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for EditPost {
+  type Response = PostResponse;
+
+  async fn send_activity(
+    _request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    CreateOrUpdatePage::send(
+      &response.post_view.post,
+      response.post_view.creator.id,
+      CreateOrUpdateType::Update,
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for LockPost {
+  type Response = PostResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    CreateOrUpdatePage::send(
+      &response.post_view.post,
+      local_user_view.person.id,
+      CreateOrUpdateType::Update,
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for StickyPost {
+  type Response = PostResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    CreateOrUpdatePage::send(
+      &response.post_view.post,
+      local_user_view.person.id,
+      CreateOrUpdateType::Update,
+      context,
+    )
+    .await
+  }
+}
+
 impl CreateOrUpdatePage {
   pub(crate) async fn new(
     post: ApubPost,
@@ -60,19 +145,30 @@ impl CreateOrUpdatePage {
   }
 
   #[tracing::instrument(skip_all)]
-  pub async fn send(
-    post: ApubPost,
-    actor: &ApubPerson,
+  async fn send(
+    post: &Post,
+    person_id: PersonId,
     kind: CreateOrUpdateType,
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
+    let post = ApubPost(post.clone());
     let community_id = post.community_id;
+    let person: ApubPerson = Person::read(context.pool(), person_id).await?.into();
     let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();
 
-    let create_or_update = CreateOrUpdatePage::new(post, actor, &community, kind, context).await?;
+    let create_or_update =
+      CreateOrUpdatePage::new(post, &person, &community, kind, context).await?;
     let is_mod_action = create_or_update.object.is_mod_action(context).await?;
     let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
-    send_activity_in_community(activity, actor, &community, vec![], is_mod_action, context).await?;
+    send_activity_in_community(
+      activity,
+      &person,
+      &community,
+      vec![],
+      is_mod_action,
+      context,
+    )
+    .await?;
     Ok(())
   }
 }
diff --git a/crates/apub/src/activities/create_or_update/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs
index 431e699ce..071209c34 100644
--- a/crates/apub/src/activities/create_or_update/private_message.rs
+++ b/crates/apub/src/activities/create_or_update/private_message.rs
@@ -6,6 +6,7 @@ use crate::{
     CreateOrUpdateType,
   },
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -14,22 +15,65 @@ use activitypub_federation::{
   utils::verify_domains_match,
 };
 use lemmy_api_common::{
+  context::LemmyContext,
+  private_message::{CreatePrivateMessage, EditPrivateMessage, PrivateMessageResponse},
   websocket::{send::send_pm_ws_message, UserOperationCrud},
-  LemmyContext,
 };
-use lemmy_db_schema::{source::person::Person, traits::Crud};
+use lemmy_db_schema::{
+  newtypes::PersonId,
+  source::{person::Person, private_message::PrivateMessage},
+  traits::Crud,
+};
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for CreatePrivateMessage {
+  type Response = PrivateMessageResponse;
+
+  async fn send_activity(
+    _request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    CreateOrUpdateChatMessage::send(
+      &response.private_message_view.private_message,
+      response.private_message_view.creator.id,
+      CreateOrUpdateType::Create,
+      context,
+    )
+    .await
+  }
+}
+#[async_trait::async_trait(?Send)]
+impl SendActivity for EditPrivateMessage {
+  type Response = PrivateMessageResponse;
+
+  async fn send_activity(
+    _request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    CreateOrUpdateChatMessage::send(
+      &response.private_message_view.private_message,
+      response.private_message_view.creator.id,
+      CreateOrUpdateType::Update,
+      context,
+    )
+    .await
+  }
+}
+
 impl CreateOrUpdateChatMessage {
   #[tracing::instrument(skip_all)]
-  pub async fn send(
-    private_message: ApubPrivateMessage,
-    actor: &ApubPerson,
+  async fn send(
+    private_message: &PrivateMessage,
+    sender_id: PersonId,
     kind: CreateOrUpdateType,
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
     let recipient_id = private_message.recipient_id;
+    let sender: ApubPerson = Person::read(context.pool(), sender_id).await?.into();
     let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
 
     let id = generate_activity_id(
@@ -38,13 +82,15 @@ impl CreateOrUpdateChatMessage {
     )?;
     let create_or_update = CreateOrUpdateChatMessage {
       id: id.clone(),
-      actor: ObjectId::new(actor.actor_id()),
+      actor: ObjectId::new(sender.actor_id()),
       to: [ObjectId::new(recipient.actor_id())],
-      object: private_message.into_apub(context).await?,
+      object: ApubPrivateMessage(private_message.clone())
+        .into_apub(context)
+        .await?,
       kind,
     };
     let inbox = vec![recipient.shared_inbox_or_inbox()];
-    send_lemmy_activity(context, create_or_update, actor, inbox, true).await
+    send_lemmy_activity(context, create_or_update, &sender, inbox, true).await
   }
 }
 
diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs
index ae93d6e09..138a1fae1 100644
--- a/crates/apub/src/activities/deletion/delete.rs
+++ b/crates/apub/src/activities/deletion/delete.rs
@@ -10,11 +10,11 @@ use crate::{
 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
 use activitystreams_kinds::activity::DeleteType;
 use lemmy_api_common::{
+  context::LemmyContext,
   websocket::{
     send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
     UserOperationCrud,
   },
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/apub/src/activities/deletion/delete_user.rs b/crates/apub/src/activities/deletion/delete_user.rs
index ffe664d59..4ace431e6 100644
--- a/crates/apub/src/activities/deletion/delete_user.rs
+++ b/crates/apub/src/activities/deletion/delete_user.rs
@@ -3,6 +3,7 @@ use crate::{
   local_instance,
   objects::{instance::remote_instance_inboxes, person::ApubPerson},
   protocol::activities::deletion::delete_user::DeleteUser,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -11,10 +12,54 @@ use activitypub_federation::{
   utils::verify_urls_match,
 };
 use activitystreams_kinds::{activity::DeleteType, public};
-use lemmy_api_common::{utils::delete_user_account, LemmyContext};
+use lemmy_api_common::{
+  context::LemmyContext,
+  person::{DeleteAccount, DeleteAccountResponse},
+  utils::{delete_user_account, get_local_user_view_from_jwt},
+};
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for DeleteAccount {
+  type Response = DeleteAccountResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let actor: ApubPerson = local_user_view.person.into();
+    delete_user_account(
+      actor.id,
+      context.pool(),
+      context.settings(),
+      context.client(),
+    )
+    .await?;
+
+    let actor_id = ObjectId::new(actor.actor_id.clone());
+    let id = generate_activity_id(
+      DeleteType::Delete,
+      &context.settings().get_protocol_and_hostname(),
+    )?;
+    let delete = DeleteUser {
+      actor: actor_id.clone(),
+      to: vec![public()],
+      object: actor_id,
+      kind: DeleteType::Delete,
+      id: id.clone(),
+      cc: vec![],
+    };
+
+    let inboxes = remote_instance_inboxes(context.pool()).await?;
+    send_lemmy_activity(context, delete, &actor, inboxes, true).await?;
+    Ok(())
+  }
+}
+
 /// This can be separate from Delete activity because it doesn't need to be handled in shared inbox
 /// (cause instance actor doesn't have shared inbox).
 #[async_trait::async_trait(?Send)]
@@ -60,26 +105,3 @@ impl ActivityHandler for DeleteUser {
     Ok(())
   }
 }
-
-impl DeleteUser {
-  #[tracing::instrument(skip_all)]
-  pub async fn send(actor: &ApubPerson, context: &LemmyContext) -> Result<(), LemmyError> {
-    let actor_id = ObjectId::new(actor.actor_id.clone());
-    let id = generate_activity_id(
-      DeleteType::Delete,
-      &context.settings().get_protocol_and_hostname(),
-    )?;
-    let delete = DeleteUser {
-      actor: actor_id.clone(),
-      to: vec![public()],
-      object: actor_id,
-      kind: DeleteType::Delete,
-      id: id.clone(),
-      cc: vec![],
-    };
-
-    let inboxes = remote_instance_inboxes(context.pool()).await?;
-    send_lemmy_activity(context, delete, actor, inboxes, true).await?;
-    Ok(())
-  }
-}
diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs
index d05e45021..dae70dc3d 100644
--- a/crates/apub/src/activities/deletion/mod.rs
+++ b/crates/apub/src/activities/deletion/mod.rs
@@ -21,6 +21,7 @@ use crate::{
     InCommunity,
   },
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -29,6 +30,12 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::public;
 use lemmy_api_common::{
+  comment::{CommentResponse, DeleteComment, RemoveComment},
+  community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
+  context::LemmyContext,
+  post::{DeletePost, PostResponse, RemovePost},
+  private_message::{DeletePrivateMessage, PrivateMessageResponse},
+  utils::get_local_user_view_from_jwt,
   websocket::{
     send::{
       send_comment_ws_message_simple,
@@ -38,7 +45,6 @@ use lemmy_api_common::{
     },
     UserOperationCrud,
   },
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
@@ -58,10 +64,176 @@ pub mod delete;
 pub mod delete_user;
 pub mod undo_delete;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for DeletePost {
+  type Response = PostResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community = Community::read(context.pool(), response.post_view.community.id).await?;
+    let deletable = DeletableObjects::Post(Box::new(response.post_view.post.clone().into()));
+    send_apub_delete_in_community(
+      local_user_view.person,
+      community,
+      deletable,
+      None,
+      request.deleted,
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for RemovePost {
+  type Response = PostResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community = Community::read(context.pool(), response.post_view.community.id).await?;
+    let deletable = DeletableObjects::Post(Box::new(response.post_view.post.clone().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(?Send)]
+impl SendActivity for DeleteComment {
+  type Response = CommentResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let community_id = response.comment_view.community.id;
+    let community = Community::read(context.pool(), community_id).await?;
+    let person = Person::read(context.pool(), response.comment_view.creator.id).await?;
+    let deletable =
+      DeletableObjects::Comment(Box::new(response.comment_view.comment.clone().into()));
+    send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
+      .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for RemoveComment {
+  type Response = CommentResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let comment = Comment::read(context.pool(), request.comment_id).await?;
+    let community = Community::read(context.pool(), response.comment_view.community.id).await?;
+    let deletable = DeletableObjects::Comment(Box::new(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(?Send)]
+impl SendActivity for DeletePrivateMessage {
+  type Response = PrivateMessageResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    send_apub_delete_private_message(
+      &local_user_view.person.into(),
+      response.private_message_view.private_message.clone(),
+      request.deleted,
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for DeleteCommunity {
+  type Response = CommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community = Community::read(context.pool(), request.community_id).await?;
+    let deletable = DeletableObjects::Community(Box::new(community.clone().into()));
+    send_apub_delete_in_community(
+      local_user_view.person,
+      community,
+      deletable,
+      None,
+      request.deleted,
+      context,
+    )
+    .await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for RemoveCommunity {
+  type Response = CommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community = Community::read(context.pool(), request.community_id).await?;
+    let deletable = DeletableObjects::Community(Box::new(community.clone().into()));
+    send_apub_delete_in_community(
+      local_user_view.person,
+      community,
+      deletable,
+      request.reason.clone().or_else(|| Some(String::new())),
+      request.removed,
+      context,
+    )
+    .await
+  }
+}
+
 /// 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)]
-pub async fn send_apub_delete_in_community(
+async fn send_apub_delete_in_community(
   actor: Person,
   community: Community,
   object: DeletableObjects,
@@ -90,7 +262,7 @@ pub async fn send_apub_delete_in_community(
 }
 
 #[tracing::instrument(skip_all)]
-pub async fn send_apub_delete_private_message(
+async fn send_apub_delete_private_message(
   actor: &ApubPerson,
   pm: PrivateMessage,
   deleted: bool,
diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs
index bc6f9fd14..b3cef3858 100644
--- a/crates/apub/src/activities/deletion/undo_delete.rs
+++ b/crates/apub/src/activities/deletion/undo_delete.rs
@@ -10,11 +10,11 @@ use crate::{
 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
 use activitystreams_kinds::activity::UndoType;
 use lemmy_api_common::{
+  context::LemmyContext,
   websocket::{
     send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
     UserOperationCrud,
   },
-  LemmyContext,
 };
 use lemmy_db_schema::{
   source::{
diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs
index 880db0d99..6702c8c95 100644
--- a/crates/apub/src/activities/following/accept.rs
+++ b/crates/apub/src/activities/following/accept.rs
@@ -13,8 +13,8 @@ use activitypub_federation::{
 use activitystreams_kinds::activity::AcceptType;
 use lemmy_api_common::{
   community::CommunityResponse,
+  context::LemmyContext,
   websocket::{messages::SendUserRoomMessage, UserOperation},
-  LemmyContext,
 };
 use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
 use lemmy_db_views::structs::LocalUserView;
diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs
index 82c0c37a5..62bd24157 100644
--- a/crates/apub/src/activities/following/follow.rs
+++ b/crates/apub/src/activities/following/follow.rs
@@ -8,8 +8,13 @@ use crate::{
   fetcher::user_or_community::UserOrCommunity,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
-  protocol::activities::following::{accept::AcceptFollow, follow::Follow},
+  protocol::activities::following::{
+    accept::AcceptFollow,
+    follow::Follow,
+    undo_follow::UndoFollow,
+  },
   ActorType,
+  SendActivity,
 };
 use activitypub_federation::{
   core::object_id::ObjectId,
@@ -17,13 +22,17 @@ use activitypub_federation::{
   traits::{ActivityHandler, Actor},
 };
 use activitystreams_kinds::activity::FollowType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::{
+  community::{BlockCommunity, BlockCommunityResponse},
+  context::LemmyContext,
+  utils::get_local_user_view_from_jwt,
+};
 use lemmy_db_schema::{
   source::{
-    community::{CommunityFollower, CommunityFollowerForm},
+    community::{Community, CommunityFollower, CommunityFollowerForm},
     person::{PersonFollower, PersonFollowerForm},
   },
-  traits::Followable,
+  traits::{Crud, Followable},
 };
 use lemmy_utils::error::LemmyError;
 use url::Url;
@@ -132,3 +141,19 @@ impl ActivityHandler for Follow {
     AcceptFollow::send(self, context, request_counter).await
   }
 }
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for BlockCommunity {
+  type Response = BlockCommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let community = Community::read(context.pool(), request.community_id).await?;
+    UndoFollow::send(&local_user_view.person.into(), &community.into(), context).await
+  }
+}
diff --git a/crates/apub/src/activities/following/mod.rs b/crates/apub/src/activities/following/mod.rs
index 60bdd5f78..e472dc73a 100644
--- a/crates/apub/src/activities/following/mod.rs
+++ b/crates/apub/src/activities/following/mod.rs
@@ -1,3 +1,41 @@
+use crate::{
+  objects::community::ApubCommunity,
+  protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
+  SendActivity,
+};
+use lemmy_api_common::{
+  community::{CommunityResponse, FollowCommunity},
+  context::LemmyContext,
+  utils::get_local_user_view_from_jwt,
+};
+use lemmy_db_schema::{source::community::Community, traits::Crud};
+use lemmy_utils::error::LemmyError;
+
 pub mod accept;
 pub mod follow;
 pub mod undo_follow;
+
+#[async_trait::async_trait(?Send)]
+impl SendActivity for FollowCommunity {
+  type Response = CommunityResponse;
+
+  async fn send_activity(
+    request: &Self,
+    _response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let local_user_view =
+      get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
+    let person = local_user_view.person.clone().into();
+    let community: ApubCommunity = Community::read(context.pool(), request.community_id)
+      .await?
+      .into();
+    if community.local {
+      Ok(())
+    } else if request.follow {
+      Follow::send(&person, &community, context).await
+    } else {
+      UndoFollow::send(&person, &community, context).await
+    }
+  }
+}
diff --git a/crates/apub/src/activities/following/undo_follow.rs b/crates/apub/src/activities/following/undo_follow.rs
index 7d48f9f3c..436d8a02e 100644
--- a/crates/apub/src/activities/following/undo_follow.rs
+++ b/crates/apub/src/activities/following/undo_follow.rs
@@ -13,7 +13,7 @@ use activitypub_federation::{
   utils::verify_urls_match,
 };
 use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{
     community::{CommunityFollower, CommunityFollowerForm},
diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs
index 68a3b535c..2fb5808b7 100644
--- a/crates/apub/src/activities/mod.rs
+++ b/crates/apub/src/activities/mod.rs
@@ -12,7 +12,7 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::public;
 use anyhow::anyhow;
-use lemmy_api_common::{generate_moderators_url, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url};
 use lemmy_db_schema::{
   newtypes::CommunityId,
   source::{community::Community, local_site::LocalSite},
@@ -30,6 +30,7 @@ pub mod community;
 pub mod create_or_update;
 pub mod deletion;
 pub mod following;
+pub mod unfederated;
 pub mod voting;
 
 /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
diff --git a/crates/apub/src/activities/unfederated.rs b/crates/apub/src/activities/unfederated.rs
new file mode 100644
index 000000000..cca3340ed
--- /dev/null
+++ b/crates/apub/src/activities/unfederated.rs
@@ -0,0 +1,351 @@
+use crate::SendActivity;
+use lemmy_api_common::{
+  comment::{
+    CommentReportResponse,
+    CommentResponse,
+    GetComment,
+    GetComments,
+    GetCommentsResponse,
+    ListCommentReports,
+    ListCommentReportsResponse,
+    ResolveCommentReport,
+    SaveComment,
+  },
+  community::{
+    CommunityResponse,
+    CreateCommunity,
+    GetCommunity,
+    GetCommunityResponse,
+    ListCommunities,
+    ListCommunitiesResponse,
+    TransferCommunity,
+  },
+  person::{
+    AddAdmin,
+    AddAdminResponse,
+    BannedPersonsResponse,
+    BlockPerson,
+    BlockPersonResponse,
+    ChangePassword,
+    CommentReplyResponse,
+    GetBannedPersons,
+    GetCaptcha,
+    GetCaptchaResponse,
+    GetPersonDetails,
+    GetPersonDetailsResponse,
+    GetPersonMentions,
+    GetPersonMentionsResponse,
+    GetReplies,
+    GetRepliesResponse,
+    GetReportCount,
+    GetReportCountResponse,
+    GetUnreadCount,
+    GetUnreadCountResponse,
+    Login,
+    LoginResponse,
+    MarkAllAsRead,
+    MarkCommentReplyAsRead,
+    MarkPersonMentionAsRead,
+    PasswordChangeAfterReset,
+    PasswordReset,
+    PasswordResetResponse,
+    PersonMentionResponse,
+    Register,
+    SaveUserSettings,
+    VerifyEmail,
+    VerifyEmailResponse,
+  },
+  post::{
+    GetPost,
+    GetPostResponse,
+    GetPosts,
+    GetPostsResponse,
+    GetSiteMetadata,
+    GetSiteMetadataResponse,
+    ListPostReports,
+    ListPostReportsResponse,
+    MarkPostAsRead,
+    PostReportResponse,
+    PostResponse,
+    ResolvePostReport,
+    SavePost,
+  },
+  private_message::{
+    CreatePrivateMessageReport,
+    GetPrivateMessages,
+    ListPrivateMessageReports,
+    ListPrivateMessageReportsResponse,
+    MarkPrivateMessageAsRead,
+    PrivateMessageReportResponse,
+    PrivateMessageResponse,
+    PrivateMessagesResponse,
+    ResolvePrivateMessageReport,
+  },
+  site::{
+    ApproveRegistrationApplication,
+    CreateSite,
+    EditSite,
+    GetModlog,
+    GetModlogResponse,
+    GetSite,
+    GetSiteResponse,
+    GetUnreadRegistrationApplicationCount,
+    GetUnreadRegistrationApplicationCountResponse,
+    LeaveAdmin,
+    ListRegistrationApplications,
+    ListRegistrationApplicationsResponse,
+    PurgeComment,
+    PurgeCommunity,
+    PurgeItemResponse,
+    PurgePerson,
+    PurgePost,
+    RegistrationApplicationResponse,
+    ResolveObject,
+    ResolveObjectResponse,
+    Search,
+    SearchResponse,
+    SiteResponse,
+  },
+  websocket::structs::{
+    CommunityJoin,
+    CommunityJoinResponse,
+    ModJoin,
+    ModJoinResponse,
+    PostJoin,
+    PostJoinResponse,
+    UserJoin,
+    UserJoinResponse,
+  },
+};
+
+impl SendActivity for Register {
+  type Response = LoginResponse;
+}
+
+impl SendActivity for GetPersonDetails {
+  type Response = GetPersonDetailsResponse;
+}
+
+impl SendActivity for GetPrivateMessages {
+  type Response = PrivateMessagesResponse;
+}
+
+impl SendActivity for CreateSite {
+  type Response = SiteResponse;
+}
+
+impl SendActivity for EditSite {
+  type Response = SiteResponse;
+}
+
+impl SendActivity for GetSite {
+  type Response = GetSiteResponse;
+}
+
+impl SendActivity for GetCommunity {
+  type Response = GetCommunityResponse;
+}
+
+impl SendActivity for ListCommunities {
+  type Response = ListCommunitiesResponse;
+}
+
+impl SendActivity for CreateCommunity {
+  type Response = CommunityResponse;
+}
+
+impl SendActivity for GetPost {
+  type Response = GetPostResponse;
+}
+
+impl SendActivity for GetPosts {
+  type Response = GetPostsResponse;
+}
+
+impl SendActivity for GetComment {
+  type Response = CommentResponse;
+}
+
+impl SendActivity for GetComments {
+  type Response = GetCommentsResponse;
+}
+
+impl SendActivity for Login {
+  type Response = LoginResponse;
+}
+
+impl SendActivity for GetCaptcha {
+  type Response = GetCaptchaResponse;
+}
+
+impl SendActivity for GetReplies {
+  type Response = GetRepliesResponse;
+}
+
+impl SendActivity for AddAdmin {
+  type Response = AddAdminResponse;
+}
+
+impl SendActivity for GetUnreadRegistrationApplicationCount {
+  type Response = GetUnreadRegistrationApplicationCountResponse;
+}
+
+impl SendActivity for ListRegistrationApplications {
+  type Response = ListRegistrationApplicationsResponse;
+}
+
+impl SendActivity for ApproveRegistrationApplication {
+  type Response = RegistrationApplicationResponse;
+}
+
+impl SendActivity for GetBannedPersons {
+  type Response = BannedPersonsResponse;
+}
+
+impl SendActivity for BlockPerson {
+  type Response = BlockPersonResponse;
+}
+
+impl SendActivity for GetPersonMentions {
+  type Response = GetPersonMentionsResponse;
+}
+
+impl SendActivity for MarkPersonMentionAsRead {
+  type Response = PersonMentionResponse;
+}
+
+impl SendActivity for MarkCommentReplyAsRead {
+  type Response = CommentReplyResponse;
+}
+
+impl SendActivity for MarkAllAsRead {
+  type Response = GetRepliesResponse;
+}
+
+impl SendActivity for PasswordReset {
+  type Response = PasswordResetResponse;
+}
+
+impl SendActivity for PasswordChangeAfterReset {
+  type Response = LoginResponse;
+}
+
+impl SendActivity for UserJoin {
+  type Response = UserJoinResponse;
+}
+
+impl SendActivity for PostJoin {
+  type Response = PostJoinResponse;
+}
+
+impl SendActivity for CommunityJoin {
+  type Response = CommunityJoinResponse;
+}
+
+impl SendActivity for ModJoin {
+  type Response = ModJoinResponse;
+}
+
+impl SendActivity for SaveUserSettings {
+  type Response = LoginResponse;
+}
+
+impl SendActivity for ChangePassword {
+  type Response = LoginResponse;
+}
+
+impl SendActivity for GetReportCount {
+  type Response = GetReportCountResponse;
+}
+
+impl SendActivity for GetUnreadCount {
+  type Response = GetUnreadCountResponse;
+}
+
+impl SendActivity for VerifyEmail {
+  type Response = VerifyEmailResponse;
+}
+
+impl SendActivity for MarkPrivateMessageAsRead {
+  type Response = PrivateMessageResponse;
+}
+
+impl SendActivity for CreatePrivateMessageReport {
+  type Response = PrivateMessageReportResponse;
+}
+
+impl SendActivity for ResolvePrivateMessageReport {
+  type Response = PrivateMessageReportResponse;
+}
+
+impl SendActivity for ListPrivateMessageReports {
+  type Response = ListPrivateMessageReportsResponse;
+}
+
+impl SendActivity for GetModlog {
+  type Response = GetModlogResponse;
+}
+
+impl SendActivity for PurgePerson {
+  type Response = PurgeItemResponse;
+}
+
+impl SendActivity for PurgeCommunity {
+  type Response = PurgeItemResponse;
+}
+
+impl SendActivity for PurgePost {
+  type Response = PurgeItemResponse;
+}
+
+impl SendActivity for PurgeComment {
+  type Response = PurgeItemResponse;
+}
+
+impl SendActivity for Search {
+  type Response = SearchResponse;
+}
+
+impl SendActivity for ResolveObject {
+  type Response = ResolveObjectResponse;
+}
+
+impl SendActivity for TransferCommunity {
+  type Response = GetCommunityResponse;
+}
+
+impl SendActivity for LeaveAdmin {
+  type Response = GetSiteResponse;
+}
+
+impl SendActivity for MarkPostAsRead {
+  type Response = PostResponse;
+}
+
+impl SendActivity for SavePost {
+  type Response = PostResponse;
+}
+
+impl SendActivity for ListPostReports {
+  type Response = ListPostReportsResponse;
+}
+
+impl SendActivity for ResolvePostReport {
+  type Response = PostReportResponse;
+}
+
+impl SendActivity for GetSiteMetadata {
+  type Response = GetSiteMetadataResponse;
+}
+
+impl SendActivity for SaveComment {
+  type Response = CommentResponse;
+}
+
+impl SendActivity for ListCommentReports {
+  type Response = ListCommentReportsResponse;
+}
+
+impl SendActivity for ResolveCommentReport {
+  type Response = CommentReportResponse;
+}
diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs
index a01dcb2ea..4471fbd92 100644
--- a/crates/apub/src/activities/voting/mod.rs
+++ b/crates/apub/src/activities/voting/mod.rs
@@ -1,26 +1,112 @@
 use crate::{
+  activities::community::send_activity_in_community,
+  activity_lists::AnnouncableActivities,
+  fetcher::post_or_comment::PostOrComment,
   objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
-  protocol::activities::voting::vote::VoteType,
+  protocol::activities::voting::{
+    undo_vote::UndoVote,
+    vote::{Vote, VoteType},
+  },
+  SendActivity,
 };
+use activitypub_federation::core::object_id::ObjectId;
 use lemmy_api_common::{
+  comment::{CommentResponse, CreateCommentLike},
+  context::LemmyContext,
+  post::{CreatePostLike, PostResponse},
+  sensitive::Sensitive,
+  utils::get_local_user_view_from_jwt,
   websocket::{
     send::{send_comment_ws_message_simple, send_post_ws_message},
     UserOperation,
   },
-  LemmyContext,
 };
 use lemmy_db_schema::{
+  newtypes::CommunityId,
   source::{
     comment::{CommentLike, CommentLikeForm},
+    community::Community,
+    person::Person,
     post::{PostLike, PostLikeForm},
   },
-  traits::Likeable,
+  traits::{Crud, Likeable},
 };
 use lemmy_utils::error::LemmyError;
 
 pub mod undo_vote;
 pub mod vote;
 
+#[async_trait::async_trait(?Send)]
+impl SendActivity for CreatePostLike {
+  type Response = PostResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let object_id = ObjectId::new(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(?Send)]
+impl SendActivity for CreateCommentLike {
+  type Response = CommentResponse;
+
+  async fn send_activity(
+    request: &Self,
+    response: &Self::Response,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let object_id = ObjectId::new(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,
+  score: i16,
+  jwt: &Sensitive<String>,
+  context: &LemmyContext,
+) -> Result<(), LemmyError> {
+  let community = Community::read(context.pool(), community_id).await?.into();
+  let local_user_view = get_local_user_view_from_jwt(jwt, context.pool(), context.secret()).await?;
+  let actor = Person::read(context.pool(), local_user_view.person.id)
+    .await?
+    .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 activity = AnnouncableActivities::Vote(vote);
+    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 activity = AnnouncableActivities::UndoVote(undo_vote);
+    send_activity_in_community(activity, &actor, &community, vec![], false, context).await
+  }
+}
+
 #[tracing::instrument(skip_all)]
 async fn vote_comment(
   vote_type: &VoteType,
diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs
index dca106ded..7a419e879 100644
--- a/crates/apub/src/activities/voting/undo_vote.rs
+++ b/crates/apub/src/activities/voting/undo_vote.rs
@@ -1,18 +1,13 @@
 use crate::{
   activities::{
-    community::send_activity_in_community,
     generate_activity_id,
     verify_person_in_community,
     voting::{undo_vote_comment, undo_vote_post},
   },
-  activity_lists::AnnouncableActivities,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::{
-    activities::voting::{
-      undo_vote::UndoVote,
-      vote::{Vote, VoteType},
-    },
+    activities::voting::{undo_vote::UndoVote, vote::Vote},
     InCommunity,
   },
   ActorType,
@@ -25,39 +20,27 @@ use activitypub_federation::{
   utils::verify_urls_match,
 };
 use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::LemmyContext;
-use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
 impl UndoVote {
-  /// UndoVote has as:Public value in cc field, unlike other activities. This indicates to other
-  /// software (like GNU social, or presumably Mastodon), that the like actor should not be
-  /// disclosed.
-  #[tracing::instrument(skip_all)]
-  pub async fn send(
-    object: &PostOrComment,
+  pub(in crate::activities::voting) fn new(
+    vote: Vote,
     actor: &ApubPerson,
-    community_id: CommunityId,
-    kind: VoteType,
+    community: &ApubCommunity,
     context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let community: ApubCommunity = Community::read(context.pool(), community_id).await?.into();
-
-    let object = Vote::new(object, actor, &community, kind.clone(), context)?;
-    let id = generate_activity_id(
-      UndoType::Undo,
-      &context.settings().get_protocol_and_hostname(),
-    )?;
-    let undo_vote = UndoVote {
+  ) -> Result<Self, LemmyError> {
+    Ok(UndoVote {
       actor: ObjectId::new(actor.actor_id()),
-      object,
+      object: vote,
       kind: UndoType::Undo,
-      id: id.clone(),
+      id: generate_activity_id(
+        UndoType::Undo,
+        &context.settings().get_protocol_and_hostname(),
+      )?,
       audience: Some(ObjectId::new(community.actor_id())),
-    };
-    let activity = AnnouncableActivities::UndoVote(undo_vote);
-    send_activity_in_community(activity, actor, &community, vec![], false, context).await
+    })
   }
 }
 
diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs
index bc702fd86..e435b6823 100644
--- a/crates/apub/src/activities/voting/vote.rs
+++ b/crates/apub/src/activities/voting/vote.rs
@@ -1,11 +1,9 @@
 use crate::{
   activities::{
-    community::send_activity_in_community,
     generate_activity_id,
     verify_person_in_community,
     voting::{vote_comment, vote_post},
   },
-  activity_lists::AnnouncableActivities,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::{
@@ -17,20 +15,14 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
 use anyhow::anyhow;
-use lemmy_api_common::LemmyContext;
-use lemmy_db_schema::{
-  newtypes::CommunityId,
-  source::{community::Community, local_site::LocalSite},
-  traits::Crud,
-};
+use lemmy_api_common::context::LemmyContext;
+use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_utils::error::LemmyError;
 use url::Url;
 
-/// Vote has as:Public value in cc field, unlike other activities. This indicates to other software
-/// (like GNU social, or presumably Mastodon), that the like actor should not be disclosed.
 impl Vote {
   pub(in crate::activities::voting) fn new(
-    object: &PostOrComment,
+    object_id: ObjectId<PostOrComment>,
     actor: &ApubPerson,
     community: &ApubCommunity,
     kind: VoteType,
@@ -38,27 +30,12 @@ impl Vote {
   ) -> Result<Vote, LemmyError> {
     Ok(Vote {
       actor: ObjectId::new(actor.actor_id()),
-      object: ObjectId::new(object.ap_id()),
+      object: object_id,
       kind: kind.clone(),
       id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
       audience: Some(ObjectId::new(community.actor_id())),
     })
   }
-
-  #[tracing::instrument(skip_all)]
-  pub async fn send(
-    object: &PostOrComment,
-    actor: &ApubPerson,
-    community_id: CommunityId,
-    kind: VoteType,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let community = Community::read(context.pool(), community_id).await?.into();
-    let vote = Vote::new(object, actor, &community, kind, context)?;
-
-    let activity = AnnouncableActivities::Vote(vote);
-    send_activity_in_community(activity, actor, &community, vec![], false, context).await
-  }
 }
 
 #[async_trait::async_trait(?Send)]
diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs
index fa343d8ae..6e1771127 100644
--- a/crates/apub/src/activity_lists.rs
+++ b/crates/apub/src/activity_lists.rs
@@ -24,7 +24,7 @@ use crate::{
   },
 };
 use activitypub_federation::{data::Data, deser::context::WithContext, traits::ActivityHandler};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/api_crud/src/comment/list.rs b/crates/apub/src/api/list_comments.rs
similarity index 94%
rename from crates/api_crud/src/comment/list.rs
rename to crates/apub/src/api/list_comments.rs
index 3e7444bf7..c4af6900a 100644
--- a/crates/api_crud/src/comment/list.rs
+++ b/crates/apub/src/api/list_comments.rs
@@ -1,15 +1,18 @@
-use crate::PerformCrud;
+use crate::{
+  api::PerformApub,
+  fetcher::resolve_actor_identifier,
+  objects::community::ApubCommunity,
+};
 use actix_web::web::Data;
 use lemmy_api_common::{
   comment::{GetComments, GetCommentsResponse},
+  context::LemmyContext,
   utils::{
     check_private_instance,
     get_local_user_view_from_jwt_opt,
     listing_type_with_site_default,
   },
-  LemmyContext,
 };
-use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
   source::{comment::Comment, community::Community, local_site::LocalSite},
   traits::{Crud, DeleteableOrRemoveable},
@@ -18,7 +21,7 @@ use lemmy_db_views::comment_view::CommentQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
-impl PerformCrud for GetComments {
+impl PerformApub for GetComments {
   type Response = GetCommentsResponse;
 
   #[tracing::instrument(skip(context, _websocket_id))]
diff --git a/crates/api_crud/src/post/list.rs b/crates/apub/src/api/list_posts.rs
similarity index 93%
rename from crates/api_crud/src/post/list.rs
rename to crates/apub/src/api/list_posts.rs
index 42944cada..7a1f815c7 100644
--- a/crates/api_crud/src/post/list.rs
+++ b/crates/apub/src/api/list_posts.rs
@@ -1,15 +1,18 @@
-use crate::PerformCrud;
+use crate::{
+  api::PerformApub,
+  fetcher::resolve_actor_identifier,
+  objects::community::ApubCommunity,
+};
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   post::{GetPosts, GetPostsResponse},
   utils::{
     check_private_instance,
     get_local_user_view_from_jwt_opt,
     listing_type_with_site_default,
   },
-  LemmyContext,
 };
-use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
   source::{community::Community, local_site::LocalSite},
   traits::DeleteableOrRemoveable,
@@ -18,7 +21,7 @@ use lemmy_db_views::post_view::PostQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
-impl PerformCrud for GetPosts {
+impl PerformApub for GetPosts {
   type Response = GetPostsResponse;
 
   #[tracing::instrument(skip(context, _websocket_id))]
diff --git a/crates/apub/src/api/mod.rs b/crates/apub/src/api/mod.rs
new file mode 100644
index 000000000..233f72f02
--- /dev/null
+++ b/crates/apub/src/api/mod.rs
@@ -0,0 +1,21 @@
+use actix_web::web::Data;
+use lemmy_api_common::context::LemmyContext;
+use lemmy_utils::{error::LemmyError, ConnectionId};
+
+mod list_comments;
+mod list_posts;
+mod read_community;
+mod read_person;
+mod resolve_object;
+mod search;
+
+#[async_trait::async_trait(?Send)]
+pub trait PerformApub {
+  type Response: serde::ser::Serialize + Send;
+
+  async fn perform(
+    &self,
+    context: &Data<LemmyContext>,
+    websocket_id: Option<ConnectionId>,
+  ) -> Result<Self::Response, LemmyError>;
+}
diff --git a/crates/api_crud/src/community/read.rs b/crates/apub/src/api/read_community.rs
similarity index 92%
rename from crates/api_crud/src/community/read.rs
rename to crates/apub/src/api/read_community.rs
index a008f7865..b1615d7d7 100644
--- a/crates/api_crud/src/community/read.rs
+++ b/crates/apub/src/api/read_community.rs
@@ -1,14 +1,14 @@
-use crate::PerformCrud;
+use crate::{
+  api::PerformApub,
+  fetcher::resolve_actor_identifier,
+  objects::community::ApubCommunity,
+};
 use actix_web::web::Data;
 use lemmy_api_common::{
   community::{GetCommunity, GetCommunityResponse},
+  context::LemmyContext,
   utils::{check_private_instance, get_local_user_view_from_jwt_opt},
   websocket::messages::GetCommunityUsersOnline,
-  LemmyContext,
-};
-use lemmy_apub::{
-  fetcher::resolve_actor_identifier,
-  objects::{community::ApubCommunity, instance::instance_actor_id_from_url},
 };
 use lemmy_db_schema::{
   impls::actor_language::default_post_language,
@@ -24,7 +24,7 @@ use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
-impl PerformCrud for GetCommunity {
+impl PerformApub for GetCommunity {
   type Response = GetCommunityResponse;
 
   #[tracing::instrument(skip(context, _websocket_id))]
@@ -78,7 +78,8 @@ impl PerformCrud for GetCommunity {
       .await
       .unwrap_or(1);
 
-    let site_id = instance_actor_id_from_url(community_view.community.actor_id.clone().into());
+    let site_id =
+      Site::instance_actor_id_from_url(community_view.community.actor_id.clone().into());
     let mut site = Site::read_from_apub_id(context.pool(), site_id).await?;
     // no need to include metadata for local site (its already available through other endpoints).
     // this also prevents us from leaking the federation private key.
diff --git a/crates/api_crud/src/user/read.rs b/crates/apub/src/api/read_person.rs
similarity index 95%
rename from crates/api_crud/src/user/read.rs
rename to crates/apub/src/api/read_person.rs
index b7b155a74..514bcfccc 100644
--- a/crates/api_crud/src/user/read.rs
+++ b/crates/apub/src/api/read_person.rs
@@ -1,11 +1,10 @@
-use crate::PerformCrud;
+use crate::{api::PerformApub, fetcher::resolve_actor_identifier, objects::person::ApubPerson};
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   person::{GetPersonDetails, GetPersonDetailsResponse},
   utils::{check_private_instance, get_local_user_view_from_jwt_opt},
-  LemmyContext,
 };
-use lemmy_apub::{fetcher::resolve_actor_identifier, objects::person::ApubPerson};
 use lemmy_db_schema::{
   source::{local_site::LocalSite, person::Person},
   utils::post_to_comment_sort_type,
@@ -15,7 +14,7 @@ use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe};
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
-impl PerformCrud for GetPersonDetails {
+impl PerformApub for GetPersonDetails {
   type Response = GetPersonDetailsResponse;
 
   #[tracing::instrument(skip(self, context, _websocket_id))]
diff --git a/crates/api/src/site/resolve_object.rs b/crates/apub/src/api/resolve_object.rs
similarity index 93%
rename from crates/api/src/site/resolve_object.rs
rename to crates/apub/src/api/resolve_object.rs
index 47aa248e7..c179ed582 100644
--- a/crates/api/src/site/resolve_object.rs
+++ b/crates/apub/src/api/resolve_object.rs
@@ -1,19 +1,21 @@
-use crate::Perform;
+use crate::{
+  api::PerformApub,
+  fetcher::search::{search_query_to_object_id, SearchableObjects},
+};
 use actix_web::web::Data;
 use diesel::NotFound;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{ResolveObject, ResolveObjectResponse},
   utils::{check_private_instance, get_local_user_view_from_jwt_opt},
-  LemmyContext,
 };
-use lemmy_apub::fetcher::search::{search_query_to_object_id, SearchableObjects};
 use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool};
 use lemmy_db_views::structs::{CommentView, PostView};
 use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
-impl Perform for ResolveObject {
+impl PerformApub for ResolveObject {
   type Response = ResolveObjectResponse;
 
   #[tracing::instrument(skip(context, _websocket_id))]
diff --git a/crates/api/src/site/search.rs b/crates/apub/src/api/search.rs
similarity index 97%
rename from crates/api/src/site/search.rs
rename to crates/apub/src/api/search.rs
index 1033f92e6..cad41c549 100644
--- a/crates/api/src/site/search.rs
+++ b/crates/apub/src/api/search.rs
@@ -1,11 +1,14 @@
-use crate::Perform;
+use crate::{
+  api::PerformApub,
+  fetcher::resolve_actor_identifier,
+  objects::community::ApubCommunity,
+};
 use actix_web::web::Data;
 use lemmy_api_common::{
+  context::LemmyContext,
   site::{Search, SearchResponse},
   utils::{check_private_instance, get_local_user_view_from_jwt_opt},
-  LemmyContext,
 };
-use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
   source::{community::Community, local_site::LocalSite},
   traits::DeleteableOrRemoveable,
@@ -17,7 +20,7 @@ use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQu
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
-impl Perform for Search {
+impl PerformApub for Search {
   type Response = SearchResponse;
 
   #[tracing::instrument(skip(context, _websocket_id))]
diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs
index aa38f8bee..9616ddb30 100644
--- a/crates/apub/src/collections/community_moderators.rs
+++ b/crates/apub/src/collections/community_moderators.rs
@@ -11,7 +11,7 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::collection::OrderedCollectionType;
 use chrono::NaiveDateTime;
-use lemmy_api_common::generate_moderators_url;
+use lemmy_api_common::utils::generate_moderators_url;
 use lemmy_db_schema::{
   source::community::{CommunityModerator, CommunityModeratorForm},
   traits::Joinable,
diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs
index 44685430e..a16fbd02b 100644
--- a/crates/apub/src/collections/community_outbox.rs
+++ b/crates/apub/src/collections/community_outbox.rs
@@ -19,7 +19,7 @@ use activitypub_federation::{
 use activitystreams_kinds::collection::OrderedCollectionType;
 use chrono::NaiveDateTime;
 use futures::future::join_all;
-use lemmy_api_common::generate_outbox_url;
+use lemmy_api_common::utils::generate_outbox_url;
 use lemmy_db_schema::{
   source::{person::Person, post::Post},
   traits::Crud,
diff --git a/crates/apub/src/collections/mod.rs b/crates/apub/src/collections/mod.rs
index 4c27beb41..40bdf1206 100644
--- a/crates/apub/src/collections/mod.rs
+++ b/crates/apub/src/collections/mod.rs
@@ -1,5 +1,5 @@
 use crate::objects::community::ApubCommunity;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 
 pub(crate) mod community_moderators;
 pub(crate) mod community_outbox;
diff --git a/crates/apub/src/fetcher/mod.rs b/crates/apub/src/fetcher/mod.rs
index 563989241..0ce4dd8f6 100644
--- a/crates/apub/src/fetcher/mod.rs
+++ b/crates/apub/src/fetcher/mod.rs
@@ -1,7 +1,7 @@
 use crate::{fetcher::webfinger::webfinger_resolve_actor, ActorType};
 use activitypub_federation::traits::ApubObject;
 use itertools::Itertools;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::traits::ApubActor;
 use lemmy_utils::error::LemmyError;
 
diff --git a/crates/apub/src/fetcher/post_or_comment.rs b/crates/apub/src/fetcher/post_or_comment.rs
index b44d9f16f..68e176b01 100644
--- a/crates/apub/src/fetcher/post_or_comment.rs
+++ b/crates/apub/src/fetcher/post_or_comment.rs
@@ -7,7 +7,7 @@ use crate::{
 };
 use activitypub_federation::traits::ApubObject;
 use chrono::NaiveDateTime;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{community::Community, post::Post},
   traits::Crud,
@@ -97,16 +97,6 @@ impl ApubObject for PostOrComment {
   }
 }
 
-impl PostOrComment {
-  pub(crate) fn ap_id(&self) -> Url {
-    match self {
-      PostOrComment::Post(p) => p.ap_id.clone(),
-      PostOrComment::Comment(c) => c.ap_id.clone(),
-    }
-    .into()
-  }
-}
-
 #[async_trait::async_trait(?Send)]
 impl InCommunity for PostOrComment {
   async fn community(
diff --git a/crates/apub/src/fetcher/search.rs b/crates/apub/src/fetcher/search.rs
index 95b47f5e4..ffc2c9611 100644
--- a/crates/apub/src/fetcher/search.rs
+++ b/crates/apub/src/fetcher/search.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
 use chrono::NaiveDateTime;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::Deserialize;
 use url::Url;
@@ -15,7 +15,7 @@ use url::Url;
 /// ObjectId directly, or a webfinger identifier (@user@example.com or !community@example.com)
 /// which gets resolved to an URL.
 #[tracing::instrument(skip_all)]
-pub async fn search_query_to_object_id(
+pub(crate) async fn search_query_to_object_id(
   query: &str,
   local_only: bool,
   context: &LemmyContext,
@@ -54,7 +54,7 @@ pub async fn search_query_to_object_id(
 
 /// The types of ActivityPub objects that can be fetched directly by searching for their ID.
 #[derive(Debug)]
-pub enum SearchableObjects {
+pub(crate) enum SearchableObjects {
   Person(ApubPerson),
   Community(ApubCommunity),
   Post(ApubPost),
@@ -63,7 +63,7 @@ pub enum SearchableObjects {
 
 #[derive(Deserialize)]
 #[serde(untagged)]
-pub enum SearchableApubTypes {
+pub(crate) enum SearchableApubTypes {
   Group(Group),
   Person(Person),
   Page(Page),
diff --git a/crates/apub/src/fetcher/user_or_community.rs b/crates/apub/src/fetcher/user_or_community.rs
index 14fc5c344..3af86be1a 100644
--- a/crates/apub/src/fetcher/user_or_community.rs
+++ b/crates/apub/src/fetcher/user_or_community.rs
@@ -5,7 +5,7 @@ use crate::{
 };
 use activitypub_federation::traits::{Actor, ApubObject};
 use chrono::NaiveDateTime;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/fetcher/webfinger.rs b/crates/apub/src/fetcher/webfinger.rs
index 3ec6fcf81..4746736eb 100644
--- a/crates/apub/src/fetcher/webfinger.rs
+++ b/crates/apub/src/fetcher/webfinger.rs
@@ -2,7 +2,7 @@ use crate::{local_instance, ActorType, FEDERATION_HTTP_FETCH_LIMIT};
 use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
 use anyhow::anyhow;
 use itertools::Itertools;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::newtypes::DbUrl;
 use lemmy_utils::{error::LemmyError, WebfingerResponse};
 use tracing::debug;
@@ -10,7 +10,6 @@ use url::Url;
 
 /// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
 /// using webfinger.
-#[tracing::instrument(skip_all)]
 pub(crate) async fn webfinger_resolve_actor<Kind>(
   identifier: &str,
   local_only: bool,
diff --git a/crates/apub/src/http/comment.rs b/crates/apub/src/http/comment.rs
index fbadd42ba..5e7de7e25 100644
--- a/crates/apub/src/http/comment.rs
+++ b/crates/apub/src/http/comment.rs
@@ -5,7 +5,7 @@ use crate::{
 use activitypub_federation::traits::ApubObject;
 use actix_web::{web, web::Path, HttpResponse};
 use diesel::result::Error::NotFound;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud};
 use lemmy_utils::error::LemmyError;
 use serde::Deserialize;
diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs
index 487ad5512..74809509f 100644
--- a/crates/apub/src/http/community.rs
+++ b/crates/apub/src/http/community.rs
@@ -16,7 +16,7 @@ use activitypub_federation::{
   traits::ApubObject,
 };
 use actix_web::{web, HttpRequest, HttpResponse};
-use lemmy_api_common::{generate_outbox_url, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url};
 use lemmy_db_schema::{source::community::Community, traits::ApubActor};
 use lemmy_utils::error::LemmyError;
 use serde::Deserialize;
diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs
index c4602c337..726834c00 100644
--- a/crates/apub/src/http/mod.rs
+++ b/crates/apub/src/http/mod.rs
@@ -15,7 +15,7 @@ use activitypub_federation::{
 };
 use actix_web::{web, HttpRequest, HttpResponse};
 use http::StatusCode;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::source::activity::Activity;
 use lemmy_utils::error::LemmyError;
 use once_cell::sync::OnceCell;
diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs
index 0e838fe13..6a1bc5b35 100644
--- a/crates/apub/src/http/person.rs
+++ b/crates/apub/src/http/person.rs
@@ -7,7 +7,7 @@ use crate::{
 };
 use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
 use actix_web::{web, HttpRequest, HttpResponse};
-use lemmy_api_common::{generate_outbox_url, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url};
 use lemmy_db_schema::{source::person::Person, traits::ApubActor};
 use lemmy_utils::error::LemmyError;
 use serde::Deserialize;
diff --git a/crates/apub/src/http/post.rs b/crates/apub/src/http/post.rs
index 85a4406f5..bea5da3a4 100644
--- a/crates/apub/src/http/post.rs
+++ b/crates/apub/src/http/post.rs
@@ -5,7 +5,7 @@ use crate::{
 use activitypub_federation::traits::ApubObject;
 use actix_web::{web, HttpResponse};
 use diesel::result::Error::NotFound;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud};
 use lemmy_utils::error::LemmyError;
 use serde::Deserialize;
diff --git a/crates/apub/src/http/site.rs b/crates/apub/src/http/site.rs
index 7a945ac72..fe6c34f63 100644
--- a/crates/apub/src/http/site.rs
+++ b/crates/apub/src/http/site.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
 use actix_web::{web, HttpRequest, HttpResponse};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_views::structs::SiteView;
 use lemmy_utils::error::LemmyError;
 use url::Url;
diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs
index 92ae014b1..0908a1f4f 100644
--- a/crates/apub/src/lib.rs
+++ b/crates/apub/src/lib.rs
@@ -7,7 +7,7 @@ use activitypub_federation::{
   UrlVerifier,
 };
 use async_trait::async_trait;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{activity::Activity, instance::Instance, local_site::LocalSite},
   utils::DbPool,
@@ -19,6 +19,7 @@ use url::Url;
 
 pub mod activities;
 pub(crate) mod activity_lists;
+pub mod api;
 pub(crate) mod collections;
 pub mod fetcher;
 pub mod http;
@@ -216,3 +217,16 @@ pub trait ActorType: Actor + ApubObject {
     PublicKey::new_main_key(self.actor_id(), self.public_key().to_string())
   }
 }
+
+#[async_trait::async_trait(?Send)]
+pub trait SendActivity {
+  type Response;
+
+  async fn send_activity(
+    _request: &Self,
+    _response: &Self::Response,
+    _context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    Ok(())
+  }
+}
diff --git a/crates/apub/src/mentions.rs b/crates/apub/src/mentions.rs
index ae7d9b6c1..559078942 100644
--- a/crates/apub/src/mentions.rs
+++ b/crates/apub/src/mentions.rs
@@ -5,7 +5,7 @@ use crate::{
 };
 use activitypub_federation::core::object_id::ObjectId;
 use activitystreams_kinds::link::MentionType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{comment::Comment, person::Person, post::Post},
   traits::Crud,
diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs
index a4c6ef737..ab11b00d8 100644
--- a/crates/apub/src/objects/comment.rs
+++ b/crates/apub/src/objects/comment.rs
@@ -20,7 +20,7 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::{object::NoteType, public};
 use chrono::NaiveDateTime;
-use lemmy_api_common::{utils::local_site_opt_to_slur_regex, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
 use lemmy_db_schema::{
   source::{
     comment::{Comment, CommentInsertForm, CommentUpdateForm},
@@ -39,7 +39,7 @@ use std::ops::Deref;
 use url::Url;
 
 #[derive(Clone, Debug)]
-pub struct ApubComment(Comment);
+pub struct ApubComment(pub(crate) Comment);
 
 impl Deref for ApubComment {
   type Target = Comment;
diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs
index d8c1596ae..b309d25fc 100644
--- a/crates/apub/src/objects/community.rs
+++ b/crates/apub/src/objects/community.rs
@@ -18,7 +18,10 @@ use activitypub_federation::{
 use activitystreams_kinds::actor::GroupType;
 use chrono::NaiveDateTime;
 use itertools::Itertools;
-use lemmy_api_common::{generate_moderators_url, generate_outbox_url, LemmyContext};
+use lemmy_api_common::{
+  context::LemmyContext,
+  utils::{generate_moderators_url, generate_outbox_url},
+};
 use lemmy_db_schema::{
   source::{
     actor_language::CommunityLanguage,
diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs
index 18e9d9f25..5ec4fe909 100644
--- a/crates/apub/src/objects/instance.rs
+++ b/crates/apub/src/objects/instance.rs
@@ -20,7 +20,7 @@ use activitypub_federation::{
   utils::verify_domains_match,
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::{utils::local_site_opt_to_slur_regex, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
 use lemmy_db_schema::{
   source::{
     actor_language::SiteLanguage,
@@ -176,15 +176,6 @@ impl Actor for ApubSite {
   }
 }
 
-/// Instance actor is at the root path, so we simply need to clear the path and other unnecessary
-/// parts of the url.
-pub fn instance_actor_id_from_url(mut url: Url) -> Url {
-  url.set_fragment(None);
-  url.set_path("");
-  url.set_query(None);
-  url
-}
-
 /// try to fetch the instance actor (to make things like instance rules available)
 pub(in crate::objects) async fn fetch_instance_actor_for_object(
   object_id: Url,
@@ -192,7 +183,7 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object(
   request_counter: &mut i32,
 ) {
   // try to fetch the instance actor (to make things like instance rules available)
-  let instance_id = instance_actor_id_from_url(object_id);
+  let instance_id = Site::instance_actor_id_from_url(object_id);
   let site = ObjectId::<ApubSite>::new(instance_id.clone())
     .dereference(context, local_instance(context).await, request_counter)
     .await;
diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs
index 1bf621fca..58e1f23f5 100644
--- a/crates/apub/src/objects/mod.rs
+++ b/crates/apub/src/objects/mod.rs
@@ -57,9 +57,9 @@ pub(crate) mod tests {
   use actix::Actor;
   use anyhow::anyhow;
   use lemmy_api_common::{
+    context::LemmyContext,
     request::build_user_agent,
     websocket::chat_server::ChatServer,
-    LemmyContext,
   };
   use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool_for_tests};
   use lemmy_utils::{
@@ -113,6 +113,7 @@ pub(crate) mod tests {
       pool.clone(),
       |_, _, _, _| Box::pin(x()),
       |_, _, _, _| Box::pin(x()),
+      |_, _, _, _| Box::pin(x()),
       client.clone(),
       settings.clone(),
       secret.clone(),
diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs
index 236f3edf3..2017b605c 100644
--- a/crates/apub/src/objects/person.rs
+++ b/crates/apub/src/objects/person.rs
@@ -18,7 +18,10 @@ use activitypub_federation::{
   utils::verify_domains_match,
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::{generate_outbox_url, utils::local_site_opt_to_slur_regex, LemmyContext};
+use lemmy_api_common::{
+  context::LemmyContext,
+  utils::{generate_outbox_url, local_site_opt_to_slur_regex},
+};
 use lemmy_db_schema::{
   source::{
     instance::Instance,
diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs
index f79e3055a..afe025ce2 100644
--- a/crates/apub/src/objects/post.rs
+++ b/crates/apub/src/objects/post.rs
@@ -23,9 +23,9 @@ use activitypub_federation::{
 use activitystreams_kinds::public;
 use chrono::NaiveDateTime;
 use lemmy_api_common::{
+  context::LemmyContext,
   request::fetch_site_data,
   utils::local_site_opt_to_slur_regex,
-  LemmyContext,
 };
 use lemmy_db_schema::{
   self,
@@ -46,7 +46,7 @@ use std::ops::Deref;
 use url::Url;
 
 #[derive(Clone, Debug)]
-pub struct ApubPost(Post);
+pub struct ApubPost(pub(crate) Post);
 
 impl Deref for ApubPost {
   type Target = Post;
diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs
index f4807076b..4d8c70076 100644
--- a/crates/apub/src/objects/private_message.rs
+++ b/crates/apub/src/objects/private_message.rs
@@ -15,7 +15,7 @@ use activitypub_federation::{
   utils::verify_domains_match,
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::{utils::check_person_block, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::check_person_block};
 use lemmy_db_schema::{
   source::{
     person::Person,
@@ -31,7 +31,7 @@ use std::ops::Deref;
 use url::Url;
 
 #[derive(Clone, Debug)]
-pub struct ApubPrivateMessage(PrivateMessage);
+pub struct ApubPrivateMessage(pub(crate) PrivateMessage);
 
 impl Deref for ApubPrivateMessage {
   type Target = PrivateMessage;
diff --git a/crates/apub/src/protocol/activities/block/block_user.rs b/crates/apub/src/protocol/activities/block/block_user.rs
index 364fe9b9f..3ac040ced 100644
--- a/crates/apub/src/protocol/activities/block/block_user.rs
+++ b/crates/apub/src/protocol/activities/block/block_user.rs
@@ -8,7 +8,7 @@ use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserial
 use activitystreams_kinds::activity::BlockType;
 use anyhow::anyhow;
 use chrono::{DateTime, FixedOffset};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use serde_with::skip_serializing_none;
diff --git a/crates/apub/src/protocol/activities/block/undo_block_user.rs b/crates/apub/src/protocol/activities/block/undo_block_user.rs
index 9646315ff..d818af9de 100644
--- a/crates/apub/src/protocol/activities/block/undo_block_user.rs
+++ b/crates/apub/src/protocol/activities/block/undo_block_user.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
 use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use serde_with::skip_serializing_none;
diff --git a/crates/apub/src/protocol/activities/community/add_mod.rs b/crates/apub/src/protocol/activities/community/add_mod.rs
index c8ea5daa9..22fc07fcb 100644
--- a/crates/apub/src/protocol/activities/community/add_mod.rs
+++ b/crates/apub/src/protocol/activities/community/add_mod.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
 use activitystreams_kinds::activity::AddType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/protocol/activities/community/remove_mod.rs b/crates/apub/src/protocol/activities/community/remove_mod.rs
index 57d9c1bca..ce46fb920 100644
--- a/crates/apub/src/protocol/activities/community/remove_mod.rs
+++ b/crates/apub/src/protocol/activities/community/remove_mod.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
 use activitystreams_kinds::activity::RemoveType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs
index a66e08b05..0a1ef650f 100644
--- a/crates/apub/src/protocol/activities/community/report.rs
+++ b/crates/apub/src/protocol/activities/community/report.rs
@@ -7,7 +7,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
 use activitystreams_kinds::activity::FlagType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/protocol/activities/community/update.rs b/crates/apub/src/protocol/activities/community/update.rs
index 8bf39a826..9a2f1f481 100644
--- a/crates/apub/src/protocol/activities/community/update.rs
+++ b/crates/apub/src/protocol/activities/community/update.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
 use activitystreams_kinds::activity::UpdateType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/protocol/activities/create_or_update/note.rs b/crates/apub/src/protocol/activities/create_or_update/note.rs
index a952811be..dfa1dbe9f 100644
--- a/crates/apub/src/protocol/activities/create_or_update/note.rs
+++ b/crates/apub/src/protocol/activities/create_or_update/note.rs
@@ -6,7 +6,7 @@ use crate::{
   protocol::{activities::CreateOrUpdateType, objects::note::Note, InCommunity},
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{source::community::Community, traits::Crud};
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
diff --git a/crates/apub/src/protocol/activities/create_or_update/page.rs b/crates/apub/src/protocol/activities/create_or_update/page.rs
index 9d45ae0bf..2c15d9f90 100644
--- a/crates/apub/src/protocol/activities/create_or_update/page.rs
+++ b/crates/apub/src/protocol/activities/create_or_update/page.rs
@@ -5,7 +5,7 @@ use crate::{
   protocol::{activities::CreateOrUpdateType, objects::page::Page, InCommunity},
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs
index 8a0c1b0bd..d92ac2456 100644
--- a/crates/apub/src/protocol/activities/deletion/delete.rs
+++ b/crates/apub/src/protocol/activities/deletion/delete.rs
@@ -7,7 +7,7 @@ use crate::{
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
 use activitystreams_kinds::activity::DeleteType;
 use anyhow::anyhow;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{community::Community, post::Post},
   traits::Crud,
diff --git a/crates/apub/src/protocol/activities/deletion/undo_delete.rs b/crates/apub/src/protocol/activities/deletion/undo_delete.rs
index 0e2c0f982..d5249ba9a 100644
--- a/crates/apub/src/protocol/activities/deletion/undo_delete.rs
+++ b/crates/apub/src/protocol/activities/deletion/undo_delete.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
 use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use serde_with::skip_serializing_none;
diff --git a/crates/apub/src/protocol/activities/voting/undo_vote.rs b/crates/apub/src/protocol/activities/voting/undo_vote.rs
index 7c23036c6..0973c76a8 100644
--- a/crates/apub/src/protocol/activities/voting/undo_vote.rs
+++ b/crates/apub/src/protocol/activities/voting/undo_vote.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use activitypub_federation::core::object_id::ObjectId;
 use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use url::Url;
diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs
index a88aa3da9..2a09a45ea 100644
--- a/crates/apub/src/protocol/activities/voting/vote.rs
+++ b/crates/apub/src/protocol/activities/voting/vote.rs
@@ -6,7 +6,7 @@ use crate::{
   protocol::InCommunity,
 };
 use activitypub_federation::core::object_id::ObjectId;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
 use std::convert::TryFrom;
diff --git a/crates/apub/src/protocol/collections/group_followers.rs b/crates/apub/src/protocol/collections/group_followers.rs
index 23ee169d4..b9cf85182 100644
--- a/crates/apub/src/protocol/collections/group_followers.rs
+++ b/crates/apub/src/protocol/collections/group_followers.rs
@@ -1,5 +1,5 @@
 use activitystreams_kinds::collection::CollectionType;
-use lemmy_api_common::{generate_followers_url, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::generate_followers_url};
 use lemmy_db_schema::source::community::Community;
 use lemmy_db_views_actor::structs::CommunityFollowerView;
 use lemmy_utils::error::LemmyError;
diff --git a/crates/apub/src/protocol/mod.rs b/crates/apub/src/protocol/mod.rs
index 16bdb1b29..ef0f2a875 100644
--- a/crates/apub/src/protocol/mod.rs
+++ b/crates/apub/src/protocol/mod.rs
@@ -1,7 +1,7 @@
 use crate::{local_instance, objects::community::ApubCommunity};
 use activitypub_federation::{deser::values::MediaTypeMarkdown, utils::fetch_object_http};
 use activitystreams_kinds::object::ImageType;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::newtypes::DbUrl;
 use lemmy_utils::error::LemmyError;
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs
index 316af1a8d..03f71a348 100644
--- a/crates/apub/src/protocol/objects/group.rs
+++ b/crates/apub/src/protocol/objects/group.rs
@@ -19,7 +19,7 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::actor::GroupType;
 use chrono::{DateTime, FixedOffset};
-use lemmy_api_common::{utils::local_site_opt_to_slur_regex, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
 use lemmy_db_schema::{
   newtypes::InstanceId,
   source::community::{CommunityInsertForm, CommunityUpdateForm},
diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs
index f8263d874..f561c313d 100644
--- a/crates/apub/src/protocol/objects/note.rs
+++ b/crates/apub/src/protocol/objects/note.rs
@@ -15,7 +15,7 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::object::NoteType;
 use chrono::{DateTime, FixedOffset};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{community::Community, post::Post},
   traits::Crud,
diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs
index 1ff12bc29..a62fb8e57 100644
--- a/crates/apub/src/protocol/objects/page.rs
+++ b/crates/apub/src/protocol/objects/page.rs
@@ -20,7 +20,7 @@ use activitystreams_kinds::{
 };
 use chrono::{DateTime, FixedOffset};
 use itertools::Itertools;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::newtypes::DbUrl;
 use lemmy_utils::error::LemmyError;
 use serde::{Deserialize, Serialize};
diff --git a/crates/db_schema/src/impls/site.rs b/crates/db_schema/src/impls/site.rs
index 42d384eb9..96f8a2f05 100644
--- a/crates/db_schema/src/impls/site.rs
+++ b/crates/db_schema/src/impls/site.rs
@@ -75,4 +75,13 @@ impl Site {
     let conn = &mut get_conn(pool).await?;
     site.order_by(id).offset(1).get_results::<Self>(conn).await
   }
+
+  /// Instance actor is at the root path, so we simply need to clear the path and other unnecessary
+  /// parts of the url.
+  pub fn instance_actor_id_from_url(mut url: Url) -> Url {
+    url.set_fragment(None);
+    url.set_path("");
+    url.set_query(None);
+    url
+  }
 }
diff --git a/crates/routes/Cargo.toml b/crates/routes/Cargo.toml
index c5349a20a..bc2b573e5 100644
--- a/crates/routes/Cargo.toml
+++ b/crates/routes/Cargo.toml
@@ -16,7 +16,7 @@ lemmy_utils = { workspace = true }
 lemmy_db_views = { workspace = true }
 lemmy_db_views_actor = { workspace = true }
 lemmy_db_schema = { workspace = true }
-lemmy_api_common = { workspace = true }
+lemmy_api_common = { workspace = true, features = ["full"] }
 diesel = { workspace = true }
 actix-web = { workspace = true }
 anyhow = { workspace = true }
diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs
index 72181bf6a..594bf1153 100644
--- a/crates/routes/src/feeds.rs
+++ b/crates/routes/src/feeds.rs
@@ -1,7 +1,7 @@
 use actix_web::{error::ErrorBadRequest, web, Error, HttpRequest, HttpResponse, Result};
 use anyhow::anyhow;
 use chrono::{DateTime, NaiveDateTime, Utc};
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   newtypes::LocalUserId,
   source::{community::Community, local_user::LocalUser, person::Person},
diff --git a/crates/routes/src/images.rs b/crates/routes/src/images.rs
index 6e839e214..0b1f6fbdd 100644
--- a/crates/routes/src/images.rs
+++ b/crates/routes/src/images.rs
@@ -11,7 +11,7 @@ use actix_web::{
   HttpResponse,
 };
 use futures::stream::{Stream, StreamExt};
-use lemmy_api_common::{utils::get_local_user_view_from_jwt, LemmyContext};
+use lemmy_api_common::{context::LemmyContext, utils::get_local_user_view_from_jwt};
 use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_utils::{claims::Claims, rate_limit::RateLimitCell, REQWEST_TIMEOUT};
 use reqwest::Body;
diff --git a/crates/routes/src/nodeinfo.rs b/crates/routes/src/nodeinfo.rs
index 3a8b41859..13786e3b6 100644
--- a/crates/routes/src/nodeinfo.rs
+++ b/crates/routes/src/nodeinfo.rs
@@ -1,6 +1,6 @@
 use actix_web::{error::ErrorBadRequest, web, Error, HttpResponse, Result};
 use anyhow::anyhow;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_views::structs::SiteView;
 use lemmy_utils::{error::LemmyError, version};
 use serde::{Deserialize, Serialize};
diff --git a/crates/routes/src/webfinger.rs b/crates/routes/src/webfinger.rs
index 68482a8d1..9056ea632 100644
--- a/crates/routes/src/webfinger.rs
+++ b/crates/routes/src/webfinger.rs
@@ -1,6 +1,6 @@
 use actix_web::{web, web::Query, HttpResponse};
 use anyhow::Context;
-use lemmy_api_common::LemmyContext;
+use lemmy_api_common::context::LemmyContext;
 use lemmy_db_schema::{
   source::{community::Community, person::Person},
   traits::ApubActor,
diff --git a/src/api_routes.rs b/src/api_routes.rs
index 626857c69..77e5c27d7 100644
--- a/src/api_routes.rs
+++ b/src/api_routes.rs
@@ -28,6 +28,7 @@ use lemmy_api_common::{
     RemoveCommunity,
     TransferCommunity,
   },
+  context::LemmyContext,
   person::{
     AddAdmin,
     BanPerson,
@@ -96,13 +97,18 @@ use lemmy_api_common::{
   },
   websocket::{
     routes::chat_route,
+    serialize_websocket_message,
     structs::{CommunityJoin, ModJoin, PostJoin, UserJoin},
+    UserOperation,
+    UserOperationApub,
+    UserOperationCrud,
   },
-  LemmyContext,
 };
 use lemmy_api_crud::PerformCrud;
-use lemmy_utils::rate_limit::RateLimitCell;
+use lemmy_apub::{api::PerformApub, SendActivity};
+use lemmy_utils::{error::LemmyError, rate_limit::RateLimitCell, ConnectionId};
 use serde::Deserialize;
+use std::result;
 
 pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
   cfg.service(
@@ -126,12 +132,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
       .service(
         web::resource("/search")
           .wrap(rate_limit.search())
-          .route(web::get().to(route_get::<Search>)),
+          .route(web::get().to(route_get_apub::<Search>)),
       )
       .service(
         web::resource("/resolve_object")
           .wrap(rate_limit.message())
-          .route(web::get().to(route_get::<ResolveObject>)),
+          .route(web::get().to(route_get_apub::<ResolveObject>)),
       )
       // Community
       .service(
@@ -143,7 +149,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
       .service(
         web::scope("/community")
           .wrap(rate_limit.message())
-          .route("", web::get().to(route_get_crud::<GetCommunity>))
+          .route("", web::get().to(route_get_apub::<GetCommunity>))
           .route("", web::put().to(route_post_crud::<EditCommunity>))
           .route("/hide", web::put().to(route_post::<HideCommunity>))
           .route("/list", web::get().to(route_get_crud::<ListCommunities>))
@@ -185,7 +191,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
           )
           .route("/lock", web::post().to(route_post::<LockPost>))
           .route("/sticky", web::post().to(route_post::<StickyPost>))
-          .route("/list", web::get().to(route_get_crud::<GetPosts>))
+          .route("/list", web::get().to(route_get_apub::<GetPosts>))
           .route("/like", web::post().to(route_post::<CreatePostLike>))
           .route("/save", web::put().to(route_post::<SavePost>))
           .route("/join", web::post().to(route_post::<PostJoin>))
@@ -221,7 +227,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
           )
           .route("/like", web::post().to(route_post::<CreateCommentLike>))
           .route("/save", web::put().to(route_post::<SaveComment>))
-          .route("/list", web::get().to(route_get_crud::<GetComments>))
+          .route("/list", web::get().to(route_get_apub::<GetComments>))
           .route("/report", web::post().to(route_post::<CreateCommentReport>))
           .route(
             "/report/resolve",
@@ -279,7 +285,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
       .service(
         web::scope("/user")
           .wrap(rate_limit.message())
-          .route("", web::get().to(route_get_crud::<GetPersonDetails>))
+          .route("", web::get().to(route_get_apub::<GetPersonDetails>))
           .route("/mention", web::get().to(route_get::<GetPersonMentions>))
           .route(
             "/mention/mark_as_read",
@@ -352,19 +358,21 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
   );
 }
 
-async fn perform<Request>(
-  data: Request,
+async fn perform<'a, Data>(
+  data: Data,
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, Error>
 where
-  Request: Perform,
-  Request: Send + 'static,
+  Data: Perform
+    + SendActivity<Response = <Data as Perform>::Response>
+    + Clone
+    + Deserialize<'a>
+    + Send
+    + 'static,
 {
-  let res = data
-    .perform(&context, None)
-    .await
-    .map(|json| HttpResponse::Ok().json(json))?;
-  Ok(res)
+  let res = data.perform(&context, None).await?;
+  SendActivity::send_activity(&data, &res, &context).await?;
+  Ok(HttpResponse::Ok().json(res))
 }
 
 async fn route_get<'a, Data>(
@@ -372,34 +380,63 @@ async fn route_get<'a, Data>(
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, Error>
 where
-  Data: Deserialize<'a> + Send + 'static + Perform,
+  Data: Perform
+    + SendActivity<Response = <Data as Perform>::Response>
+    + Clone
+    + Deserialize<'a>
+    + Send
+    + 'static,
 {
   perform::<Data>(data.0, context).await
 }
 
+async fn route_get_apub<'a, Data>(
+  data: web::Query<Data>,
+  context: web::Data<LemmyContext>,
+) -> Result<HttpResponse, Error>
+where
+  Data: PerformApub
+    + SendActivity<Response = <Data as PerformApub>::Response>
+    + Clone
+    + Deserialize<'a>
+    + Send
+    + 'static,
+{
+  let res = data.perform(&context, None).await?;
+  SendActivity::send_activity(&data.0, &res, &context).await?;
+  Ok(HttpResponse::Ok().json(res))
+}
+
 async fn route_post<'a, Data>(
   data: web::Json<Data>,
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, Error>
 where
-  Data: Deserialize<'a> + Send + 'static + Perform,
+  Data: Perform
+    + SendActivity<Response = <Data as Perform>::Response>
+    + Clone
+    + Deserialize<'a>
+    + Send
+    + 'static,
 {
   perform::<Data>(data.0, context).await
 }
 
-async fn perform_crud<Request>(
-  data: Request,
+async fn perform_crud<'a, Data>(
+  data: Data,
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, Error>
 where
-  Request: PerformCrud,
-  Request: Send + 'static,
+  Data: PerformCrud
+    + SendActivity<Response = <Data as PerformCrud>::Response>
+    + Clone
+    + Deserialize<'a>
+    + Send
+    + 'static,
 {
-  let res = data
-    .perform(&context, None)
-    .await
-    .map(|json| HttpResponse::Ok().json(json))?;
-  Ok(res)
+  let res = data.perform(&context, None).await?;
+  SendActivity::send_activity(&data, &res, &context).await?;
+  Ok(HttpResponse::Ok().json(res))
 }
 
 async fn route_get_crud<'a, Data>(
@@ -407,7 +444,12 @@ async fn route_get_crud<'a, Data>(
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, Error>
 where
-  Data: Deserialize<'a> + Send + 'static + PerformCrud,
+  Data: PerformCrud
+    + SendActivity<Response = <Data as PerformCrud>::Response>
+    + Clone
+    + Deserialize<'a>
+    + Send
+    + 'static,
 {
   perform_crud::<Data>(data.0, context).await
 }
@@ -417,7 +459,340 @@ async fn route_post_crud<'a, Data>(
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, Error>
 where
-  Data: Deserialize<'a> + Send + 'static + PerformCrud,
+  Data: PerformCrud
+    + SendActivity<Response = <Data as PerformCrud>::Response>
+    + Clone
+    + Deserialize<'a>
+    + Send
+    + 'static,
 {
   perform_crud::<Data>(data.0, context).await
 }
+
+pub async fn match_websocket_operation_crud(
+  context: LemmyContext,
+  id: ConnectionId,
+  op: UserOperationCrud,
+  data: &str,
+) -> result::Result<String, LemmyError> {
+  match op {
+    // User ops
+    UserOperationCrud::Register => {
+      do_websocket_operation_crud::<Register>(context, id, op, data).await
+    }
+    UserOperationCrud::DeleteAccount => {
+      do_websocket_operation_crud::<DeleteAccount>(context, id, op, data).await
+    }
+
+    // Private Message ops
+    UserOperationCrud::CreatePrivateMessage => {
+      do_websocket_operation_crud::<CreatePrivateMessage>(context, id, op, data).await
+    }
+    UserOperationCrud::EditPrivateMessage => {
+      do_websocket_operation_crud::<EditPrivateMessage>(context, id, op, data).await
+    }
+    UserOperationCrud::DeletePrivateMessage => {
+      do_websocket_operation_crud::<DeletePrivateMessage>(context, id, op, data).await
+    }
+    UserOperationCrud::GetPrivateMessages => {
+      do_websocket_operation_crud::<GetPrivateMessages>(context, id, op, data).await
+    }
+
+    // Site ops
+    UserOperationCrud::CreateSite => {
+      do_websocket_operation_crud::<CreateSite>(context, id, op, data).await
+    }
+    UserOperationCrud::EditSite => {
+      do_websocket_operation_crud::<EditSite>(context, id, op, data).await
+    }
+    UserOperationCrud::GetSite => {
+      do_websocket_operation_crud::<GetSite>(context, id, op, data).await
+    }
+
+    // Community ops
+    UserOperationCrud::ListCommunities => {
+      do_websocket_operation_crud::<ListCommunities>(context, id, op, data).await
+    }
+    UserOperationCrud::CreateCommunity => {
+      do_websocket_operation_crud::<CreateCommunity>(context, id, op, data).await
+    }
+    UserOperationCrud::EditCommunity => {
+      do_websocket_operation_crud::<EditCommunity>(context, id, op, data).await
+    }
+    UserOperationCrud::DeleteCommunity => {
+      do_websocket_operation_crud::<DeleteCommunity>(context, id, op, data).await
+    }
+    UserOperationCrud::RemoveCommunity => {
+      do_websocket_operation_crud::<RemoveCommunity>(context, id, op, data).await
+    }
+
+    // Post ops
+    UserOperationCrud::CreatePost => {
+      do_websocket_operation_crud::<CreatePost>(context, id, op, data).await
+    }
+    UserOperationCrud::GetPost => {
+      do_websocket_operation_crud::<GetPost>(context, id, op, data).await
+    }
+    UserOperationCrud::EditPost => {
+      do_websocket_operation_crud::<EditPost>(context, id, op, data).await
+    }
+    UserOperationCrud::DeletePost => {
+      do_websocket_operation_crud::<DeletePost>(context, id, op, data).await
+    }
+    UserOperationCrud::RemovePost => {
+      do_websocket_operation_crud::<RemovePost>(context, id, op, data).await
+    }
+
+    // Comment ops
+    UserOperationCrud::CreateComment => {
+      do_websocket_operation_crud::<CreateComment>(context, id, op, data).await
+    }
+    UserOperationCrud::EditComment => {
+      do_websocket_operation_crud::<EditComment>(context, id, op, data).await
+    }
+    UserOperationCrud::DeleteComment => {
+      do_websocket_operation_crud::<DeleteComment>(context, id, op, data).await
+    }
+    UserOperationCrud::RemoveComment => {
+      do_websocket_operation_crud::<RemoveComment>(context, id, op, data).await
+    }
+    UserOperationCrud::GetComment => {
+      do_websocket_operation_crud::<GetComment>(context, id, op, data).await
+    }
+  }
+}
+
+async fn do_websocket_operation_crud<'a, 'b, Data>(
+  context: LemmyContext,
+  id: ConnectionId,
+  op: UserOperationCrud,
+  data: &str,
+) -> result::Result<String, LemmyError>
+where
+  Data: PerformCrud + SendActivity<Response = <Data as PerformCrud>::Response>,
+  for<'de> Data: Deserialize<'de>,
+{
+  let parsed_data: Data = serde_json::from_str(data)?;
+  let res = parsed_data
+    .perform(&web::Data::new(context.clone()), Some(id))
+    .await?;
+  SendActivity::send_activity(&parsed_data, &res, &context).await?;
+  serialize_websocket_message(&op, &res)
+}
+
+pub async fn match_websocket_operation_apub(
+  context: LemmyContext,
+  id: ConnectionId,
+  op: UserOperationApub,
+  data: &str,
+) -> result::Result<String, LemmyError> {
+  match op {
+    UserOperationApub::GetPersonDetails => {
+      do_websocket_operation_apub::<GetPersonDetails>(context, id, op, data).await
+    }
+    UserOperationApub::GetCommunity => {
+      do_websocket_operation_apub::<GetCommunity>(context, id, op, data).await
+    }
+    UserOperationApub::GetComments => {
+      do_websocket_operation_apub::<GetComments>(context, id, op, data).await
+    }
+    UserOperationApub::GetPosts => {
+      do_websocket_operation_apub::<GetPosts>(context, id, op, data).await
+    }
+    UserOperationApub::ResolveObject => {
+      do_websocket_operation_apub::<ResolveObject>(context, id, op, data).await
+    }
+    UserOperationApub::Search => do_websocket_operation_apub::<Search>(context, id, op, data).await,
+  }
+}
+
+async fn do_websocket_operation_apub<'a, 'b, Data>(
+  context: LemmyContext,
+  id: ConnectionId,
+  op: UserOperationApub,
+  data: &str,
+) -> result::Result<String, LemmyError>
+where
+  Data: PerformApub + SendActivity<Response = <Data as PerformApub>::Response>,
+  for<'de> Data: Deserialize<'de>,
+{
+  let parsed_data: Data = serde_json::from_str(data)?;
+  let res = parsed_data
+    .perform(&web::Data::new(context.clone()), Some(id))
+    .await?;
+  SendActivity::send_activity(&parsed_data, &res, &context).await?;
+  serialize_websocket_message(&op, &res)
+}
+
+pub async fn match_websocket_operation(
+  context: LemmyContext,
+  id: ConnectionId,
+  op: UserOperation,
+  data: &str,
+) -> result::Result<String, LemmyError> {
+  match op {
+    // User ops
+    UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
+    UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
+    UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
+    UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
+    UserOperation::GetUnreadRegistrationApplicationCount => {
+      do_websocket_operation::<GetUnreadRegistrationApplicationCount>(context, id, op, data).await
+    }
+    UserOperation::ListRegistrationApplications => {
+      do_websocket_operation::<ListRegistrationApplications>(context, id, op, data).await
+    }
+    UserOperation::ApproveRegistrationApplication => {
+      do_websocket_operation::<ApproveRegistrationApplication>(context, id, op, data).await
+    }
+    UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
+    UserOperation::GetBannedPersons => {
+      do_websocket_operation::<GetBannedPersons>(context, id, op, data).await
+    }
+    UserOperation::BlockPerson => {
+      do_websocket_operation::<BlockPerson>(context, id, op, data).await
+    }
+    UserOperation::GetPersonMentions => {
+      do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
+    }
+    UserOperation::MarkPersonMentionAsRead => {
+      do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
+    }
+    UserOperation::MarkCommentReplyAsRead => {
+      do_websocket_operation::<MarkCommentReplyAsRead>(context, id, op, data).await
+    }
+    UserOperation::MarkAllAsRead => {
+      do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
+    }
+    UserOperation::PasswordReset => {
+      do_websocket_operation::<PasswordReset>(context, id, op, data).await
+    }
+    UserOperation::PasswordChange => {
+      do_websocket_operation::<PasswordChangeAfterReset>(context, id, op, data).await
+    }
+    UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
+    UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
+    UserOperation::CommunityJoin => {
+      do_websocket_operation::<CommunityJoin>(context, id, op, data).await
+    }
+    UserOperation::ModJoin => do_websocket_operation::<ModJoin>(context, id, op, data).await,
+    UserOperation::SaveUserSettings => {
+      do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
+    }
+    UserOperation::ChangePassword => {
+      do_websocket_operation::<ChangePassword>(context, id, op, data).await
+    }
+    UserOperation::GetReportCount => {
+      do_websocket_operation::<GetReportCount>(context, id, op, data).await
+    }
+    UserOperation::GetUnreadCount => {
+      do_websocket_operation::<GetUnreadCount>(context, id, op, data).await
+    }
+    UserOperation::VerifyEmail => {
+      do_websocket_operation::<VerifyEmail>(context, id, op, data).await
+    }
+
+    // Private Message ops
+    UserOperation::MarkPrivateMessageAsRead => {
+      do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
+    }
+    UserOperation::CreatePrivateMessageReport => {
+      do_websocket_operation::<CreatePrivateMessageReport>(context, id, op, data).await
+    }
+    UserOperation::ResolvePrivateMessageReport => {
+      do_websocket_operation::<ResolvePrivateMessageReport>(context, id, op, data).await
+    }
+    UserOperation::ListPrivateMessageReports => {
+      do_websocket_operation::<ListPrivateMessageReports>(context, id, op, data).await
+    }
+
+    // Site ops
+    UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
+    UserOperation::PurgePerson => {
+      do_websocket_operation::<PurgePerson>(context, id, op, data).await
+    }
+    UserOperation::PurgeCommunity => {
+      do_websocket_operation::<PurgeCommunity>(context, id, op, data).await
+    }
+    UserOperation::PurgePost => do_websocket_operation::<PurgePost>(context, id, op, data).await,
+    UserOperation::PurgeComment => {
+      do_websocket_operation::<PurgeComment>(context, id, op, data).await
+    }
+    UserOperation::TransferCommunity => {
+      do_websocket_operation::<TransferCommunity>(context, id, op, data).await
+    }
+    UserOperation::LeaveAdmin => do_websocket_operation::<LeaveAdmin>(context, id, op, data).await,
+
+    // Community ops
+    UserOperation::FollowCommunity => {
+      do_websocket_operation::<FollowCommunity>(context, id, op, data).await
+    }
+    UserOperation::BlockCommunity => {
+      do_websocket_operation::<BlockCommunity>(context, id, op, data).await
+    }
+    UserOperation::BanFromCommunity => {
+      do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
+    }
+    UserOperation::AddModToCommunity => {
+      do_websocket_operation::<AddModToCommunity>(context, id, op, data).await
+    }
+
+    // Post ops
+    UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
+    UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
+    UserOperation::CreatePostLike => {
+      do_websocket_operation::<CreatePostLike>(context, id, op, data).await
+    }
+    UserOperation::MarkPostAsRead => {
+      do_websocket_operation::<MarkPostAsRead>(context, id, op, data).await
+    }
+    UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
+    UserOperation::CreatePostReport => {
+      do_websocket_operation::<CreatePostReport>(context, id, op, data).await
+    }
+    UserOperation::ListPostReports => {
+      do_websocket_operation::<ListPostReports>(context, id, op, data).await
+    }
+    UserOperation::ResolvePostReport => {
+      do_websocket_operation::<ResolvePostReport>(context, id, op, data).await
+    }
+    UserOperation::GetSiteMetadata => {
+      do_websocket_operation::<GetSiteMetadata>(context, id, op, data).await
+    }
+
+    // Comment ops
+    UserOperation::SaveComment => {
+      do_websocket_operation::<SaveComment>(context, id, op, data).await
+    }
+    UserOperation::CreateCommentLike => {
+      do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
+    }
+    UserOperation::CreateCommentReport => {
+      do_websocket_operation::<CreateCommentReport>(context, id, op, data).await
+    }
+    UserOperation::ListCommentReports => {
+      do_websocket_operation::<ListCommentReports>(context, id, op, data).await
+    }
+    UserOperation::ResolveCommentReport => {
+      do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await
+    }
+  }
+}
+
+async fn do_websocket_operation<'a, 'b, Data>(
+  context: LemmyContext,
+  id: ConnectionId,
+  op: UserOperation,
+  data: &str,
+) -> result::Result<String, LemmyError>
+where
+  Data: Perform + SendActivity<Response = <Data as Perform>::Response>,
+  for<'de> Data: Deserialize<'de>,
+{
+  let parsed_data: Data = serde_json::from_str(data)?;
+  let res = parsed_data
+    .perform(&web::Data::new(context.clone()), Some(id))
+    .await?;
+  SendActivity::send_activity(&parsed_data, &res, &context).await?;
+  serialize_websocket_message(&op, &res)
+}
diff --git a/src/code_migrations.rs b/src/code_migrations.rs
index 0c9fecf69..89933a15e 100644
--- a/src/code_migrations.rs
+++ b/src/code_migrations.rs
@@ -9,13 +9,15 @@ use diesel::{
 };
 use diesel_async::RunQueryDsl;
 use lemmy_api_common::{
-  generate_followers_url,
-  generate_inbox_url,
-  generate_local_apub_endpoint,
-  generate_shared_inbox_url,
-  generate_site_inbox_url,
   lemmy_db_views::structs::SiteView,
-  EndpointType,
+  utils::{
+    generate_followers_url,
+    generate_inbox_url,
+    generate_local_apub_endpoint,
+    generate_shared_inbox_url,
+    generate_site_inbox_url,
+    EndpointType,
+  },
 };
 use lemmy_db_schema::{
   source::{
diff --git a/src/main.rs b/src/main.rs
index e4b8e8e32..c60c1823a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,8 +5,8 @@ use actix::prelude::*;
 use actix_web::{middleware, web::Data, App, HttpServer, Result};
 use diesel_migrations::EmbeddedMigrations;
 use doku::json::{AutoComments, CommentsStyle, Formatting, ObjectsStyle};
-use lemmy_api::match_websocket_operation;
 use lemmy_api_common::{
+  context::LemmyContext,
   lemmy_db_views::structs::SiteView,
   request::build_user_agent,
   utils::{
@@ -14,9 +14,7 @@ use lemmy_api_common::{
     local_site_rate_limit_to_rate_limit_config,
   },
   websocket::chat_server::ChatServer,
-  LemmyContext,
 };
-use lemmy_api_crud::match_websocket_operation_crud;
 use lemmy_db_schema::{
   source::secret::Secret,
   utils::{build_db_pool, get_database_url, run_migrations},
@@ -24,6 +22,11 @@ use lemmy_db_schema::{
 use lemmy_routes::{feeds, images, nodeinfo, webfinger};
 use lemmy_server::{
   api_routes,
+  api_routes::{
+    match_websocket_operation,
+    match_websocket_operation_apub,
+    match_websocket_operation_crud,
+  },
   code_migrations::run_advanced_migrations,
   init_logging,
   root_span_builder::QuieterRootSpanBuilder,
@@ -136,6 +139,7 @@ async fn main() -> Result<(), LemmyError> {
     pool.clone(),
     |c, i, o, d| Box::pin(match_websocket_operation(c, i, o, d)),
     |c, i, o, d| Box::pin(match_websocket_operation_crud(c, i, o, d)),
+    |c, i, o, d| Box::pin(match_websocket_operation_apub(c, i, o, d)),
     client.clone(),
     settings.clone(),
     secret.clone(),
-- 
GitLab