From 27be1efb74d6761046999980561a343d06235674 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Wed, 2 Aug 2023 18:52:41 +0200 Subject: [PATCH] Rewrite remaining federation actions, get rid of PerformCrud trait (#3794) * Rewrite ban actions * Rewrite delete/remove actions * Rewrite remove/delete community * Rewrite report actions * Rewrite feature/lock post * Rewrite update community actions * Rewrite remaining federation actions * Get rid of PerformCrud trait * clippy --- api_tests/prepare-drone-federation-test.sh | 3 - api_tests/run-federation-test.sh | 2 + crates/api/src/comment_report/create.rs | 97 +++--- crates/api/src/community/add_mod.rs | 111 +++---- crates/api/src/community/ban.rs | 143 +++++---- crates/api/src/community/block.rs | 93 +++--- crates/api/src/community/hide.rs | 59 ++-- crates/api/src/community/mod.rs | 10 +- crates/api/src/local_user/ban_person.rs | 114 +++---- crates/api/src/post/feature.rs | 113 ++++--- crates/api/src/post/like.rs | 16 +- crates/api/src/post/lock.rs | 97 +++--- crates/api/src/post_report/create.rs | 92 +++--- crates/api/src/post_report/mod.rs | 6 +- crates/api_common/src/build_response.rs | 14 +- crates/api_common/src/send_activity.rs | 36 ++- crates/api_crud/src/community/create.rs | 204 ++++++------ crates/api_crud/src/community/delete.rs | 62 ++-- crates/api_crud/src/community/remove.rs | 79 ++--- crates/api_crud/src/community/update.rs | 118 +++---- crates/api_crud/src/custom_emoji/create.rs | 65 ++-- crates/api_crud/src/custom_emoji/delete.rs | 36 +-- crates/api_crud/src/custom_emoji/mod.rs | 6 +- crates/api_crud/src/custom_emoji/update.rs | 63 ++-- crates/api_crud/src/lib.rs | 11 - crates/api_crud/src/post/create.rs | 4 +- crates/api_crud/src/post/delete.rs | 93 +++--- crates/api_crud/src/post/remove.rs | 97 +++--- crates/api_crud/src/post/update.rs | 16 +- crates/api_crud/src/private_message/create.rs | 132 ++++---- crates/api_crud/src/private_message/delete.rs | 76 ++--- crates/api_crud/src/private_message/update.rs | 88 +++--- crates/api_crud/src/user/create.rs | 293 +++++++++--------- crates/api_crud/src/user/delete.rs | 46 +-- crates/api_crud/src/user/mod.rs | 4 +- crates/apub/src/activities/block/mod.rs | 147 ++++----- .../activities/community/collection_add.rs | 92 +++--- .../src/activities/community/lock_page.rs | 96 +++--- .../apub/src/activities/community/report.rs | 65 +--- .../apub/src/activities/community/update.rs | 85 ++--- .../create_or_update/private_message.rs | 100 ++---- .../src/activities/deletion/delete_user.rs | 66 ++-- crates/apub/src/activities/deletion/mod.rs | 170 +++------- .../apub/src/activities/following/follow.rs | 32 +- crates/apub/src/activities/mod.rs | 91 +++++- src/api_routes_http.rs | 183 ++++------- 46 files changed, 1699 insertions(+), 1927 deletions(-) diff --git a/api_tests/prepare-drone-federation-test.sh b/api_tests/prepare-drone-federation-test.sh index 3aae16bda..ef1133287 100755 --- a/api_tests/prepare-drone-federation-test.sh +++ b/api_tests/prepare-drone-federation-test.sh @@ -30,9 +30,6 @@ else done fi -echo "killall existing lemmy_server processes" -killall -s1 lemmy_server || true - echo "$PWD" echo "start alpha" diff --git a/api_tests/run-federation-test.sh b/api_tests/run-federation-test.sh index f611cce65..ff74744a1 100755 --- a/api_tests/run-federation-test.sh +++ b/api_tests/run-federation-test.sh @@ -7,12 +7,14 @@ pushd .. cargo build rm target/lemmy_server || true cp target/debug/lemmy_server target/lemmy_server +killall -s1 lemmy_server || true ./api_tests/prepare-drone-federation-test.sh popd yarn yarn api-test || true +killall -s1 lemmy_server || true for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do psql "$LEMMY_DATABASE_URL" -c "DROP DATABASE $INSTANCE" done diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs index 190e47a1e..2ea973d3c 100644 --- a/crates/api/src/comment_report/create.rs +++ b/crates/api/src/comment_report/create.rs @@ -1,8 +1,10 @@ -use crate::{check_report_reason, Perform}; -use actix_web::web::Data; +use crate::check_report_reason; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ comment::{CommentReportResponse, CreateCommentReport}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::{ check_community_ban, local_user_view_from_jwt, @@ -21,55 +23,60 @@ use lemmy_db_views::structs::{CommentReportView, CommentView}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; /// Creates a comment report and notifies the moderators of the community -#[async_trait::async_trait(?Send)] -impl Perform for CreateCommentReport { - type Response = CommentReportResponse; +#[tracing::instrument(skip(context))] +pub async fn create_comment_report( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + let local_site = LocalSite::read(&mut context.pool()).await?; - #[tracing::instrument(skip(context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &CreateCommentReport = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - let local_site = LocalSite::read(&mut context.pool()).await?; + let reason = sanitize_html(data.reason.trim()); + check_report_reason(&reason, &local_site)?; - let reason = sanitize_html(self.reason.trim()); - check_report_reason(&reason, &local_site)?; + let person_id = local_user_view.person.id; + let comment_id = data.comment_id; + let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?; - let person_id = local_user_view.person.id; - let comment_id = data.comment_id; - let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?; + check_community_ban(person_id, comment_view.community.id, &mut context.pool()).await?; - check_community_ban(person_id, comment_view.community.id, &mut context.pool()).await?; + let report_form = CommentReportForm { + creator_id: person_id, + comment_id, + original_comment_text: comment_view.comment.content, + reason, + }; - let report_form = CommentReportForm { - creator_id: person_id, - comment_id, - original_comment_text: comment_view.comment.content, - reason, - }; + let report = CommentReport::report(&mut context.pool(), &report_form) + .await + .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; - let report = CommentReport::report(&mut context.pool(), &report_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; + let comment_report_view = + CommentReportView::read(&mut context.pool(), report.id, person_id).await?; - let comment_report_view = - CommentReportView::read(&mut context.pool(), report.id, person_id).await?; - - // Email the admins - if local_site.reports_email_admins { - send_new_report_email_to_admins( - &comment_report_view.creator.name, - &comment_report_view.comment_creator.name, - &mut context.pool(), - context.settings(), - ) - .await?; - } - - Ok(CommentReportResponse { - comment_report_view, - }) + // Email the admins + if local_site.reports_email_admins { + send_new_report_email_to_admins( + &comment_report_view.creator.name, + &comment_report_view.comment_creator.name, + &mut context.pool(), + context.settings(), + ) + .await?; } + + ActivityChannel::submit_activity( + SendActivityData::CreateReport( + comment_view.comment.ap_id.inner().clone(), + local_user_view.person, + comment_view.community, + data.reason.clone(), + ), + &context, + ) + .await?; + + Ok(Json(CommentReportResponse { + comment_report_view, + })) } diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs index 086207775..2d7b88750 100644 --- a/crates/api/src/community/add_mod.rs +++ b/crates/api/src/community/add_mod.rs @@ -1,8 +1,9 @@ -use crate::Perform; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ community::{AddModToCommunity, AddModToCommunityResponse}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::{is_mod_or_admin, local_user_view_from_jwt}, }; use lemmy_db_schema::{ @@ -15,58 +16,62 @@ use lemmy_db_schema::{ use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; -#[async_trait::async_trait(?Send)] -impl Perform for AddModToCommunity { - type Response = AddModToCommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn add_mod_to_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &AddModToCommunity = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let community_id = data.community_id; - let community_id = data.community_id; - - // Verify that only mods or admins can add mod - is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?; - let community = Community::read(&mut context.pool(), community_id).await?; - if local_user_view.person.admin && !community.local { - return Err(LemmyErrorType::NotAModerator)?; - } - - // Update in local database - let community_moderator_form = CommunityModeratorForm { - community_id: data.community_id, - person_id: data.person_id, - }; - if data.added { - CommunityModerator::join(&mut context.pool(), &community_moderator_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; - } else { - CommunityModerator::leave(&mut context.pool(), &community_moderator_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; - } - - // Mod tables - let form = ModAddCommunityForm { - mod_person_id: local_user_view.person.id, - other_person_id: data.person_id, - community_id: data.community_id, - removed: Some(!data.added), - }; - - ModAddCommunity::create(&mut context.pool(), &form).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; - let moderators = - CommunityModeratorView::for_community(&mut context.pool(), community_id).await?; - - Ok(AddModToCommunityResponse { moderators }) + // Verify that only mods or admins can add mod + is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?; + let community = Community::read(&mut context.pool(), community_id).await?; + if local_user_view.person.admin && !community.local { + return Err(LemmyErrorType::NotAModerator)?; } + + // Update in local database + let community_moderator_form = CommunityModeratorForm { + community_id: data.community_id, + person_id: data.person_id, + }; + if data.added { + CommunityModerator::join(&mut context.pool(), &community_moderator_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; + } else { + CommunityModerator::leave(&mut context.pool(), &community_moderator_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; + } + + // Mod tables + let form = ModAddCommunityForm { + mod_person_id: local_user_view.person.id, + other_person_id: data.person_id, + community_id: data.community_id, + removed: Some(!data.added), + }; + + ModAddCommunity::create(&mut context.pool(), &form).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; + let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?; + + ActivityChannel::submit_activity( + SendActivityData::AddModToCommunity( + local_user_view.person, + data.community_id, + data.person_id, + data.added, + ), + &context, + ) + .await?; + + Ok(Json(AddModToCommunityResponse { moderators })) } diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index 95c2bbc04..d04a8a0a9 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -1,8 +1,9 @@ -use crate::Perform; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ community::{BanFromCommunity, BanFromCommunityResponse}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::{ is_mod_or_admin, local_user_view_from_jwt, @@ -28,77 +29,85 @@ use lemmy_utils::{ utils::{time::naive_from_unix, validation::is_valid_body_field}, }; -#[async_trait::async_trait(?Send)] -impl Perform for BanFromCommunity { - type Response = BanFromCommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn ban_from_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &BanFromCommunity = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let banned_person_id = data.person_id; + let remove_data = data.remove_data.unwrap_or(false); + let expires = data.expires.map(naive_from_unix); - let community_id = data.community_id; - let banned_person_id = data.person_id; - let remove_data = data.remove_data.unwrap_or(false); - let expires = data.expires.map(naive_from_unix); + // Verify that only mods or admins can ban + is_mod_or_admin( + &mut context.pool(), + local_user_view.person.id, + data.community_id, + ) + .await?; + is_valid_body_field(&data.reason, false)?; - // Verify that only mods or admins can ban - is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?; - is_valid_body_field(&data.reason, false)?; + let community_user_ban_form = CommunityPersonBanForm { + community_id: data.community_id, + person_id: data.person_id, + expires: Some(expires), + }; - let community_user_ban_form = CommunityPersonBanForm { + if data.ban { + CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?; + + // Also unsubscribe them from the community, if they are subscribed + let community_follower_form = CommunityFollowerForm { community_id: data.community_id, - person_id: data.person_id, - expires: Some(expires), + person_id: banned_person_id, + pending: false, }; - if data.ban { - CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?; - - // Also unsubscribe them from the community, if they are subscribed - let community_follower_form = CommunityFollowerForm { - community_id: data.community_id, - person_id: banned_person_id, - pending: false, - }; - - CommunityFollower::unfollow(&mut context.pool(), &community_follower_form) - .await - .ok(); - } else { - CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?; - } - - // Remove/Restore their data if that's desired - if remove_data { - remove_user_data_in_community(community_id, banned_person_id, &mut context.pool()).await?; - } - - // Mod tables - let form = ModBanFromCommunityForm { - mod_person_id: local_user_view.person.id, - other_person_id: data.person_id, - community_id: data.community_id, - reason: sanitize_html_opt(&data.reason), - banned: Some(data.ban), - expires, - }; - - ModBanFromCommunity::create(&mut context.pool(), &form).await?; - - let person_id = data.person_id; - let person_view = PersonView::read(&mut context.pool(), person_id).await?; - - Ok(BanFromCommunityResponse { - person_view, - banned: data.ban, - }) + CommunityFollower::unfollow(&mut context.pool(), &community_follower_form) + .await + .ok(); + } else { + CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?; } + + // Remove/Restore their data if that's desired + if remove_data { + remove_user_data_in_community(data.community_id, banned_person_id, &mut context.pool()).await?; + } + + // Mod tables + let form = ModBanFromCommunityForm { + mod_person_id: local_user_view.person.id, + other_person_id: data.person_id, + community_id: data.community_id, + reason: sanitize_html_opt(&data.reason), + banned: Some(data.ban), + expires, + }; + + ModBanFromCommunity::create(&mut context.pool(), &form).await?; + + let person_view = PersonView::read(&mut context.pool(), data.person_id).await?; + + ActivityChannel::submit_activity( + SendActivityData::BanFromCommunity( + local_user_view.person, + data.community_id, + person_view.person.clone(), + data.0.clone(), + ), + &context, + ) + .await?; + + Ok(Json(BanFromCommunityResponse { + person_view, + banned: data.ban, + })) } diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs index 66d6adac6..f807574ed 100644 --- a/crates/api/src/community/block.rs +++ b/crates/api/src/community/block.rs @@ -1,8 +1,9 @@ -use crate::Perform; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ community::{BlockCommunity, BlockCommunityResponse}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::local_user_view_from_jwt, }; use lemmy_db_schema::{ @@ -15,52 +16,56 @@ use lemmy_db_schema::{ use lemmy_db_views_actor::structs::CommunityView; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; -#[async_trait::async_trait(?Send)] -impl Perform for BlockCommunity { - type Response = BlockCommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn block_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &BlockCommunity = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let community_id = data.community_id; + let person_id = local_user_view.person.id; + let community_block_form = CommunityBlockForm { + person_id, + community_id, + }; - let community_id = data.community_id; - let person_id = local_user_view.person.id; - let community_block_form = CommunityBlockForm { + if data.block { + CommunityBlock::block(&mut context.pool(), &community_block_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?; + + // Also, unfollow the community, and send a federated unfollow + let community_follower_form = CommunityFollowerForm { + community_id: data.community_id, person_id, - community_id, + pending: false, }; - if data.block { - CommunityBlock::block(&mut context.pool(), &community_block_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?; - - // Also, unfollow the community, and send a federated unfollow - let community_follower_form = CommunityFollowerForm { - community_id: data.community_id, - person_id, - pending: false, - }; - - CommunityFollower::unfollow(&mut context.pool(), &community_follower_form) - .await - .ok(); - } else { - CommunityBlock::unblock(&mut context.pool(), &community_block_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?; - } - - let community_view = - CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?; - - Ok(BlockCommunityResponse { - blocked: data.block, - community_view, - }) + CommunityFollower::unfollow(&mut context.pool(), &community_follower_form) + .await + .ok(); + } else { + CommunityBlock::unblock(&mut context.pool(), &community_block_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?; } + + let community_view = + CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?; + + ActivityChannel::submit_activity( + SendActivityData::FollowCommunity( + community_view.community.clone(), + local_user_view.person.clone(), + false, + ), + &context, + ) + .await?; + + Ok(Json(BlockCommunityResponse { + blocked: data.block, + community_view, + })) } diff --git a/crates/api/src/community/hide.rs b/crates/api/src/community/hide.rs index 4c05a71cf..ce437ad99 100644 --- a/crates/api/src/community/hide.rs +++ b/crates/api/src/community/hide.rs @@ -1,9 +1,10 @@ -use crate::Perform; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_community_response, community::{CommunityResponse, HideCommunity}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::{is_admin, local_user_view_from_jwt, sanitize_html_opt}, }; use lemmy_db_schema::{ @@ -15,36 +16,38 @@ use lemmy_db_schema::{ }; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; -#[async_trait::async_trait(?Send)] -impl Perform for HideCommunity { - type Response = CommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn hide_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + // Verify its a admin (only admin can hide or unhide it) + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + is_admin(&local_user_view)?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &HideCommunity = self; + let community_form = CommunityUpdateForm::builder() + .hidden(Some(data.hidden)) + .build(); - // Verify its a admin (only admin can hide or unhide it) - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - is_admin(&local_user_view)?; + let mod_hide_community_form = ModHideCommunityForm { + community_id: data.community_id, + mod_person_id: local_user_view.person.id, + reason: sanitize_html_opt(&data.reason), + hidden: Some(data.hidden), + }; - let community_form = CommunityUpdateForm::builder() - .hidden(Some(data.hidden)) - .build(); + let community_id = data.community_id; + let community = Community::update(&mut context.pool(), community_id, &community_form) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunityHiddenStatus)?; - let mod_hide_community_form = ModHideCommunityForm { - community_id: data.community_id, - mod_person_id: local_user_view.person.id, - reason: sanitize_html_opt(&data.reason), - hidden: Some(data.hidden), - }; + ModHideCommunity::create(&mut context.pool(), &mod_hide_community_form).await?; - let community_id = data.community_id; - Community::update(&mut context.pool(), community_id, &community_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunityHiddenStatus)?; + ActivityChannel::submit_activity( + SendActivityData::UpdateCommunity(local_user_view.person.clone(), community), + &context, + ) + .await?; - ModHideCommunity::create(&mut context.pool(), &mod_hide_community_form).await?; - - build_community_response(context, local_user_view, community_id).await - } + build_community_response(&context, local_user_view, community_id).await } diff --git a/crates/api/src/community/mod.rs b/crates/api/src/community/mod.rs index fc3ef67c5..478192229 100644 --- a/crates/api/src/community/mod.rs +++ b/crates/api/src/community/mod.rs @@ -1,6 +1,6 @@ -mod add_mod; -mod ban; -mod block; +pub mod add_mod; +pub mod ban; +pub mod block; pub mod follow; -mod hide; -mod transfer; +pub mod hide; +pub mod transfer; diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs index 77e8e8056..6e99ef250 100644 --- a/crates/api/src/local_user/ban_person.rs +++ b/crates/api/src/local_user/ban_person.rs @@ -1,8 +1,9 @@ -use crate::Perform; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, person::{BanPerson, BanPersonResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::{is_admin, local_user_view_from_jwt, remove_user_data, sanitize_html_opt}, }; use lemmy_db_schema::{ @@ -17,65 +18,68 @@ use lemmy_utils::{ error::{LemmyError, LemmyErrorExt, LemmyErrorType}, utils::{time::naive_from_unix, validation::is_valid_body_field}, }; +#[tracing::instrument(skip(context))] +pub async fn ban_from_site( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; -#[async_trait::async_trait(?Send)] -impl Perform for BanPerson { - type Response = BanPersonResponse; + // Make sure user is an admin + is_admin(&local_user_view)?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &BanPerson = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + is_valid_body_field(&data.reason, false)?; - // Make sure user is an admin - is_admin(&local_user_view)?; + let expires = data.expires.map(naive_from_unix); - is_valid_body_field(&data.reason, false)?; + let person = Person::update( + &mut context.pool(), + data.person_id, + &PersonUpdateForm::builder() + .banned(Some(data.ban)) + .ban_expires(Some(expires)) + .build(), + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?; - let ban = data.ban; - let banned_person_id = data.person_id; - let expires = data.expires.map(naive_from_unix); - - let person = Person::update( + // Remove their data if that's desired + let remove_data = data.remove_data.unwrap_or(false); + if remove_data { + remove_user_data( + person.id, &mut context.pool(), - banned_person_id, - &PersonUpdateForm::builder() - .banned(Some(ban)) - .ban_expires(Some(expires)) - .build(), + context.settings(), + context.client(), ) - .await - .with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?; - - // Remove their data if that's desired - let remove_data = data.remove_data.unwrap_or(false); - if remove_data { - remove_user_data( - person.id, - &mut context.pool(), - context.settings(), - context.client(), - ) - .await?; - } - - // Mod tables - let form = ModBanForm { - mod_person_id: local_user_view.person.id, - other_person_id: data.person_id, - reason: sanitize_html_opt(&data.reason), - banned: Some(data.ban), - expires, - }; - - ModBan::create(&mut context.pool(), &form).await?; - - let person_id = data.person_id; - let person_view = PersonView::read(&mut context.pool(), person_id).await?; - - Ok(BanPersonResponse { - person_view, - banned: data.ban, - }) + .await?; } + + // Mod tables + let form = ModBanForm { + mod_person_id: local_user_view.person.id, + other_person_id: data.person_id, + reason: sanitize_html_opt(&data.reason), + banned: Some(data.ban), + expires, + }; + + ModBan::create(&mut context.pool(), &form).await?; + + let person_view = PersonView::read(&mut context.pool(), data.person_id).await?; + + ActivityChannel::submit_activity( + SendActivityData::BanFromSite( + local_user_view.person, + person_view.person.clone(), + data.0.clone(), + ), + &context, + ) + .await?; + + Ok(Json(BanPersonResponse { + person_view, + banned: data.ban, + })) } diff --git a/crates/api/src/post/feature.rs b/crates/api/src/post/feature.rs index c59eba313..5e18c2b64 100644 --- a/crates/api/src/post/feature.rs +++ b/crates/api/src/post/feature.rs @@ -1,9 +1,10 @@ -use crate::Perform; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_post_response, context::LemmyContext, post::{FeaturePost, PostResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::{ check_community_ban, check_community_deleted_or_removed, @@ -22,67 +23,65 @@ use lemmy_db_schema::{ }; use lemmy_utils::error::LemmyError; -#[async_trait::async_trait(?Send)] -impl Perform for FeaturePost { - type Response = PostResponse; +#[tracing::instrument(skip(context))] +pub async fn feature_post( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &FeaturePost = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let post_id = data.post_id; + let orig_post = Post::read(&mut context.pool(), post_id).await?; - let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + check_community_ban( + local_user_view.person.id, + orig_post.community_id, + &mut context.pool(), + ) + .await?; + check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; - check_community_ban( + if data.feature_type == PostFeatureType::Community { + // Verify that only the mods can feature in community + is_mod_or_admin( + &mut context.pool(), local_user_view.person.id, orig_post.community_id, - &mut context.pool(), ) .await?; - check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; - - if data.feature_type == PostFeatureType::Community { - // Verify that only the mods can feature in community - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, - orig_post.community_id, - ) - .await?; - } else { - is_admin(&local_user_view)?; - } - - // Update the post - let post_id = data.post_id; - let new_post: PostUpdateForm = if data.feature_type == PostFeatureType::Community { - PostUpdateForm::builder() - .featured_community(Some(data.featured)) - .build() - } else { - PostUpdateForm::builder() - .featured_local(Some(data.featured)) - .build() - }; - Post::update(&mut context.pool(), post_id, &new_post).await?; - - // Mod tables - let form = ModFeaturePostForm { - mod_person_id: local_user_view.person.id, - post_id: data.post_id, - featured: data.featured, - is_featured_community: data.feature_type == PostFeatureType::Community, - }; - - ModFeaturePost::create(&mut context.pool(), &form).await?; - - build_post_response( - context, - orig_post.community_id, - local_user_view.person.id, - post_id, - ) - .await + } else { + is_admin(&local_user_view)?; } + + // Update the post + let post_id = data.post_id; + let new_post: PostUpdateForm = if data.feature_type == PostFeatureType::Community { + PostUpdateForm::builder() + .featured_community(Some(data.featured)) + .build() + } else { + PostUpdateForm::builder() + .featured_local(Some(data.featured)) + .build() + }; + let post = Post::update(&mut context.pool(), post_id, &new_post).await?; + + // Mod tables + let form = ModFeaturePostForm { + mod_person_id: local_user_view.person.id, + post_id: data.post_id, + featured: data.featured, + is_featured_community: data.feature_type == PostFeatureType::Community, + }; + + ModFeaturePost::create(&mut context.pool(), &form).await?; + + let person_id = local_user_view.person.id; + ActivityChannel::submit_activity( + SendActivityData::FeaturePost(post, local_user_view.person, data.featured), + &context, + ) + .await?; + + build_post_response(&context, orig_post.community_id, person_id, post_id).await } diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs index 1ff119f03..b9b2bede0 100644 --- a/crates/api/src/post/like.rs +++ b/crates/api/src/post/like.rs @@ -80,13 +80,11 @@ pub async fn like_post( ) .await?; - Ok(Json( - build_post_response( - context.deref(), - post.community_id, - local_user_view.person.id, - post_id, - ) - .await?, - )) + build_post_response( + context.deref(), + post.community_id, + local_user_view.person.id, + post_id, + ) + .await } diff --git a/crates/api/src/post/lock.rs b/crates/api/src/post/lock.rs index 627e9d8eb..45ec3317f 100644 --- a/crates/api/src/post/lock.rs +++ b/crates/api/src/post/lock.rs @@ -1,9 +1,10 @@ -use crate::Perform; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_post_response, context::LemmyContext, post::{LockPost, PostResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::{ check_community_ban, check_community_deleted_or_removed, @@ -20,58 +21,56 @@ use lemmy_db_schema::{ }; use lemmy_utils::error::LemmyError; -#[async_trait::async_trait(?Send)] -impl Perform for LockPost { - type Response = PostResponse; +#[tracing::instrument(skip(context))] +pub async fn lock_post( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &LockPost = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let post_id = data.post_id; + let orig_post = Post::read(&mut context.pool(), post_id).await?; - let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + check_community_ban( + local_user_view.person.id, + orig_post.community_id, + &mut context.pool(), + ) + .await?; + check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; - check_community_ban( - local_user_view.person.id, - orig_post.community_id, - &mut context.pool(), - ) - .await?; - check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; + // Verify that only the mods can lock + is_mod_or_admin( + &mut context.pool(), + local_user_view.person.id, + orig_post.community_id, + ) + .await?; - // Verify that only the mods can lock - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, - orig_post.community_id, - ) - .await?; + // Update the post + let post_id = data.post_id; + let locked = data.locked; + let post = Post::update( + &mut context.pool(), + post_id, + &PostUpdateForm::builder().locked(Some(locked)).build(), + ) + .await?; - // Update the post - let post_id = data.post_id; - let locked = data.locked; - Post::update( - &mut context.pool(), - post_id, - &PostUpdateForm::builder().locked(Some(locked)).build(), - ) - .await?; + // Mod tables + let form = ModLockPostForm { + mod_person_id: local_user_view.person.id, + post_id: data.post_id, + locked: Some(locked), + }; + ModLockPost::create(&mut context.pool(), &form).await?; - // Mod tables - let form = ModLockPostForm { - mod_person_id: local_user_view.person.id, - post_id: data.post_id, - locked: Some(locked), - }; - ModLockPost::create(&mut context.pool(), &form).await?; + let person_id = local_user_view.person.id; + ActivityChannel::submit_activity( + SendActivityData::LockPost(post, local_user_view.person, data.locked), + &context, + ) + .await?; - build_post_response( - context, - orig_post.community_id, - local_user_view.person.id, - post_id, - ) - .await - } + build_post_response(&context, orig_post.community_id, person_id, post_id).await } diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs index a4081015c..68eb8f7bd 100644 --- a/crates/api/src/post_report/create.rs +++ b/crates/api/src/post_report/create.rs @@ -1,8 +1,10 @@ -use crate::{check_report_reason, Perform}; -use actix_web::web::Data; +use crate::check_report_reason; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, post::{CreatePostReport, PostReportResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::{ check_community_ban, local_user_view_from_jwt, @@ -21,51 +23,59 @@ use lemmy_db_views::structs::{PostReportView, PostView}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; /// Creates a post report and notifies the moderators of the community -#[async_trait::async_trait(?Send)] -impl Perform for CreatePostReport { - type Response = PostReportResponse; +#[tracing::instrument(skip(context))] +pub async fn create_post_report( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + let local_site = LocalSite::read(&mut context.pool()).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &CreatePostReport = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - let local_site = LocalSite::read(&mut context.pool()).await?; + let reason = sanitize_html(data.reason.trim()); + check_report_reason(&reason, &local_site)?; - let reason = sanitize_html(self.reason.trim()); - check_report_reason(&reason, &local_site)?; + let person_id = local_user_view.person.id; + let post_id = data.post_id; + let post_view = PostView::read(&mut context.pool(), post_id, None, None).await?; - let person_id = local_user_view.person.id; - let post_id = data.post_id; - let post_view = PostView::read(&mut context.pool(), post_id, None, None).await?; + check_community_ban(person_id, post_view.community.id, &mut context.pool()).await?; - check_community_ban(person_id, post_view.community.id, &mut context.pool()).await?; + let report_form = PostReportForm { + creator_id: person_id, + post_id, + original_post_name: post_view.post.name, + original_post_url: post_view.post.url, + original_post_body: post_view.post.body, + reason, + }; - let report_form = PostReportForm { - creator_id: person_id, - post_id, - original_post_name: post_view.post.name, - original_post_url: post_view.post.url, - original_post_body: post_view.post.body, - reason, - }; + let report = PostReport::report(&mut context.pool(), &report_form) + .await + .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; - let report = PostReport::report(&mut context.pool(), &report_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; + let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?; - let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?; - - // Email the admins - if local_site.reports_email_admins { - send_new_report_email_to_admins( - &post_report_view.creator.name, - &post_report_view.post_creator.name, - &mut context.pool(), - context.settings(), - ) - .await?; - } - - Ok(PostReportResponse { post_report_view }) + // Email the admins + if local_site.reports_email_admins { + send_new_report_email_to_admins( + &post_report_view.creator.name, + &post_report_view.post_creator.name, + &mut context.pool(), + context.settings(), + ) + .await?; } + + ActivityChannel::submit_activity( + SendActivityData::CreateReport( + post_view.post.ap_id.inner().clone(), + local_user_view.person, + post_view.community, + data.reason.clone(), + ), + &context, + ) + .await?; + + Ok(Json(PostReportResponse { post_report_view })) } diff --git a/crates/api/src/post_report/mod.rs b/crates/api/src/post_report/mod.rs index 375fde4c3..3bb1a9b46 100644 --- a/crates/api/src/post_report/mod.rs +++ b/crates/api/src/post_report/mod.rs @@ -1,3 +1,3 @@ -mod create; -mod list; -mod resolve; +pub mod create; +pub mod list; +pub mod resolve; diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index b8c02457d..8d3bcda1a 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -5,7 +5,7 @@ use crate::{ post::PostResponse, utils::{check_person_block, get_interface_language, is_mod_or_admin, send_email_to_user}, }; -use actix_web::web::Data; +use actix_web::web::Json; use lemmy_db_schema::{ newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId}, source::{ @@ -39,10 +39,10 @@ pub async fn build_comment_response( } pub async fn build_community_response( - context: &Data, + context: &LemmyContext, local_user_view: LocalUserView, community_id: CommunityId, -) -> Result { +) -> Result, LemmyError> { let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id) .await @@ -57,10 +57,10 @@ pub async fn build_community_response( .await?; let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?; - Ok(CommunityResponse { + Ok(Json(CommunityResponse { community_view, discussion_languages, - }) + })) } pub async fn build_post_response( @@ -68,7 +68,7 @@ pub async fn build_post_response( community_id: CommunityId, person_id: PersonId, post_id: PostId, -) -> Result { +) -> Result, LemmyError> { let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), person_id, community_id) .await .is_ok(); @@ -79,7 +79,7 @@ pub async fn build_post_response( Some(is_mod_or_admin), ) .await?; - Ok(PostResponse { post_view }) + Ok(Json(PostResponse { post_view })) } // TODO: this function is a mess and should be split up to handle email seperately diff --git a/crates/api_common/src/send_activity.rs b/crates/api_common/src/send_activity.rs index 8580c2175..1ba9aa646 100644 --- a/crates/api_common/src/send_activity.rs +++ b/crates/api_common/src/send_activity.rs @@ -1,10 +1,22 @@ -use crate::context::LemmyContext; +use crate::{ + community::BanFromCommunity, + context::LemmyContext, + person::BanPerson, + post::{DeletePost, RemovePost}, +}; use activitypub_federation::config::Data; use futures::future::BoxFuture; use lemmy_db_schema::{ - newtypes::DbUrl, - source::{comment::Comment, community::Community, person::Person, post::Post}, + newtypes::{CommunityId, DbUrl, PersonId}, + source::{ + comment::Comment, + community::Community, + person::Person, + post::Post, + private_message::PrivateMessage, + }, }; +use lemmy_db_views::structs::PrivateMessageView; use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION}; use once_cell::sync::{Lazy, OnceCell}; use tokio::{ @@ -15,6 +27,7 @@ use tokio::{ }, task::JoinHandle, }; +use url::Url; type MatchOutgoingActivitiesBoxed = Box fn(SendActivityData, &'a Data) -> BoxFuture<'a, LemmyResult<()>>>; @@ -26,12 +39,27 @@ pub static MATCH_OUTGOING_ACTIVITIES: OnceCell = O pub enum SendActivityData { CreatePost(Post), UpdatePost(Post), + DeletePost(Post, Person, DeletePost), + RemovePost(Post, Person, RemovePost), + LockPost(Post, Person, bool), + FeaturePost(Post, Person, bool), CreateComment(Comment), + UpdateComment(Comment), DeleteComment(Comment, Person, Community), RemoveComment(Comment, Person, Community, Option), - UpdateComment(Comment), LikePostOrComment(DbUrl, Person, Community, i16), FollowCommunity(Community, Person, bool), + UpdateCommunity(Person, Community), + DeleteCommunity(Person, Community, bool), + RemoveCommunity(Person, Community, Option, bool), + AddModToCommunity(Person, CommunityId, PersonId, bool), + BanFromCommunity(Person, CommunityId, Person, BanFromCommunity), + BanFromSite(Person, Person, BanPerson), + CreatePrivateMessage(PrivateMessageView), + UpdatePrivateMessage(PrivateMessageView), + DeletePrivateMessage(Person, PrivateMessage, bool), + DeleteUser(Person), + CreateReport(Url, Person, Community, String), } // TODO: instead of static, move this into LemmyContext. make sure that stopping the process with diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 7c84a2150..7bfeabd6d 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -1,6 +1,5 @@ -use crate::PerformCrud; -use activitypub_federation::http_signatures::generate_actor_keypair; -use actix_web::web::Data; +use activitypub_federation::{config::Data, http_signatures::generate_actor_keypair}; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_community_response, community::{CommunityResponse, CreateCommunity}, @@ -42,107 +41,104 @@ use lemmy_utils::{ }, }; -#[async_trait::async_trait(?Send)] -impl PerformCrud for CreateCommunity { - type Response = CommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn create_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + let site_view = SiteView::read_local(&mut context.pool()).await?; + let local_site = site_view.local_site; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &CreateCommunity = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - let site_view = SiteView::read_local(&mut context.pool()).await?; - let local_site = site_view.local_site; - - if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() { - return Err(LemmyErrorType::OnlyAdminsCanCreateCommunities)?; - } - - // Check to make sure the icon and banners are urls - let icon = diesel_option_overwrite_to_url_create(&data.icon)?; - let banner = diesel_option_overwrite_to_url_create(&data.banner)?; - - let name = sanitize_html(&data.name); - let title = sanitize_html(&data.title); - let description = sanitize_html_opt(&data.description); - - let slur_regex = local_site_to_slur_regex(&local_site); - check_slurs(&name, &slur_regex)?; - check_slurs(&title, &slur_regex)?; - check_slurs_opt(&description, &slur_regex)?; - - is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?; - is_valid_body_field(&data.description, false)?; - - // Double check for duplicate community actor_ids - let community_actor_id = generate_local_apub_endpoint( - EndpointType::Community, - &data.name, - &context.settings().get_protocol_and_hostname(), - )?; - let community_dupe = - Community::read_from_apub_id(&mut context.pool(), &community_actor_id).await?; - if community_dupe.is_some() { - return Err(LemmyErrorType::CommunityAlreadyExists)?; - } - - // When you create a community, make sure the user becomes a moderator and a follower - let keypair = generate_actor_keypair()?; - - let community_form = CommunityInsertForm::builder() - .name(name) - .title(title) - .description(description) - .icon(icon) - .banner(banner) - .nsfw(data.nsfw) - .actor_id(Some(community_actor_id.clone())) - .private_key(Some(keypair.private_key)) - .public_key(keypair.public_key) - .followers_url(Some(generate_followers_url(&community_actor_id)?)) - .inbox_url(Some(generate_inbox_url(&community_actor_id)?)) - .shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?)) - .posting_restricted_to_mods(data.posting_restricted_to_mods) - .instance_id(site_view.site.instance_id) - .build(); - - let inserted_community = Community::create(&mut context.pool(), &community_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityAlreadyExists)?; - - // The community creator becomes a moderator - let community_moderator_form = CommunityModeratorForm { - community_id: inserted_community.id, - person_id: local_user_view.person.id, - }; - - CommunityModerator::join(&mut context.pool(), &community_moderator_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; - - // Follow your own community - let community_follower_form = CommunityFollowerForm { - community_id: inserted_community.id, - person_id: local_user_view.person.id, - pending: false, - }; - - CommunityFollower::follow(&mut context.pool(), &community_follower_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?; - - // Update the discussion_languages if that's provided - let community_id = inserted_community.id; - if let Some(languages) = data.discussion_languages.clone() { - let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; - // check that community languages are a subset of site languages - // https://stackoverflow.com/a/64227550 - let is_subset = languages.iter().all(|item| site_languages.contains(item)); - if !is_subset { - return Err(LemmyErrorType::LanguageNotAllowed)?; - } - CommunityLanguage::update(&mut context.pool(), languages, community_id).await?; - } - - build_community_response(context, local_user_view, community_id).await + if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() { + return Err(LemmyErrorType::OnlyAdminsCanCreateCommunities)?; } + + // Check to make sure the icon and banners are urls + let icon = diesel_option_overwrite_to_url_create(&data.icon)?; + let banner = diesel_option_overwrite_to_url_create(&data.banner)?; + + let name = sanitize_html(&data.name); + let title = sanitize_html(&data.title); + let description = sanitize_html_opt(&data.description); + + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs(&name, &slur_regex)?; + check_slurs(&title, &slur_regex)?; + check_slurs_opt(&description, &slur_regex)?; + + is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?; + is_valid_body_field(&data.description, false)?; + + // Double check for duplicate community actor_ids + let community_actor_id = generate_local_apub_endpoint( + EndpointType::Community, + &data.name, + &context.settings().get_protocol_and_hostname(), + )?; + let community_dupe = + Community::read_from_apub_id(&mut context.pool(), &community_actor_id).await?; + if community_dupe.is_some() { + return Err(LemmyErrorType::CommunityAlreadyExists)?; + } + + // When you create a community, make sure the user becomes a moderator and a follower + let keypair = generate_actor_keypair()?; + + let community_form = CommunityInsertForm::builder() + .name(name) + .title(title) + .description(description) + .icon(icon) + .banner(banner) + .nsfw(data.nsfw) + .actor_id(Some(community_actor_id.clone())) + .private_key(Some(keypair.private_key)) + .public_key(keypair.public_key) + .followers_url(Some(generate_followers_url(&community_actor_id)?)) + .inbox_url(Some(generate_inbox_url(&community_actor_id)?)) + .shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?)) + .posting_restricted_to_mods(data.posting_restricted_to_mods) + .instance_id(site_view.site.instance_id) + .build(); + + let inserted_community = Community::create(&mut context.pool(), &community_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityAlreadyExists)?; + + // The community creator becomes a moderator + let community_moderator_form = CommunityModeratorForm { + community_id: inserted_community.id, + person_id: local_user_view.person.id, + }; + + CommunityModerator::join(&mut context.pool(), &community_moderator_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; + + // Follow your own community + let community_follower_form = CommunityFollowerForm { + community_id: inserted_community.id, + person_id: local_user_view.person.id, + pending: false, + }; + + CommunityFollower::follow(&mut context.pool(), &community_follower_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?; + + // Update the discussion_languages if that's provided + let community_id = inserted_community.id; + if let Some(languages) = data.discussion_languages.clone() { + let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; + // check that community languages are a subset of site languages + // https://stackoverflow.com/a/64227550 + let is_subset = languages.iter().all(|item| site_languages.contains(item)); + if !is_subset { + return Err(LemmyErrorType::LanguageNotAllowed)?; + } + CommunityLanguage::update(&mut context.pool(), languages, community_id).await?; + } + + build_community_response(&context, local_user_view, community_id).await } diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index d3e58d56c..e23b1f404 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -1,9 +1,10 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_community_response, community::{CommunityResponse, DeleteCommunity}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::{is_top_mod, local_user_view_from_jwt}, }; use lemmy_db_schema::{ @@ -13,36 +14,39 @@ use lemmy_db_schema::{ use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; -#[async_trait::async_trait(?Send)] -impl PerformCrud for DeleteCommunity { - type Response = CommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn delete_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &DeleteCommunity = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + // Fetch the community mods + let community_id = data.community_id; + let community_mods = + CommunityModeratorView::for_community(&mut context.pool(), community_id).await?; - // Fetch the community mods - let community_id = data.community_id; - let community_mods = - CommunityModeratorView::for_community(&mut context.pool(), community_id).await?; + // Make sure deleter is the top mod + is_top_mod(&local_user_view, &community_mods)?; - // Make sure deleter is the top mod - is_top_mod(&local_user_view, &community_mods)?; + // Do the delete + let community_id = data.community_id; + let deleted = data.deleted; + let community = Community::update( + &mut context.pool(), + community_id, + &CommunityUpdateForm::builder() + .deleted(Some(deleted)) + .build(), + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; - // Do the delete - let community_id = data.community_id; - let deleted = data.deleted; - Community::update( - &mut context.pool(), - community_id, - &CommunityUpdateForm::builder() - .deleted(Some(deleted)) - .build(), - ) - .await - .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; + ActivityChannel::submit_activity( + SendActivityData::DeleteCommunity(local_user_view.person.clone(), community, data.deleted), + &context, + ) + .await?; - build_community_response(context, local_user_view, community_id).await - } + build_community_response(&context, local_user_view, community_id).await } diff --git a/crates/api_crud/src/community/remove.rs b/crates/api_crud/src/community/remove.rs index 2bcd3d857..b79b2a666 100644 --- a/crates/api_crud/src/community/remove.rs +++ b/crates/api_crud/src/community/remove.rs @@ -1,9 +1,10 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_community_response, community::{CommunityResponse, RemoveCommunity}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::{is_admin, local_user_view_from_jwt}, }; use lemmy_db_schema::{ @@ -18,42 +19,50 @@ use lemmy_utils::{ utils::time::naive_from_unix, }; -#[async_trait::async_trait(?Send)] -impl PerformCrud for RemoveCommunity { - type Response = CommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn remove_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &RemoveCommunity = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + // Verify its an admin (only an admin can remove a community) + is_admin(&local_user_view)?; - // Verify its an admin (only an admin can remove a community) - is_admin(&local_user_view)?; + // Do the remove + let community_id = data.community_id; + let removed = data.removed; + let community = Community::update( + &mut context.pool(), + community_id, + &CommunityUpdateForm::builder() + .removed(Some(removed)) + .build(), + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; - // Do the remove - let community_id = data.community_id; - let removed = data.removed; - Community::update( - &mut context.pool(), - community_id, - &CommunityUpdateForm::builder() - .removed(Some(removed)) - .build(), - ) - .await - .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; + // Mod tables + let expires = data.expires.map(naive_from_unix); + let form = ModRemoveCommunityForm { + mod_person_id: local_user_view.person.id, + community_id: data.community_id, + removed: Some(removed), + reason: data.reason.clone(), + expires, + }; + ModRemoveCommunity::create(&mut context.pool(), &form).await?; - // Mod tables - let expires = data.expires.map(naive_from_unix); - let form = ModRemoveCommunityForm { - mod_person_id: local_user_view.person.id, - community_id: data.community_id, - removed: Some(removed), - reason: data.reason.clone(), - expires, - }; - ModRemoveCommunity::create(&mut context.pool(), &form).await?; + ActivityChannel::submit_activity( + SendActivityData::RemoveCommunity( + local_user_view.person.clone(), + community, + data.reason.clone(), + data.removed, + ), + &context, + ) + .await?; - build_community_response(context, local_user_view, community_id).await - } + build_community_response(&context, local_user_view, community_id).await } diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 128be036f..baa4e1bdb 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -1,9 +1,10 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_community_response, community::{CommunityResponse, EditCommunity}, context::LemmyContext, + send_activity::{ActivityChannel, SendActivityData}, utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html_opt}, }; use lemmy_db_schema::{ @@ -22,65 +23,68 @@ use lemmy_utils::{ utils::{slurs::check_slurs_opt, validation::is_valid_body_field}, }; -#[async_trait::async_trait(?Send)] -impl PerformCrud for EditCommunity { - type Response = CommunityResponse; +#[tracing::instrument(skip(context))] +pub async fn update_community( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + let local_site = LocalSite::read(&mut context.pool()).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &EditCommunity = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - let local_site = LocalSite::read(&mut context.pool()).await?; + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs_opt(&data.title, &slur_regex)?; + check_slurs_opt(&data.description, &slur_regex)?; + is_valid_body_field(&data.description, false)?; - let slur_regex = local_site_to_slur_regex(&local_site); - check_slurs_opt(&data.title, &slur_regex)?; - check_slurs_opt(&data.description, &slur_regex)?; - is_valid_body_field(&data.description, false)?; + let title = sanitize_html_opt(&data.title); + let description = sanitize_html_opt(&data.description); - let title = sanitize_html_opt(&data.title); - let description = sanitize_html_opt(&data.description); + let icon = diesel_option_overwrite_to_url(&data.icon)?; + let banner = diesel_option_overwrite_to_url(&data.banner)?; + let description = diesel_option_overwrite(description); - let icon = diesel_option_overwrite_to_url(&data.icon)?; - let banner = diesel_option_overwrite_to_url(&data.banner)?; - let description = diesel_option_overwrite(description); - - // Verify its a mod (only mods can edit it) - let community_id = data.community_id; - let mods: Vec = - CommunityModeratorView::for_community(&mut context.pool(), community_id) - .await - .map(|v| v.into_iter().map(|m| m.moderator.id).collect())?; - if !mods.contains(&local_user_view.person.id) { - return Err(LemmyErrorType::NotAModerator)?; - } - - let community_id = data.community_id; - if let Some(languages) = data.discussion_languages.clone() { - let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; - // check that community languages are a subset of site languages - // https://stackoverflow.com/a/64227550 - let is_subset = languages.iter().all(|item| site_languages.contains(item)); - if !is_subset { - return Err(LemmyErrorType::LanguageNotAllowed)?; - } - CommunityLanguage::update(&mut context.pool(), languages, community_id).await?; - } - - let community_form = CommunityUpdateForm::builder() - .title(title) - .description(description) - .icon(icon) - .banner(banner) - .nsfw(data.nsfw) - .posting_restricted_to_mods(data.posting_restricted_to_mods) - .updated(Some(Some(naive_now()))) - .build(); - - let community_id = data.community_id; - Community::update(&mut context.pool(), community_id, &community_form) + // Verify its a mod (only mods can edit it) + let community_id = data.community_id; + let mods: Vec = + CommunityModeratorView::for_community(&mut context.pool(), community_id) .await - .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; - - build_community_response(context, local_user_view, community_id).await + .map(|v| v.into_iter().map(|m| m.moderator.id).collect())?; + if !mods.contains(&local_user_view.person.id) { + return Err(LemmyErrorType::NotAModerator)?; } + + let community_id = data.community_id; + if let Some(languages) = data.discussion_languages.clone() { + let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; + // check that community languages are a subset of site languages + // https://stackoverflow.com/a/64227550 + let is_subset = languages.iter().all(|item| site_languages.contains(item)); + if !is_subset { + return Err(LemmyErrorType::LanguageNotAllowed)?; + } + CommunityLanguage::update(&mut context.pool(), languages, community_id).await?; + } + + let community_form = CommunityUpdateForm::builder() + .title(title) + .description(description) + .icon(icon) + .banner(banner) + .nsfw(data.nsfw) + .posting_restricted_to_mods(data.posting_restricted_to_mods) + .updated(Some(Some(naive_now()))) + .build(); + + let community_id = data.community_id; + let community = Community::update(&mut context.pool(), community_id, &community_form) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; + + ActivityChannel::submit_activity( + SendActivityData::UpdateCommunity(local_user_view.person.clone(), community), + &context, + ) + .await?; + + build_community_response(&context, local_user_view, community_id).await } diff --git a/crates/api_crud/src/custom_emoji/create.rs b/crates/api_crud/src/custom_emoji/create.rs index 93e7114ae..589174455 100644 --- a/crates/api_crud/src/custom_emoji/create.rs +++ b/crates/api_crud/src/custom_emoji/create.rs @@ -1,5 +1,5 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, custom_emoji::{CreateCustomEmoji, CustomEmojiResponse}, @@ -13,41 +13,38 @@ use lemmy_db_schema::source::{ use lemmy_db_views::structs::CustomEmojiView; use lemmy_utils::error::LemmyError; -#[async_trait::async_trait(?Send)] -impl PerformCrud for CreateCustomEmoji { - type Response = CustomEmojiResponse; +#[tracing::instrument(skip(context))] +pub async fn create_custom_emoji( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(self, context))] - async fn perform(&self, context: &Data) -> Result { - let data: &CreateCustomEmoji = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let local_site = LocalSite::read(&mut context.pool()).await?; + // Make sure user is an admin + is_admin(&local_user_view)?; - let local_site = LocalSite::read(&mut context.pool()).await?; - // Make sure user is an admin - is_admin(&local_user_view)?; + let shortcode = sanitize_html(data.shortcode.to_lowercase().trim()); + let alt_text = sanitize_html(&data.alt_text); + let category = sanitize_html(&data.category); - let shortcode = sanitize_html(data.shortcode.to_lowercase().trim()); - let alt_text = sanitize_html(&data.alt_text); - let category = sanitize_html(&data.category); - - let emoji_form = CustomEmojiInsertForm::builder() - .local_site_id(local_site.id) - .shortcode(shortcode) - .alt_text(alt_text) - .category(category) - .image_url(data.clone().image_url.into()) + let emoji_form = CustomEmojiInsertForm::builder() + .local_site_id(local_site.id) + .shortcode(shortcode) + .alt_text(alt_text) + .category(category) + .image_url(data.clone().image_url.into()) + .build(); + let emoji = CustomEmoji::create(&mut context.pool(), &emoji_form).await?; + let mut keywords = vec![]; + for keyword in &data.keywords { + let keyword_form = CustomEmojiKeywordInsertForm::builder() + .custom_emoji_id(emoji.id) + .keyword(keyword.to_lowercase().trim().to_string()) .build(); - let emoji = CustomEmoji::create(&mut context.pool(), &emoji_form).await?; - let mut keywords = vec![]; - for keyword in &data.keywords { - let keyword_form = CustomEmojiKeywordInsertForm::builder() - .custom_emoji_id(emoji.id) - .keyword(keyword.to_lowercase().trim().to_string()) - .build(); - keywords.push(keyword_form); - } - CustomEmojiKeyword::create(&mut context.pool(), keywords).await?; - let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?; - Ok(CustomEmojiResponse { custom_emoji: view }) + keywords.push(keyword_form); } + CustomEmojiKeyword::create(&mut context.pool(), keywords).await?; + let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?; + Ok(Json(CustomEmojiResponse { custom_emoji: view })) } diff --git a/crates/api_crud/src/custom_emoji/delete.rs b/crates/api_crud/src/custom_emoji/delete.rs index 069129239..be88d310f 100644 --- a/crates/api_crud/src/custom_emoji/delete.rs +++ b/crates/api_crud/src/custom_emoji/delete.rs @@ -1,5 +1,5 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, custom_emoji::{DeleteCustomEmoji, DeleteCustomEmojiResponse}, @@ -8,24 +8,18 @@ use lemmy_api_common::{ use lemmy_db_schema::source::custom_emoji::CustomEmoji; use lemmy_utils::error::LemmyError; -#[async_trait::async_trait(?Send)] -impl PerformCrud for DeleteCustomEmoji { - type Response = DeleteCustomEmojiResponse; +#[tracing::instrument(skip(context))] +pub async fn delete_custom_emoji( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(self, context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &DeleteCustomEmoji = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - - // Make sure user is an admin - is_admin(&local_user_view)?; - CustomEmoji::delete(&mut context.pool(), data.id).await?; - Ok(DeleteCustomEmojiResponse { - id: data.id, - success: true, - }) - } + // Make sure user is an admin + is_admin(&local_user_view)?; + CustomEmoji::delete(&mut context.pool(), data.id).await?; + Ok(Json(DeleteCustomEmojiResponse { + id: data.id, + success: true, + })) } diff --git a/crates/api_crud/src/custom_emoji/mod.rs b/crates/api_crud/src/custom_emoji/mod.rs index b9d8b5577..fdb2f5561 100644 --- a/crates/api_crud/src/custom_emoji/mod.rs +++ b/crates/api_crud/src/custom_emoji/mod.rs @@ -1,3 +1,3 @@ -mod create; -mod delete; -mod update; +pub mod create; +pub mod delete; +pub mod update; diff --git a/crates/api_crud/src/custom_emoji/update.rs b/crates/api_crud/src/custom_emoji/update.rs index 93708c379..7a2056895 100644 --- a/crates/api_crud/src/custom_emoji/update.rs +++ b/crates/api_crud/src/custom_emoji/update.rs @@ -1,5 +1,5 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, custom_emoji::{CustomEmojiResponse, EditCustomEmoji}, @@ -13,40 +13,37 @@ use lemmy_db_schema::source::{ use lemmy_db_views::structs::CustomEmojiView; use lemmy_utils::error::LemmyError; -#[async_trait::async_trait(?Send)] -impl PerformCrud for EditCustomEmoji { - type Response = CustomEmojiResponse; +#[tracing::instrument(skip(context))] +pub async fn update_custom_emoji( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(self, context))] - async fn perform(&self, context: &Data) -> Result { - let data: &EditCustomEmoji = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let local_site = LocalSite::read(&mut context.pool()).await?; + // Make sure user is an admin + is_admin(&local_user_view)?; - let local_site = LocalSite::read(&mut context.pool()).await?; - // Make sure user is an admin - is_admin(&local_user_view)?; + let alt_text = sanitize_html(&data.alt_text); + let category = sanitize_html(&data.category); - let alt_text = sanitize_html(&data.alt_text); - let category = sanitize_html(&data.category); - - let emoji_form = CustomEmojiUpdateForm::builder() - .local_site_id(local_site.id) - .alt_text(alt_text) - .category(category) - .image_url(data.clone().image_url.into()) + let emoji_form = CustomEmojiUpdateForm::builder() + .local_site_id(local_site.id) + .alt_text(alt_text) + .category(category) + .image_url(data.clone().image_url.into()) + .build(); + let emoji = CustomEmoji::update(&mut context.pool(), data.id, &emoji_form).await?; + CustomEmojiKeyword::delete(&mut context.pool(), data.id).await?; + let mut keywords = vec![]; + for keyword in &data.keywords { + let keyword_form = CustomEmojiKeywordInsertForm::builder() + .custom_emoji_id(emoji.id) + .keyword(keyword.to_lowercase().trim().to_string()) .build(); - let emoji = CustomEmoji::update(&mut context.pool(), data.id, &emoji_form).await?; - CustomEmojiKeyword::delete(&mut context.pool(), data.id).await?; - let mut keywords = vec![]; - for keyword in &data.keywords { - let keyword_form = CustomEmojiKeywordInsertForm::builder() - .custom_emoji_id(emoji.id) - .keyword(keyword.to_lowercase().trim().to_string()) - .build(); - keywords.push(keyword_form); - } - CustomEmojiKeyword::create(&mut context.pool(), keywords).await?; - let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?; - Ok(CustomEmojiResponse { custom_emoji: view }) + keywords.push(keyword_form); } + CustomEmojiKeyword::create(&mut context.pool(), keywords).await?; + let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?; + Ok(Json(CustomEmojiResponse { custom_emoji: view })) } diff --git a/crates/api_crud/src/lib.rs b/crates/api_crud/src/lib.rs index edd5c46f2..aee3e8134 100644 --- a/crates/api_crud/src/lib.rs +++ b/crates/api_crud/src/lib.rs @@ -1,7 +1,3 @@ -use actix_web::web::Data; -use lemmy_api_common::context::LemmyContext; -use lemmy_utils::error::LemmyError; - pub mod comment; pub mod community; pub mod custom_emoji; @@ -9,10 +5,3 @@ pub mod post; pub mod private_message; pub mod site; pub mod user; - -#[async_trait::async_trait(?Send)] -pub trait PerformCrud { - type Response: serde::ser::Serialize + Send + Clone + Sync; - - async fn perform(&self, context: &Data) -> Result; -} diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 264cdbc82..dd88204a9 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -194,7 +194,5 @@ pub async fn create_post( } }; - Ok(Json( - build_post_response(&context, community_id, person_id, post_id).await?, - )) + build_post_response(&context, community_id, person_id, post_id).await } diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index eaeb66c43..0d5eea886 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -1,9 +1,10 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_post_response, context::LemmyContext, post::{DeletePost, PostResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt}, }; use lemmy_db_schema::{ @@ -12,52 +13,50 @@ use lemmy_db_schema::{ }; use lemmy_utils::error::{LemmyError, LemmyErrorType}; -#[async_trait::async_trait(?Send)] -impl PerformCrud for DeletePost { - type Response = PostResponse; +#[tracing::instrument(skip(context))] +pub async fn delete_post( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &DeletePost = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let post_id = data.post_id; + let orig_post = Post::read(&mut context.pool(), post_id).await?; - let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; - - // Dont delete it if its already been deleted. - if orig_post.deleted == data.deleted { - return Err(LemmyErrorType::CouldntUpdatePost)?; - } - - check_community_ban( - local_user_view.person.id, - orig_post.community_id, - &mut context.pool(), - ) - .await?; - check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; - - // Verify that only the creator can delete - if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) { - return Err(LemmyErrorType::NoPostEditAllowed)?; - } - - // Update the post - let post_id = data.post_id; - let deleted = data.deleted; - Post::update( - &mut context.pool(), - post_id, - &PostUpdateForm::builder().deleted(Some(deleted)).build(), - ) - .await?; - - build_post_response( - context, - orig_post.community_id, - local_user_view.person.id, - post_id, - ) - .await + // Dont delete it if its already been deleted. + if orig_post.deleted == data.deleted { + return Err(LemmyErrorType::CouldntUpdatePost)?; } + + check_community_ban( + local_user_view.person.id, + orig_post.community_id, + &mut context.pool(), + ) + .await?; + check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?; + + // Verify that only the creator can delete + if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) { + return Err(LemmyErrorType::NoPostEditAllowed)?; + } + + // Update the post + let post = Post::update( + &mut context.pool(), + data.post_id, + &PostUpdateForm::builder() + .deleted(Some(data.deleted)) + .build(), + ) + .await?; + + let person_id = local_user_view.person.id; + ActivityChannel::submit_activity( + SendActivityData::DeletePost(post, local_user_view.person, data.0.clone()), + &context, + ) + .await?; + + build_post_response(&context, orig_post.community_id, person_id, data.post_id).await } diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs index 7950d5047..4a87eb784 100644 --- a/crates/api_crud/src/post/remove.rs +++ b/crates/api_crud/src/post/remove.rs @@ -1,9 +1,10 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ build_response::build_post_response, context::LemmyContext, post::{PostResponse, RemovePost}, + send_activity::{ActivityChannel, SendActivityData}, utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt}, }; use lemmy_db_schema::{ @@ -15,58 +16,56 @@ use lemmy_db_schema::{ }; use lemmy_utils::error::LemmyError; -#[async_trait::async_trait(?Send)] -impl PerformCrud for RemovePost { - type Response = PostResponse; +#[tracing::instrument(skip(context))] +pub async fn remove_post( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(context))] - async fn perform(&self, context: &Data) -> Result { - let data: &RemovePost = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let post_id = data.post_id; + let orig_post = Post::read(&mut context.pool(), post_id).await?; - let post_id = data.post_id; - let orig_post = Post::read(&mut context.pool(), post_id).await?; + check_community_ban( + local_user_view.person.id, + orig_post.community_id, + &mut context.pool(), + ) + .await?; - check_community_ban( - local_user_view.person.id, - orig_post.community_id, - &mut context.pool(), - ) - .await?; + // Verify that only the mods can remove + is_mod_or_admin( + &mut context.pool(), + local_user_view.person.id, + orig_post.community_id, + ) + .await?; - // Verify that only the mods can remove - is_mod_or_admin( - &mut context.pool(), - local_user_view.person.id, - orig_post.community_id, - ) - .await?; + // Update the post + let post_id = data.post_id; + let removed = data.removed; + let post = Post::update( + &mut context.pool(), + post_id, + &PostUpdateForm::builder().removed(Some(removed)).build(), + ) + .await?; - // Update the post - let post_id = data.post_id; - let removed = data.removed; - Post::update( - &mut context.pool(), - post_id, - &PostUpdateForm::builder().removed(Some(removed)).build(), - ) - .await?; + // Mod tables + let form = ModRemovePostForm { + mod_person_id: local_user_view.person.id, + post_id: data.post_id, + removed: Some(removed), + reason: data.reason.clone(), + }; + ModRemovePost::create(&mut context.pool(), &form).await?; - // Mod tables - let form = ModRemovePostForm { - mod_person_id: local_user_view.person.id, - post_id: data.post_id, - removed: Some(removed), - reason: data.reason.clone(), - }; - ModRemovePost::create(&mut context.pool(), &form).await?; + let person_id = local_user_view.person.id; + ActivityChannel::submit_activity( + SendActivityData::RemovePost(post, local_user_view.person, data.0), + &context, + ) + .await?; - build_post_response( - context, - orig_post.community_id, - local_user_view.person.id, - post_id, - ) - .await - } + build_post_response(&context, orig_post.community_id, person_id, post_id).await } diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index 3341392bc..c831c88f1 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -113,13 +113,11 @@ pub async fn update_post( ActivityChannel::submit_activity(SendActivityData::UpdatePost(updated_post), &context).await?; - Ok(Json( - build_post_response( - context.deref(), - orig_post.community_id, - local_user_view.person.id, - post_id, - ) - .await?, - )) + build_post_response( + context.deref(), + orig_post.community_id, + local_user_view.person.id, + post_id, + ) + .await } diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index 3b1a625f6..b9ac916f3 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -1,8 +1,9 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, private_message::{CreatePrivateMessage, PrivateMessageResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::{ check_person_block, generate_local_apub_endpoint, @@ -27,78 +28,77 @@ use lemmy_utils::{ utils::{slurs::remove_slurs, validation::is_valid_body_field}, }; -#[async_trait::async_trait(?Send)] -impl PerformCrud for CreatePrivateMessage { - type Response = PrivateMessageResponse; +#[tracing::instrument(skip(context))] +pub async fn create_private_message( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + let local_site = LocalSite::read(&mut context.pool()).await?; - #[tracing::instrument(skip(self, context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &CreatePrivateMessage = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - let local_site = LocalSite::read(&mut context.pool()).await?; + let content = sanitize_html(&data.content); + let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site)); + is_valid_body_field(&Some(content.clone()), false)?; - let content = sanitize_html(&data.content); - let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site)); - is_valid_body_field(&Some(content.clone()), false)?; + check_person_block( + local_user_view.person.id, + data.recipient_id, + &mut context.pool(), + ) + .await?; - check_person_block( - local_user_view.person.id, - data.recipient_id, - &mut context.pool(), - ) - .await?; + let private_message_form = PrivateMessageInsertForm::builder() + .content(content.clone()) + .creator_id(local_user_view.person.id) + .recipient_id(data.recipient_id) + .build(); - let private_message_form = PrivateMessageInsertForm::builder() - .content(content.clone()) - .creator_id(local_user_view.person.id) - .recipient_id(data.recipient_id) - .build(); - - let inserted_private_message = - PrivateMessage::create(&mut context.pool(), &private_message_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?; - - let inserted_private_message_id = inserted_private_message.id; - let protocol_and_hostname = context.settings().get_protocol_and_hostname(); - let apub_id = generate_local_apub_endpoint( - EndpointType::PrivateMessage, - &inserted_private_message_id.to_string(), - &protocol_and_hostname, - )?; - PrivateMessage::update( - &mut context.pool(), - inserted_private_message.id, - &PrivateMessageUpdateForm::builder() - .ap_id(Some(apub_id)) - .build(), - ) + let inserted_private_message = PrivateMessage::create(&mut context.pool(), &private_message_form) .await .with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?; - let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id).await?; + let inserted_private_message_id = inserted_private_message.id; + let protocol_and_hostname = context.settings().get_protocol_and_hostname(); + let apub_id = generate_local_apub_endpoint( + EndpointType::PrivateMessage, + &inserted_private_message_id.to_string(), + &protocol_and_hostname, + )?; + PrivateMessage::update( + &mut context.pool(), + inserted_private_message.id, + &PrivateMessageUpdateForm::builder() + .ap_id(Some(apub_id)) + .build(), + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?; - // Send email to the local recipient, if one exists - if view.recipient.local { - let recipient_id = data.recipient_id; - let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?; - let lang = get_interface_language(&local_recipient); - let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname()); - let sender_name = &local_user_view.person.name; - send_email_to_user( - &local_recipient, - &lang.notification_private_message_subject(sender_name), - &lang.notification_private_message_body(inbox_link, &content, sender_name), - context.settings(), - ) - .await; - } + let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id).await?; - Ok(PrivateMessageResponse { - private_message_view: view, - }) + // Send email to the local recipient, if one exists + if view.recipient.local { + let recipient_id = data.recipient_id; + let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?; + let lang = get_interface_language(&local_recipient); + let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname()); + let sender_name = &local_user_view.person.name; + send_email_to_user( + &local_recipient, + &lang.notification_private_message_subject(sender_name), + &lang.notification_private_message_body(inbox_link, &content, sender_name), + context.settings(), + ) + .await; } + + ActivityChannel::submit_activity( + SendActivityData::CreatePrivateMessage(view.clone()), + &context, + ) + .await?; + + Ok(Json(PrivateMessageResponse { + private_message_view: view, + })) } diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs index c18e94c0a..7657a7ff7 100644 --- a/crates/api_crud/src/private_message/delete.rs +++ b/crates/api_crud/src/private_message/delete.rs @@ -1,8 +1,9 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, private_message::{DeletePrivateMessage, PrivateMessageResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::local_user_view_from_jwt, }; use lemmy_db_schema::{ @@ -12,42 +13,41 @@ use lemmy_db_schema::{ use lemmy_db_views::structs::PrivateMessageView; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; -#[async_trait::async_trait(?Send)] -impl PerformCrud for DeletePrivateMessage { - type Response = PrivateMessageResponse; +#[tracing::instrument(skip(context))] +pub async fn delete_private_message( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; - #[tracing::instrument(skip(self, context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &DeletePrivateMessage = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - - // Checking permissions - let private_message_id = data.private_message_id; - let orig_private_message = - PrivateMessage::read(&mut context.pool(), private_message_id).await?; - if local_user_view.person.id != orig_private_message.creator_id { - return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?; - } - - // Doing the update - let private_message_id = data.private_message_id; - let deleted = data.deleted; - PrivateMessage::update( - &mut context.pool(), - private_message_id, - &PrivateMessageUpdateForm::builder() - .deleted(Some(deleted)) - .build(), - ) - .await - .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?; - - let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; - Ok(PrivateMessageResponse { - private_message_view: view, - }) + // Checking permissions + let private_message_id = data.private_message_id; + let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?; + if local_user_view.person.id != orig_private_message.creator_id { + return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?; } + + // Doing the update + let private_message_id = data.private_message_id; + let deleted = data.deleted; + let private_message = PrivateMessage::update( + &mut context.pool(), + private_message_id, + &PrivateMessageUpdateForm::builder() + .deleted(Some(deleted)) + .build(), + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?; + + ActivityChannel::submit_activity( + SendActivityData::DeletePrivateMessage(local_user_view.person, private_message, data.deleted), + &context, + ) + .await?; + + let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; + Ok(Json(PrivateMessageResponse { + private_message_view: view, + })) } diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index 09b50540d..eb24d7c12 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -1,8 +1,9 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, private_message::{EditPrivateMessage, PrivateMessageResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html}, }; use lemmy_db_schema::{ @@ -19,48 +20,47 @@ use lemmy_utils::{ utils::{slurs::remove_slurs, validation::is_valid_body_field}, }; -#[async_trait::async_trait(?Send)] -impl PerformCrud for EditPrivateMessage { - type Response = PrivateMessageResponse; +#[tracing::instrument(skip(context))] +pub async fn update_private_message( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?; + let local_site = LocalSite::read(&mut context.pool()).await?; - #[tracing::instrument(skip(self, context))] - async fn perform( - &self, - context: &Data, - ) -> Result { - let data: &EditPrivateMessage = self; - let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; - let local_site = LocalSite::read(&mut context.pool()).await?; - - // Checking permissions - let private_message_id = data.private_message_id; - let orig_private_message = - PrivateMessage::read(&mut context.pool(), private_message_id).await?; - if local_user_view.person.id != orig_private_message.creator_id { - return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?; - } - - // Doing the update - let content = sanitize_html(&data.content); - let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site)); - is_valid_body_field(&Some(content.clone()), false)?; - - let private_message_id = data.private_message_id; - PrivateMessage::update( - &mut context.pool(), - private_message_id, - &PrivateMessageUpdateForm::builder() - .content(Some(content)) - .updated(Some(Some(naive_now()))) - .build(), - ) - .await - .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?; - - let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; - - Ok(PrivateMessageResponse { - private_message_view: view, - }) + // Checking permissions + let private_message_id = data.private_message_id; + let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?; + if local_user_view.person.id != orig_private_message.creator_id { + return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?; } + + // Doing the update + let content = sanitize_html(&data.content); + let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site)); + is_valid_body_field(&Some(content.clone()), false)?; + + let private_message_id = data.private_message_id; + PrivateMessage::update( + &mut context.pool(), + private_message_id, + &PrivateMessageUpdateForm::builder() + .content(Some(content)) + .updated(Some(Some(naive_now()))) + .build(), + ) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?; + + let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; + + ActivityChannel::submit_activity( + SendActivityData::UpdatePrivateMessage(view.clone()), + &context, + ) + .await?; + + Ok(Json(PrivateMessageResponse { + private_message_view: view, + })) } diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index f2af6940e..1665ba191 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -1,6 +1,5 @@ -use crate::PerformCrud; -use activitypub_federation::http_signatures::generate_actor_keypair; -use actix_web::web::Data; +use activitypub_federation::{config::Data, http_signatures::generate_actor_keypair}; +use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, person::{LoginResponse, Register}, @@ -38,177 +37,173 @@ use lemmy_utils::{ }, }; -#[async_trait::async_trait(?Send)] -impl PerformCrud for Register { - type Response = LoginResponse; +#[tracing::instrument(skip(context))] +pub async fn register( + data: Json, + context: Data, +) -> Result, LemmyError> { + let site_view = SiteView::read_local(&mut context.pool()).await?; + let local_site = site_view.local_site; + let require_registration_application = + local_site.registration_mode == RegistrationMode::RequireApplication; - #[tracing::instrument(skip(self, context))] - async fn perform(&self, context: &Data) -> Result { - let data: &Register = self; + if local_site.registration_mode == RegistrationMode::Closed { + return Err(LemmyErrorType::RegistrationClosed)?; + } - let site_view = SiteView::read_local(&mut context.pool()).await?; - let local_site = site_view.local_site; - let require_registration_application = - local_site.registration_mode == RegistrationMode::RequireApplication; + password_length_check(&data.password)?; + honeypot_check(&data.honeypot)?; - if local_site.registration_mode == RegistrationMode::Closed { - return Err(LemmyErrorType::RegistrationClosed)?; - } + if local_site.require_email_verification && data.email.is_none() { + return Err(LemmyErrorType::EmailRequired)?; + } - password_length_check(&data.password)?; - honeypot_check(&data.honeypot)?; + if local_site.site_setup && require_registration_application && data.answer.is_none() { + return Err(LemmyErrorType::RegistrationApplicationAnswerRequired)?; + } - if local_site.require_email_verification && data.email.is_none() { - return Err(LemmyErrorType::EmailRequired)?; - } + // Make sure passwords match + if data.password != data.password_verify { + return Err(LemmyErrorType::PasswordsDoNotMatch)?; + } - if local_site.site_setup && require_registration_application && data.answer.is_none() { - return Err(LemmyErrorType::RegistrationApplicationAnswerRequired)?; - } - - // Make sure passwords match - if data.password != data.password_verify { - return Err(LemmyErrorType::PasswordsDoNotMatch)?; - } - - if local_site.site_setup && local_site.captcha_enabled { - if let Some(captcha_uuid) = &data.captcha_uuid { - let uuid = uuid::Uuid::parse_str(captcha_uuid)?; - let check = CaptchaAnswer::check_captcha( - &mut context.pool(), - CheckCaptchaAnswer { - uuid, - answer: data.captcha_answer.clone().unwrap_or_default(), - }, - ) - .await?; - if !check { - return Err(LemmyErrorType::CaptchaIncorrect)?; - } - } else { + if local_site.site_setup && local_site.captcha_enabled { + if let Some(captcha_uuid) = &data.captcha_uuid { + let uuid = uuid::Uuid::parse_str(captcha_uuid)?; + let check = CaptchaAnswer::check_captcha( + &mut context.pool(), + CheckCaptchaAnswer { + uuid, + answer: data.captcha_answer.clone().unwrap_or_default(), + }, + ) + .await?; + if !check { return Err(LemmyErrorType::CaptchaIncorrect)?; } + } else { + return Err(LemmyErrorType::CaptchaIncorrect)?; } + } - let slur_regex = local_site_to_slur_regex(&local_site); - check_slurs(&data.username, &slur_regex)?; - check_slurs_opt(&data.answer, &slur_regex)?; - let username = sanitize_html(&data.username); + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs(&data.username, &slur_regex)?; + check_slurs_opt(&data.answer, &slur_regex)?; + let username = sanitize_html(&data.username); - let actor_keypair = generate_actor_keypair()?; - is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?; - let actor_id = generate_local_apub_endpoint( - EndpointType::Person, - &data.username, - &context.settings().get_protocol_and_hostname(), - )?; + let actor_keypair = generate_actor_keypair()?; + is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?; + let actor_id = generate_local_apub_endpoint( + EndpointType::Person, + &data.username, + &context.settings().get_protocol_and_hostname(), + )?; - if let Some(email) = &data.email { - if LocalUser::is_email_taken(&mut context.pool(), email).await? { - return Err(LemmyErrorType::EmailAlreadyExists)?; - } + if let Some(email) = &data.email { + if LocalUser::is_email_taken(&mut context.pool(), email).await? { + return Err(LemmyErrorType::EmailAlreadyExists)?; } + } - // We have to create both a person, and local_user + // We have to create both a person, and local_user - // Register the new person - let person_form = PersonInsertForm::builder() - .name(username) - .actor_id(Some(actor_id.clone())) - .private_key(Some(actor_keypair.private_key)) - .public_key(actor_keypair.public_key) - .inbox_url(Some(generate_inbox_url(&actor_id)?)) - .shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?)) - // If its the initial site setup, they are an admin - .admin(Some(!local_site.site_setup)) - .instance_id(site_view.site.instance_id) - .build(); + // Register the new person + let person_form = PersonInsertForm::builder() + .name(username) + .actor_id(Some(actor_id.clone())) + .private_key(Some(actor_keypair.private_key)) + .public_key(actor_keypair.public_key) + .inbox_url(Some(generate_inbox_url(&actor_id)?)) + .shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?)) + // If its the initial site setup, they are an admin + .admin(Some(!local_site.site_setup)) + .instance_id(site_view.site.instance_id) + .build(); - // insert the person - let inserted_person = Person::create(&mut context.pool(), &person_form) - .await - .with_lemmy_type(LemmyErrorType::UserAlreadyExists)?; + // insert the person + let inserted_person = Person::create(&mut context.pool(), &person_form) + .await + .with_lemmy_type(LemmyErrorType::UserAlreadyExists)?; - // Automatically set their application as accepted, if they created this with open registration. - // Also fixes a bug which allows users to log in when registrations are changed to closed. - let accepted_application = Some(!require_registration_application); + // Automatically set their application as accepted, if they created this with open registration. + // Also fixes a bug which allows users to log in when registrations are changed to closed. + let accepted_application = Some(!require_registration_application); - // Create the local user - let local_user_form = LocalUserInsertForm::builder() - .person_id(inserted_person.id) - .email(data.email.as_deref().map(str::to_lowercase)) - .password_encrypted(data.password.to_string()) - .show_nsfw(Some(data.show_nsfw)) - .accepted_application(accepted_application) - .default_listing_type(Some(local_site.default_post_listing_type)) - .build(); + // Create the local user + let local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_person.id) + .email(data.email.as_deref().map(str::to_lowercase)) + .password_encrypted(data.password.to_string()) + .show_nsfw(Some(data.show_nsfw)) + .accepted_application(accepted_application) + .default_listing_type(Some(local_site.default_post_listing_type)) + .build(); - let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?; + let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?; - if local_site.site_setup && require_registration_application { - // Create the registration application - let form = RegistrationApplicationInsertForm { - local_user_id: inserted_local_user.id, - // We already made sure answer was not null above - answer: data.answer.clone().expect("must have an answer"), - }; - - RegistrationApplication::create(&mut context.pool(), &form).await?; - } - - // Email the admins - if local_site.application_email_admins { - send_new_applicant_email_to_admins(&data.username, &mut context.pool(), context.settings()) - .await?; - } - - let mut login_response = LoginResponse { - jwt: None, - registration_created: false, - verify_email_sent: false, + if local_site.site_setup && require_registration_application { + // Create the registration application + let form = RegistrationApplicationInsertForm { + local_user_id: inserted_local_user.id, + // We already made sure answer was not null above + answer: data.answer.clone().expect("must have an answer"), }; - // Log the user in directly if the site is not setup, or email verification and application aren't required - if !local_site.site_setup - || (!require_registration_application && !local_site.require_email_verification) - { - login_response.jwt = Some( - Claims::jwt( - inserted_local_user.id.0, - &context.secret().jwt_secret, - &context.settings().hostname, - )? - .into(), - ); - } else { - if local_site.require_email_verification { - let local_user_view = LocalUserView { - local_user: inserted_local_user, - person: inserted_person, - counts: PersonAggregates::default(), - }; - // we check at the beginning of this method that email is set - let email = local_user_view - .local_user - .email - .clone() - .expect("email was provided"); + RegistrationApplication::create(&mut context.pool(), &form).await?; + } - send_verification_email( - &local_user_view, - &email, - &mut context.pool(), - context.settings(), - ) - .await?; - login_response.verify_email_sent = true; - } + // Email the admins + if local_site.application_email_admins { + send_new_applicant_email_to_admins(&data.username, &mut context.pool(), context.settings()) + .await?; + } - if require_registration_application { - login_response.registration_created = true; - } + let mut login_response = LoginResponse { + jwt: None, + registration_created: false, + verify_email_sent: false, + }; + + // Log the user in directly if the site is not setup, or email verification and application aren't required + if !local_site.site_setup + || (!require_registration_application && !local_site.require_email_verification) + { + login_response.jwt = Some( + Claims::jwt( + inserted_local_user.id.0, + &context.secret().jwt_secret, + &context.settings().hostname, + )? + .into(), + ); + } else { + if local_site.require_email_verification { + let local_user_view = LocalUserView { + local_user: inserted_local_user, + person: inserted_person, + counts: PersonAggregates::default(), + }; + // we check at the beginning of this method that email is set + let email = local_user_view + .local_user + .email + .clone() + .expect("email was provided"); + + send_verification_email( + &local_user_view, + &email, + &mut context.pool(), + context.settings(), + ) + .await?; + login_response.verify_email_sent = true; } - Ok(login_response) + if require_registration_application { + login_response.registration_created = true; + } } + + Ok(Json(login_response)) } diff --git a/crates/api_crud/src/user/delete.rs b/crates/api_crud/src/user/delete.rs index 5a8b4d036..94c547b14 100644 --- a/crates/api_crud/src/user/delete.rs +++ b/crates/api_crud/src/user/delete.rs @@ -1,32 +1,36 @@ -use crate::PerformCrud; -use actix_web::web::Data; +use activitypub_federation::config::Data; +use actix_web::web::Json; use bcrypt::verify; use lemmy_api_common::{ context::LemmyContext, person::{DeleteAccount, DeleteAccountResponse}, + send_activity::{ActivityChannel, SendActivityData}, utils::local_user_view_from_jwt, }; use lemmy_utils::error::{LemmyError, LemmyErrorType}; -#[async_trait::async_trait(?Send)] -impl PerformCrud for DeleteAccount { - type Response = DeleteAccountResponse; +#[tracing::instrument(skip(context))] +pub async fn delete_account( + data: Json, + context: Data, +) -> Result, LemmyError> { + let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), &context).await?; - #[tracing::instrument(skip(self, context))] - async fn perform(&self, context: &Data) -> Result { - let data = self; - let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), context).await?; - - // Verify the password - let valid: bool = verify( - &data.password, - &local_user_view.local_user.password_encrypted, - ) - .unwrap_or(false); - if !valid { - return Err(LemmyErrorType::IncorrectLogin)?; - } - - Ok(DeleteAccountResponse {}) + // Verify the password + let valid: bool = verify( + &data.password, + &local_user_view.local_user.password_encrypted, + ) + .unwrap_or(false); + if !valid { + return Err(LemmyErrorType::IncorrectLogin)?; } + + ActivityChannel::submit_activity( + SendActivityData::DeleteUser(local_user_view.person), + &context, + ) + .await?; + + Ok(Json(DeleteAccountResponse {})) } diff --git a/crates/api_crud/src/user/mod.rs b/crates/api_crud/src/user/mod.rs index aeaae9ddd..da1aa3ace 100644 --- a/crates/api_crud/src/user/mod.rs +++ b/crates/api_crud/src/user/mod.rs @@ -1,2 +1,2 @@ -mod create; -mod delete; +pub mod create; +pub mod delete; diff --git a/crates/apub/src/activities/block/mod.rs b/crates/apub/src/activities/block/mod.rs index 7ee9ec177..e9986afc8 100644 --- a/crates/apub/src/activities/block/mod.rs +++ b/crates/apub/src/activities/block/mod.rs @@ -1,10 +1,9 @@ use crate::{ - objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson}, + objects::{community::ApubCommunity, instance::ApubSite}, protocol::{ activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, objects::{group::Group, instance::Instance}, }, - SendActivity, }; use activitypub_federation::{ config::Data, @@ -12,19 +11,18 @@ use activitypub_federation::{ traits::{Actor, Object}, }; use chrono::NaiveDateTime; -use lemmy_api_common::{ - community::{BanFromCommunity, BanFromCommunityResponse}, - context::LemmyContext, - person::{BanPerson, BanPersonResponse}, - utils::local_user_view_from_jwt, -}; +use lemmy_api_common::{community::BanFromCommunity, context::LemmyContext, person::BanPerson}; use lemmy_db_schema::{ + newtypes::CommunityId, source::{community::Community, person::Person, site::Site}, traits::Crud, utils::DbPool, }; use lemmy_db_views::structs::SiteView; -use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix}; +use lemmy_utils::{ + error::{LemmyError, LemmyResult}, + utils::time::naive_from_unix, +}; use serde::Deserialize; use url::Url; @@ -132,87 +130,74 @@ async fn generate_cc( }) } -#[async_trait::async_trait] -impl SendActivity for BanPerson { - type Response = BanPersonResponse; +pub(crate) async fn send_ban_from_site( + mod_: Person, + banned_user: Person, + data: BanPerson, + context: Data, +) -> Result<(), LemmyError> { + let site = SiteOrCommunity::Site(SiteView::read_local(&mut context.pool()).await?.site.into()); + let expires = data.expires.map(naive_from_unix); - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let person = Person::read(&mut context.pool(), request.person_id).await?; - let site = SiteOrCommunity::Site(SiteView::read_local(&mut 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] -impl SendActivity for BanFromCommunity { - type Response = BanFromCommunityResponse; - - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id) - .await? - .into(); - let banned_person: ApubPerson = Person::read(&mut context.pool(), request.person_id) - .await? - .into(); - let expires = request.expires.map(naive_from_unix); - - if request.ban { + // if the action affects a local user, federate to other instances + if banned_user.local { + if data.ban { BlockUser::send( - &SiteOrCommunity::Community(community), - &banned_person, - &local_user_view.person.clone().into(), - request.remove_data.unwrap_or(false), - request.reason.clone(), + &site, + &banned_user.into(), + &mod_.into(), + data.remove_data.unwrap_or(false), + data.reason.clone(), expires, - context, + &context, ) .await } else { UndoBlockUser::send( - &SiteOrCommunity::Community(community), - &banned_person, - &local_user_view.person.clone().into(), - request.reason.clone(), - context, + &site, + &banned_user.into(), + &mod_.into(), + data.reason.clone(), + &context, ) .await } + } else { + Ok(()) + } +} + +pub(crate) async fn send_ban_from_community( + mod_: Person, + community_id: CommunityId, + banned_person: Person, + data: BanFromCommunity, + context: Data, +) -> LemmyResult<()> { + let community: ApubCommunity = Community::read(&mut context.pool(), community_id) + .await? + .into(); + let expires = data.expires.map(naive_from_unix); + + if data.ban { + BlockUser::send( + &SiteOrCommunity::Community(community), + &banned_person.into(), + &mod_.into(), + data.remove_data.unwrap_or(false), + data.reason.clone(), + expires, + &context, + ) + .await + } else { + UndoBlockUser::send( + &SiteOrCommunity::Community(community), + &banned_person.into(), + &mod_.into(), + data.reason.clone(), + &context, + ) + .await } } diff --git a/crates/apub/src/activities/community/collection_add.rs b/crates/apub/src/activities/community/collection_add.rs index c36a8f0da..e03ded2b3 100644 --- a/crates/apub/src/activities/community/collection_add.rs +++ b/crates/apub/src/activities/community/collection_add.rs @@ -13,7 +13,6 @@ use crate::{ activities::community::{collection_add::CollectionAdd, collection_remove::CollectionRemove}, InCommunity, }, - SendActivity, }; use activitypub_federation::{ config::Data, @@ -22,13 +21,12 @@ use activitypub_federation::{ traits::{ActivityHandler, Actor}, }; use lemmy_api_common::{ - community::{AddModToCommunity, AddModToCommunityResponse}, context::LemmyContext, - post::{FeaturePost, PostResponse}, - utils::{generate_featured_url, generate_moderators_url, local_user_view_from_jwt}, + utils::{generate_featured_url, generate_moderators_url}, }; use lemmy_db_schema::{ impls::community::CollectionType, + newtypes::{CommunityId, PersonId}, source::{ community::{Community, CommunityModerator, CommunityModeratorForm}, moderator::{ModAddCommunity, ModAddCommunityForm}, @@ -165,61 +163,41 @@ impl ActivityHandler for CollectionAdd { } } -#[async_trait::async_trait] -impl SendActivity for AddModToCommunity { - type Response = AddModToCommunityResponse; - - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id) - .await? - .into(); - let updated_mod: ApubPerson = Person::read(&mut context.pool(), request.person_id) - .await? - .into(); - if request.added { - CollectionAdd::send_add_mod( - &community, - &updated_mod, - &local_user_view.person.into(), - context, - ) - .await - } else { - CollectionRemove::send_remove_mod( - &community, - &updated_mod, - &local_user_view.person.into(), - context, - ) - .await - } +pub(crate) async fn send_add_mod_to_community( + actor: Person, + community_id: CommunityId, + updated_mod_id: PersonId, + added: bool, + context: Data, +) -> Result<(), LemmyError> { + let actor: ApubPerson = actor.into(); + let community: ApubCommunity = Community::read(&mut context.pool(), community_id) + .await? + .into(); + let updated_mod: ApubPerson = Person::read(&mut context.pool(), updated_mod_id) + .await? + .into(); + if added { + CollectionAdd::send_add_mod(&community, &updated_mod, &actor, &context).await + } else { + CollectionRemove::send_remove_mod(&community, &updated_mod, &actor, &context).await } } -#[async_trait::async_trait] -impl SendActivity for FeaturePost { - type Response = PostResponse; - - async fn send_activity( - request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut context.pool(), response.post_view.community.id) - .await? - .into(); - let post = response.post_view.post.clone().into(); - let person = local_user_view.person.into(); - if request.featured { - CollectionAdd::send_add_featured_post(&community, &post, &person, context).await - } else { - CollectionRemove::send_remove_featured_post(&community, &post, &person, context).await - } +pub(crate) async fn send_feature_post( + post: Post, + actor: Person, + featured: bool, + context: Data, +) -> Result<(), LemmyError> { + let actor: ApubPerson = actor.into(); + let post: ApubPost = post.into(); + let community = Community::read(&mut context.pool(), post.community_id) + .await? + .into(); + if featured { + CollectionAdd::send_add_featured_post(&community, &post, &actor, &context).await + } else { + CollectionRemove::send_remove_featured_post(&community, &post, &actor, &context).await } } diff --git a/crates/apub/src/activities/community/lock_page.rs b/crates/apub/src/activities/community/lock_page.rs index 94135ede9..2ceb18384 100644 --- a/crates/apub/src/activities/community/lock_page.rs +++ b/crates/apub/src/activities/community/lock_page.rs @@ -9,25 +9,23 @@ use crate::{ }, activity_lists::AnnouncableActivities, insert_received_activity, + objects::community::ApubCommunity, protocol::{ activities::community::lock_page::{LockPage, LockType, UndoLockPage}, InCommunity, }, - SendActivity, }; use activitypub_federation::{ config::Data, + fetch::object_id::ObjectId, kinds::{activity::UndoType, public}, traits::ActivityHandler, }; -use lemmy_api_common::{ - context::LemmyContext, - post::{LockPost, PostResponse}, - utils::local_user_view_from_jwt, -}; +use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ community::Community, + person::Person, post::{Post, PostUpdateForm}, }, traits::Crud, @@ -102,59 +100,47 @@ impl ActivityHandler for UndoLockPage { } } -#[async_trait::async_trait] -impl SendActivity for LockPost { - type Response = PostResponse; - - async fn send_activity( - request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; +pub(crate) async fn send_lock_post( + post: Post, + actor: Person, + locked: bool, + context: Data, +) -> Result<(), LemmyError> { + let community: ApubCommunity = Community::read(&mut context.pool(), post.community_id) + .await? + .into(); + let id = generate_activity_id( + LockType::Lock, + &context.settings().get_protocol_and_hostname(), + )?; + let community_id = community.actor_id.inner().clone(); + let lock = LockPage { + actor: actor.actor_id.clone().into(), + to: vec![public()], + object: ObjectId::from(post.ap_id), + cc: vec![community_id.clone()], + kind: LockType::Lock, + id, + audience: Some(community_id.into()), + }; + let activity = if locked { + AnnouncableActivities::LockPost(lock) + } else { let id = generate_activity_id( - LockType::Lock, + UndoType::Undo, &context.settings().get_protocol_and_hostname(), )?; - let community_id = response.post_view.community.actor_id.clone(); - let actor = local_user_view.person.actor_id.clone().into(); - let lock = LockPage { - actor, + let undo = UndoLockPage { + actor: lock.actor.clone(), to: vec![public()], - object: response.post_view.post.ap_id.clone().into(), - cc: vec![community_id.clone().into()], - kind: LockType::Lock, + cc: lock.cc.clone(), + kind: UndoType::Undo, id, - audience: Some(community_id.into()), + audience: lock.audience.clone(), + object: lock, }; - let activity = if request.locked { - AnnouncableActivities::LockPost(lock) - } else { - let id = generate_activity_id( - UndoType::Undo, - &context.settings().get_protocol_and_hostname(), - )?; - let undo = UndoLockPage { - actor: lock.actor.clone(), - to: vec![public()], - cc: lock.cc.clone(), - kind: UndoType::Undo, - id, - audience: lock.audience.clone(), - object: lock, - }; - AnnouncableActivities::UndoLockPost(undo) - }; - let community = Community::read(&mut context.pool(), response.post_view.community.id).await?; - send_activity_in_community( - activity, - &local_user_view.person.into(), - &community.into(), - vec![], - true, - context, - ) - .await?; - Ok(()) - } + AnnouncableActivities::UndoLockPost(undo) + }; + send_activity_in_community(activity, &actor.into(), &community, vec![], true, &context).await?; + Ok(()) } diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index 22a8c12be..a17df7119 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -4,7 +4,6 @@ use crate::{ objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::community::report::Report, InCommunity}, PostOrComment, - SendActivity, }; use activitypub_federation::{ config::Data, @@ -12,15 +11,12 @@ use activitypub_federation::{ kinds::activity::FlagType, traits::{ActivityHandler, Actor}, }; -use lemmy_api_common::{ - comment::{CommentReportResponse, CreateCommentReport}, - context::LemmyContext, - post::{CreatePostReport, PostReportResponse}, - utils::{local_user_view_from_jwt, sanitize_html}, -}; +use lemmy_api_common::{context::LemmyContext, utils::sanitize_html}; use lemmy_db_schema::{ source::{ comment_report::{CommentReport, CommentReportForm}, + community::Community, + person::Person, post_report::{PostReport, PostReportForm}, }, traits::Reportable, @@ -28,58 +24,17 @@ use lemmy_db_schema::{ use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait] -impl SendActivity for CreatePostReport { - type Response = PostReportResponse; - - async fn send_activity( - request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - Report::send( - ObjectId::from(response.post_report_view.post.ap_id.clone()), - &local_user_view.person.into(), - ObjectId::from(response.post_report_view.community.actor_id.clone()), - request.reason.to_string(), - context, - ) - .await - } -} - -#[async_trait::async_trait] -impl SendActivity for CreateCommentReport { - type Response = CommentReportResponse; - - async fn send_activity( - request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - Report::send( - ObjectId::from(response.comment_report_view.comment.ap_id.clone()), - &local_user_view.person.into(), - ObjectId::from(response.comment_report_view.community.actor_id.clone()), - request.reason.to_string(), - context, - ) - .await - } -} - impl Report { #[tracing::instrument(skip_all)] - async fn send( + pub(crate) async fn send( object_id: ObjectId, - actor: &ApubPerson, - community_id: ObjectId, + actor: Person, + community: Community, reason: String, - context: &Data, + context: Data, ) -> Result<(), LemmyError> { - let community = community_id.dereference_local(context).await?; + let actor: ApubPerson = actor.into(); + let community: ApubCommunity = community.into(); let kind = FlagType::Flag; let id = generate_activity_id( kind.clone(), @@ -96,7 +51,7 @@ impl Report { }; let inbox = vec![community.shared_inbox_or_inbox()]; - send_lemmy_activity(context, report, actor, inbox, false).await + send_lemmy_activity(&context, report, &actor, inbox, false).await } } diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index fe2477d6e..c3b2a2ae1 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -10,61 +10,43 @@ use crate::{ insert_received_activity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::community::update::UpdateCommunity, InCommunity}, - SendActivity, }; use activitypub_federation::{ config::Data, kinds::{activity::UpdateType, public}, traits::{ActivityHandler, Actor, Object}, }; -use lemmy_api_common::{ - community::{CommunityResponse, EditCommunity, HideCommunity}, - context::LemmyContext, - utils::local_user_view_from_jwt, +use lemmy_api_common::context::LemmyContext; +use lemmy_db_schema::{ + source::{community::Community, person::Person}, + traits::Crud, }; -use lemmy_db_schema::{source::community::Community, traits::Crud}; use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait] -impl SendActivity for EditCommunity { - type Response = CommunityResponse; +pub(crate) async fn send_update_community( + community: Community, + actor: Person, + context: Data, +) -> Result<(), LemmyError> { + let community: ApubCommunity = community.into(); + let actor: ApubPerson = actor.into(); + let id = generate_activity_id( + UpdateType::Update, + &context.settings().get_protocol_and_hostname(), + )?; + let update = UpdateCommunity { + actor: actor.id().into(), + to: vec![public()], + object: Box::new(community.clone().into_json(&context).await?), + cc: vec![community.id()], + kind: UpdateType::Update, + id: id.clone(), + audience: Some(community.id().into()), + }; - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut 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( - community: ApubCommunity, - actor: &ApubPerson, - context: &Data, - ) -> Result<(), LemmyError> { - let id = generate_activity_id( - UpdateType::Update, - &context.settings().get_protocol_and_hostname(), - )?; - let update = UpdateCommunity { - actor: actor.id().into(), - to: vec![public()], - object: Box::new(community.clone().into_json(context).await?), - cc: vec![community.id()], - kind: UpdateType::Update, - id: id.clone(), - audience: Some(community.id().into()), - }; - - let activity = AnnouncableActivities::UpdateCommunity(update); - send_activity_in_community(activity, actor, &community, vec![], true, context).await - } + let activity = AnnouncableActivities::UpdateCommunity(update); + send_activity_in_community(activity, &actor, &community, vec![], true, &context).await } #[async_trait::async_trait] @@ -101,18 +83,3 @@ impl ActivityHandler for UpdateCommunity { Ok(()) } } - -#[async_trait::async_trait] -impl SendActivity for HideCommunity { - type Response = CommunityResponse; - - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut 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/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs index 3eaad2f71..77a430c3c 100644 --- a/crates/apub/src/activities/create_or_update/private_message.rs +++ b/crates/apub/src/activities/create_or_update/private_message.rs @@ -6,92 +6,40 @@ use crate::{ create_or_update::chat_message::CreateOrUpdateChatMessage, CreateOrUpdateType, }, - SendActivity, }; use activitypub_federation::{ config::Data, protocol::verification::verify_domains_match, traits::{ActivityHandler, Actor, Object}, }; -use lemmy_api_common::{ - context::LemmyContext, - private_message::{CreatePrivateMessage, EditPrivateMessage, PrivateMessageResponse}, -}; -use lemmy_db_schema::{ - newtypes::PersonId, - source::{person::Person, private_message::PrivateMessage}, - traits::Crud, -}; +use lemmy_api_common::context::LemmyContext; +use lemmy_db_views::structs::PrivateMessageView; use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait] -impl SendActivity for CreatePrivateMessage { - type Response = PrivateMessageResponse; +pub(crate) async fn send_create_or_update_pm( + pm_view: PrivateMessageView, + kind: CreateOrUpdateType, + context: Data, +) -> Result<(), LemmyError> { + let actor: ApubPerson = pm_view.creator.into(); + let recipient: ApubPerson = pm_view.recipient.into(); - async fn send_activity( - _request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - CreateOrUpdateChatMessage::send( - &response.private_message_view.private_message, - response.private_message_view.creator.id, - CreateOrUpdateType::Create, - context, - ) - .await - } -} -#[async_trait::async_trait] -impl SendActivity for EditPrivateMessage { - type Response = PrivateMessageResponse; - - async fn send_activity( - _request: &Self, - response: &Self::Response, - context: &Data, - ) -> 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)] - async fn send( - private_message: &PrivateMessage, - sender_id: PersonId, - kind: CreateOrUpdateType, - context: &Data, - ) -> Result<(), LemmyError> { - let recipient_id = private_message.recipient_id; - let sender: ApubPerson = Person::read(&mut context.pool(), sender_id).await?.into(); - let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id) - .await? - .into(); - - let id = generate_activity_id( - kind.clone(), - &context.settings().get_protocol_and_hostname(), - )?; - let create_or_update = CreateOrUpdateChatMessage { - id: id.clone(), - actor: sender.id().into(), - to: [recipient.id().into()], - object: ApubPrivateMessage(private_message.clone()) - .into_json(context) - .await?, - kind, - }; - let inbox = vec![recipient.shared_inbox_or_inbox()]; - send_lemmy_activity(context, create_or_update, &sender, inbox, true).await - } + let id = generate_activity_id( + kind.clone(), + &context.settings().get_protocol_and_hostname(), + )?; + let create_or_update = CreateOrUpdateChatMessage { + id: id.clone(), + actor: actor.id().into(), + to: [recipient.id().into()], + object: ApubPrivateMessage(pm_view.private_message.clone()) + .into_json(&context) + .await?, + kind, + }; + let inbox = vec![recipient.shared_inbox_or_inbox()]; + send_lemmy_activity(&context, create_or_update, &actor, inbox, true).await } #[async_trait::async_trait] diff --git a/crates/apub/src/activities/deletion/delete_user.rs b/crates/apub/src/activities/deletion/delete_user.rs index b388ed9e1..cf37dc5ab 100644 --- a/crates/apub/src/activities/deletion/delete_user.rs +++ b/crates/apub/src/activities/deletion/delete_user.rs @@ -3,7 +3,6 @@ use crate::{ insert_received_activity, objects::{instance::remote_instance_inboxes, person::ApubPerson}, protocol::activities::deletion::delete_user::DeleteUser, - SendActivity, }; use activitypub_federation::{ config::Data, @@ -11,50 +10,37 @@ use activitypub_federation::{ protocol::verification::verify_urls_match, traits::{ActivityHandler, Actor}, }; -use lemmy_api_common::{ - context::LemmyContext, - person::{DeleteAccount, DeleteAccountResponse}, - utils::{delete_user_account, local_user_view_from_jwt}, -}; +use lemmy_api_common::{context::LemmyContext, utils::delete_user_account}; +use lemmy_db_schema::source::person::Person; use lemmy_utils::error::LemmyError; use url::Url; -#[async_trait::async_trait] -impl SendActivity for DeleteAccount { - type Response = DeleteAccountResponse; +pub async fn delete_user(person: Person, context: Data) -> Result<(), LemmyError> { + let actor: ApubPerson = person.into(); + delete_user_account( + actor.id, + &mut context.pool(), + context.settings(), + context.client(), + ) + .await?; - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let actor: ApubPerson = local_user_view.person.into(); - delete_user_account( - actor.id, - &mut context.pool(), - context.settings(), - context.client(), - ) - .await?; + let id = generate_activity_id( + DeleteType::Delete, + &context.settings().get_protocol_and_hostname(), + )?; + let delete = DeleteUser { + actor: actor.id().into(), + to: vec![public()], + object: actor.id().into(), + kind: DeleteType::Delete, + id: id.clone(), + cc: vec![], + }; - let id = generate_activity_id( - DeleteType::Delete, - &context.settings().get_protocol_and_hostname(), - )?; - let delete = DeleteUser { - actor: actor.id().into(), - to: vec![public()], - object: actor.id().into(), - kind: DeleteType::Delete, - id: id.clone(), - cc: vec![], - }; - - let inboxes = remote_instance_inboxes(&mut context.pool()).await?; - send_lemmy_activity(context, delete, &actor, inboxes, true).await?; - Ok(()) - } + let inboxes = remote_instance_inboxes(&mut 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 diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index c571ac22b..535a2af1a 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -19,7 +19,6 @@ use crate::{ activities::deletion::{delete::Delete, undo_delete::UndoDelete}, InCommunity, }, - SendActivity, }; use activitypub_federation::{ config::Data, @@ -28,14 +27,9 @@ use activitypub_federation::{ protocol::verification::verify_domains_match, traits::{Actor, Object}, }; -use lemmy_api_common::{ - community::{CommunityResponse, DeleteCommunity, RemoveCommunity}, - context::LemmyContext, - post::{DeletePost, PostResponse, RemovePost}, - private_message::{DeletePrivateMessage, PrivateMessageResponse}, - utils::local_user_view_from_jwt, -}; +use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ + newtypes::CommunityId, source::{ comment::{Comment, CommentUpdateForm}, community::{Community, CommunityUpdateForm}, @@ -53,122 +47,6 @@ pub mod delete; pub mod delete_user; pub mod undo_delete; -#[async_trait::async_trait] -impl SendActivity for DeletePost { - type Response = PostResponse; - - async fn send_activity( - request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut context.pool(), response.post_view.community.id).await?; - let deletable = DeletableObjects::Post(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] -impl SendActivity for RemovePost { - type Response = PostResponse; - - async fn send_activity( - request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut context.pool(), response.post_view.community.id).await?; - let deletable = DeletableObjects::Post(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] -impl SendActivity for DeletePrivateMessage { - type Response = PrivateMessageResponse; - - async fn send_activity( - request: &Self, - response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).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] -impl SendActivity for DeleteCommunity { - type Response = CommunityResponse; - - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut context.pool(), request.community_id).await?; - let deletable = DeletableObjects::Community(community.clone().into()); - send_apub_delete_in_community( - local_user_view.person, - community, - deletable, - None, - request.deleted, - context, - ) - .await - } -} - -#[async_trait::async_trait] -impl SendActivity for RemoveCommunity { - type Response = CommunityResponse; - - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut context.pool(), request.community_id).await?; - let deletable = DeletableObjects::Community(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)] @@ -200,12 +78,44 @@ pub(crate) async fn send_apub_delete_in_community( .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)] -async fn send_apub_delete_private_message( +pub(crate) async fn send_apub_delete_in_community_new( + actor: Person, + community_id: CommunityId, + object: DeletableObjects, + reason: Option, + deleted: bool, + context: Data, +) -> Result<(), LemmyError> { + let community = Community::read(&mut context.pool(), community_id).await?; + let actor = ApubPerson::from(actor); + let is_mod_action = reason.is_some(); + let activity = if deleted { + let delete = Delete::new(&actor, object, public(), Some(&community), reason, &context)?; + AnnouncableActivities::Delete(delete) + } else { + let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, &context)?; + AnnouncableActivities::UndoDelete(undo) + }; + send_activity_in_community( + activity, + &actor, + &community.into(), + vec![], + is_mod_action, + &context, + ) + .await +} + +#[tracing::instrument(skip_all)] +pub(crate) async fn send_apub_delete_private_message( actor: &ApubPerson, pm: PrivateMessage, deleted: bool, - context: &Data, + context: Data, ) -> Result<(), LemmyError> { let recipient_id = pm.recipient_id; let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id) @@ -215,11 +125,11 @@ async fn send_apub_delete_private_message( let deletable = DeletableObjects::PrivateMessage(pm.into()); let inbox = vec![recipient.shared_inbox_or_inbox()]; if deleted { - let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?; - send_lemmy_activity(context, delete, actor, inbox, true).await?; + let delete = Delete::new(actor, deletable, recipient.id(), None, None, &context)?; + send_lemmy_activity(&context, delete, actor, inbox, true).await?; } else { - let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?; - send_lemmy_activity(context, undo, actor, inbox, true).await?; + let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, &context)?; + send_lemmy_activity(&context, undo, actor, inbox, true).await?; }; Ok(()) } diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 2f0f5037a..d64041b94 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -8,12 +8,7 @@ use crate::{ fetcher::user_or_community::UserOrCommunity, insert_received_activity, objects::{community::ApubCommunity, person::ApubPerson}, - protocol::activities::following::{ - accept::AcceptFollow, - follow::Follow, - undo_follow::UndoFollow, - }, - SendActivity, + protocol::activities::following::{accept::AcceptFollow, follow::Follow}, }; use activitypub_federation::{ config::Data, @@ -21,17 +16,13 @@ use activitypub_federation::{ protocol::verification::verify_urls_match, traits::{ActivityHandler, Actor}, }; -use lemmy_api_common::{ - community::{BlockCommunity, BlockCommunityResponse}, - context::LemmyContext, - utils::local_user_view_from_jwt, -}; +use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ - community::{Community, CommunityFollower, CommunityFollowerForm}, + community::{CommunityFollower, CommunityFollowerForm}, person::{PersonFollower, PersonFollowerForm}, }, - traits::{Crud, Followable}, + traits::Followable, }; use lemmy_utils::error::LemmyError; use url::Url; @@ -128,18 +119,3 @@ impl ActivityHandler for Follow { AcceptFollow::send(self, context).await } } - -#[async_trait::async_trait] -impl SendActivity for BlockCommunity { - type Response = BlockCommunityResponse; - - async fn send_activity( - request: &Self, - _response: &Self::Response, - context: &Data, - ) -> Result<(), LemmyError> { - let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; - let community = Community::read(&mut context.pool(), request.community_id).await?; - UndoFollow::send(&local_user_view.person.into(), &community.into(), context).await - } -} diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 780755766..885abc603 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -1,11 +1,25 @@ use self::following::send_follow_community; use crate::{ activities::{ - deletion::{send_apub_delete_in_community, DeletableObjects}, + block::{send_ban_from_community, send_ban_from_site}, + community::{ + collection_add::{send_add_mod_to_community, send_feature_post}, + lock_page::send_lock_post, + update::send_update_community, + }, + create_or_update::private_message::send_create_or_update_pm, + deletion::{ + delete_user::delete_user, + send_apub_delete_in_community, + send_apub_delete_in_community_new, + send_apub_delete_private_message, + DeletableObjects, + }, voting::send_like_activity, }, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::{ + community::report::Report, create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage}, CreateOrUpdateType, }, @@ -229,14 +243,46 @@ pub async fn match_outgoing_activities( let fed_task = async { use SendActivityData::*; match data { - CreatePost(post) | UpdatePost(post) => { + CreatePost(post) => { let creator_id = post.creator_id; CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await } - CreateComment(comment) | UpdateComment(comment) => { + UpdatePost(post) => { + let creator_id = post.creator_id; + CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Update, context).await + } + DeletePost(post, person, data) => { + send_apub_delete_in_community_new( + person, + post.community_id, + DeletableObjects::Post(post.into()), + None, + data.deleted, + context, + ) + .await + } + RemovePost(post, person, data) => { + send_apub_delete_in_community_new( + person, + post.community_id, + DeletableObjects::Post(post.into()), + data.reason.or_else(|| Some(String::new())), + data.removed, + context, + ) + .await + } + LockPost(post, actor, locked) => send_lock_post(post, actor, locked, context).await, + FeaturePost(post, actor, featured) => send_feature_post(post, actor, featured, context).await, + CreateComment(comment) => { let creator_id = comment.creator_id; CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await } + UpdateComment(comment) => { + let creator_id = comment.creator_id; + CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Update, context).await + } DeleteComment(comment, actor, community) => { let is_deleted = comment.deleted; let deletable = DeletableObjects::Comment(comment.into()); @@ -251,9 +297,46 @@ pub async fn match_outgoing_activities( LikePostOrComment(object_id, person, community, score) => { send_like_activity(object_id, person, community, score, context).await } - SendActivityData::FollowCommunity(community, person, follow) => { + FollowCommunity(community, person, follow) => { send_follow_community(community, person, follow, &context).await } + UpdateCommunity(actor, community) => send_update_community(community, actor, context).await, + DeleteCommunity(actor, community, removed) => { + let deletable = DeletableObjects::Community(community.clone().into()); + send_apub_delete_in_community(actor, community, deletable, None, removed, &context).await + } + RemoveCommunity(actor, community, reason, removed) => { + let deletable = DeletableObjects::Community(community.clone().into()); + send_apub_delete_in_community( + actor, + community, + deletable, + reason.clone().or_else(|| Some(String::new())), + removed, + &context, + ) + .await + } + AddModToCommunity(actor, community_id, updated_mod_id, added) => { + send_add_mod_to_community(actor, community_id, updated_mod_id, added, context).await + } + BanFromCommunity(mod_, community_id, target, data) => { + send_ban_from_community(mod_, community_id, target, data, context).await + } + BanFromSite(mod_, target, data) => send_ban_from_site(mod_, target, data, context).await, + CreatePrivateMessage(pm) => { + send_create_or_update_pm(pm, CreateOrUpdateType::Create, context).await + } + UpdatePrivateMessage(pm) => { + send_create_or_update_pm(pm, CreateOrUpdateType::Update, context).await + } + DeletePrivateMessage(person, pm, deleted) => { + send_apub_delete_private_message(&person.into(), pm, deleted, context).await + } + DeleteUser(person) => delete_user(person, context).await, + CreateReport(url, actor, community, reason) => { + Report::send(ObjectId::from(url), actor, community, reason, context).await + } } }; if *SYNCHRONOUS_FEDERATION { diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index dccf18dd4..f4a137d9a 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -1,33 +1,30 @@ use actix_web::{guard, web, Error, HttpResponse, Result}; use lemmy_api::{ comment::{distinguish::distinguish_comment, like::like_comment, save::save_comment}, - comment_report::{list::list_comment_reports, resolve::resolve_comment_report}, - community::follow::follow_community, - local_user::notifications::mark_reply_read::mark_reply_as_read, - post::like::like_post, + comment_report::{ + create::create_comment_report, + list::list_comment_reports, + resolve::resolve_comment_report, + }, + community::{ + add_mod::add_mod_to_community, + ban::ban_from_community, + block::block_community, + follow::follow_community, + hide::hide_community, + }, + local_user::{ban_person::ban_from_site, notifications::mark_reply_read::mark_reply_as_read}, + post::{feature::feature_post, like::like_post, lock::lock_post}, + post_report::create::create_post_report, Perform, }; use lemmy_api_common::{ - comment::CreateCommentReport, - community::{ - AddModToCommunity, - BanFromCommunity, - BlockCommunity, - CreateCommunity, - DeleteCommunity, - EditCommunity, - HideCommunity, - RemoveCommunity, - TransferCommunity, - }, + community::TransferCommunity, context::LemmyContext, - custom_emoji::{CreateCustomEmoji, DeleteCustomEmoji, EditCustomEmoji}, person::{ AddAdmin, - BanPerson, BlockPerson, ChangePassword, - DeleteAccount, GetBannedPersons, GetCaptcha, GetPersonMentions, @@ -39,27 +36,12 @@ use lemmy_api_common::{ MarkPersonMentionAsRead, PasswordChangeAfterReset, PasswordReset, - Register, SaveUserSettings, VerifyEmail, }, - post::{ - CreatePostReport, - DeletePost, - FeaturePost, - GetSiteMetadata, - ListPostReports, - LockPost, - MarkPostAsRead, - RemovePost, - ResolvePostReport, - SavePost, - }, + post::{GetSiteMetadata, ListPostReports, MarkPostAsRead, ResolvePostReport, SavePost}, private_message::{ - CreatePrivateMessage, CreatePrivateMessageReport, - DeletePrivateMessage, - EditPrivateMessage, ListPrivateMessageReports, MarkPrivateMessageAsRead, ResolvePrivateMessageReport, @@ -85,11 +67,33 @@ use lemmy_api_crud::{ remove::remove_comment, update::update_comment, }, - community::list::list_communities, - post::{create::create_post, read::get_post, update::update_post}, - private_message::read::get_private_message, + community::{ + create::create_community, + delete::delete_community, + list::list_communities, + remove::remove_community, + update::update_community, + }, + custom_emoji::{ + create::create_custom_emoji, + delete::delete_custom_emoji, + update::update_custom_emoji, + }, + post::{ + create::create_post, + delete::delete_post, + read::get_post, + remove::remove_post, + update::update_post, + }, + private_message::{ + create::create_private_message, + delete::delete_private_message, + read::get_private_message, + update::update_private_message, + }, site::{create::create_site, read::get_site, update::update_site}, - PerformCrud, + user::{create::register, delete::delete_account}, }; use lemmy_apub::{ api::{ @@ -137,29 +141,23 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { web::resource("/community") .guard(guard::Post()) .wrap(rate_limit.register()) - .route(web::post().to(route_post_crud::)), + .route(web::post().to(create_community)), ) .service( web::scope("/community") .wrap(rate_limit.message()) .route("", web::get().to(get_community)) - .route("", web::put().to(route_post_crud::)) - .route("/hide", web::put().to(route_post::)) + .route("", web::put().to(update_community)) + .route("/hide", web::put().to(hide_community)) .route("/list", web::get().to(list_communities)) .route("/follow", web::post().to(follow_community)) - .route("/block", web::post().to(route_post::)) - .route( - "/delete", - web::post().to(route_post_crud::), - ) + .route("/block", web::post().to(block_community)) + .route("/delete", web::post().to(delete_community)) // Mod Actions - .route( - "/remove", - web::post().to(route_post_crud::), - ) + .route("/remove", web::post().to(remove_community)) .route("/transfer", web::post().to(route_post::)) - .route("/ban_user", web::post().to(route_post::)) - .route("/mod", web::post().to(route_post::)), + .route("/ban_user", web::post().to(ban_from_community)) + .route("/mod", web::post().to(add_mod_to_community)), ) .service( web::scope("/federated_instances") @@ -179,18 +177,18 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { .wrap(rate_limit.message()) .route("", web::get().to(get_post)) .route("", web::put().to(update_post)) - .route("/delete", web::post().to(route_post_crud::)) - .route("/remove", web::post().to(route_post_crud::)) + .route("/delete", web::post().to(delete_post)) + .route("/remove", web::post().to(remove_post)) .route( "/mark_as_read", web::post().to(route_post::), ) - .route("/lock", web::post().to(route_post::)) - .route("/feature", web::post().to(route_post::)) + .route("/lock", web::post().to(lock_post)) + .route("/feature", web::post().to(feature_post)) .route("/list", web::get().to(list_posts)) .route("/like", web::post().to(like_post)) .route("/save", web::put().to(route_post::)) - .route("/report", web::post().to(route_post::)) + .route("/report", web::post().to(create_post_report)) .route( "/report/resolve", web::put().to(route_post::), @@ -221,7 +219,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { .route("/like", web::post().to(like_comment)) .route("/save", web::put().to(save_comment)) .route("/list", web::get().to(list_comments)) - .route("/report", web::post().to(route_post::)) + .route("/report", web::post().to(create_comment_report)) .route("/report/resolve", web::put().to(resolve_comment_report)) .route("/report/list", web::get().to(list_comment_reports)), ) @@ -230,12 +228,9 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { web::scope("/private_message") .wrap(rate_limit.message()) .route("/list", web::get().to(get_private_message)) - .route("", web::post().to(route_post_crud::)) - .route("", web::put().to(route_post_crud::)) - .route( - "/delete", - web::post().to(route_post_crud::), - ) + .route("", web::post().to(create_private_message)) + .route("", web::put().to(update_private_message)) + .route("/delete", web::post().to(delete_private_message)) .route( "/mark_as_read", web::post().to(route_post::), @@ -260,7 +255,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { web::resource("/user/register") .guard(guard::Post()) .wrap(rate_limit.register()) - .route(web::post().to(route_post_crud::)), + .route(web::post().to(register)), ) .service( // Handle captcha separately @@ -280,15 +275,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { ) .route("/replies", web::get().to(route_get::)) // Admin action. I don't like that it's in /user - .route("/ban", web::post().to(route_post::)) + .route("/ban", web::post().to(ban_from_site)) .route("/banned", web::get().to(route_get::)) .route("/block", web::post().to(route_post::)) // Account actions. I don't like that they're in /user maybe /accounts .route("/login", web::post().to(route_post::)) - .route( - "/delete_account", - web::post().to(route_post_crud::), - ) + .route("/delete_account", web::post().to(delete_account)) .route( "/password_reset", web::post().to(route_post::), @@ -343,12 +335,9 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { .service( web::scope("/custom_emoji") .wrap(rate_limit.message()) - .route("", web::post().to(route_post_crud::)) - .route("", web::put().to(route_post_crud::)) - .route( - "/delete", - web::post().to(route_post_crud::), - ), + .route("", web::post().to(create_custom_emoji)) + .route("", web::put().to(update_custom_emoji)) + .route("/delete", web::post().to(delete_custom_emoji)), ), ); } @@ -408,43 +397,3 @@ where { perform::(data.0, context, apub_data).await } - -async fn perform_crud<'a, Data>( - data: Data, - context: web::Data, - apub_data: activitypub_federation::config::Data, -) -> Result -where - Data: PerformCrud - + SendActivity::Response> - + Clone - + Deserialize<'a> - + Send - + 'static, -{ - let res = data.perform(&context).await?; - let res_clone = res.clone(); - let fed_task = async move { SendActivity::send_activity(&data, &res_clone, &apub_data).await }; - if *SYNCHRONOUS_FEDERATION { - fed_task.await?; - } else { - spawn_try_task(fed_task); - } - Ok(HttpResponse::Ok().json(&res)) -} - -async fn route_post_crud<'a, Data>( - data: web::Json, - context: web::Data, - apub_data: activitypub_federation::config::Data, -) -> Result -where - Data: PerformCrud - + SendActivity::Response> - + Clone - + Deserialize<'a> - + Send - + 'static, -{ - perform_crud::(data.0, context, apub_data).await -}