From 28d6370c359d368882d9337d1b32ec2c14987a08 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Tue, 17 Aug 2021 18:04:58 +0000 Subject: [PATCH] Rewrite delete activities (#1699) * Limit type/method visibility in apub code * Simplify db_queries traits by removing generics * Simplify delete activity implementation * Rewrite delete activities * Implement helper functions for websocket message sending * When receiving delete reason as empty string, change to none --- Cargo.lock | 2 + crates/api/src/comment.rs | 31 +- crates/api/src/post.rs | 69 ++--- crates/api/src/private_message.rs | 32 +-- crates/api_crud/src/comment/create.rs | 37 +-- crates/api_crud/src/comment/delete.rs | 136 ++++----- crates/api_crud/src/comment/update.rs | 37 +-- crates/api_crud/src/community/delete.rs | 104 +++---- crates/api_crud/src/community/mod.rs | 23 -- crates/api_crud/src/community/update.rs | 35 +-- crates/api_crud/src/post/create.rs | 26 +- crates/api_crud/src/post/delete.rs | 100 +++---- crates/api_crud/src/post/update.rs | 31 +- crates/api_crud/src/private_message/create.rs | 41 ++- crates/api_crud/src/private_message/delete.rs | 39 +-- crates/api_crud/src/private_message/update.rs | 41 +-- .../activities/comment/create_or_update.rs | 10 +- crates/apub/src/activities/comment/mod.rs | 37 +-- .../apub/src/activities/community/announce.rs | 16 +- crates/apub/src/activities/community/mod.rs | 30 +- .../apub/src/activities/community/update.rs | 16 +- crates/apub/src/activities/deletion/delete.rs | 269 +++++++++++------- crates/apub/src/activities/deletion/mod.rs | 207 ++++++++++++++ .../src/activities/deletion/undo_delete.rs | 201 +++++++------ .../src/activities/post/create_or_update.rs | 6 +- crates/apub/src/activities/post/mod.rs | 29 -- .../private_message/create_or_update.rs | 12 +- .../src/activities/private_message/delete.rs | 12 +- .../src/activities/private_message/mod.rs | 38 --- .../activities/private_message/undo_delete.rs | 7 +- crates/apub/src/activities/removal/remove.rs | 112 ++------ .../src/activities/removal/undo_remove.rs | 100 ++----- crates/apub/src/activities/send/comment.rs | 153 ---------- crates/apub/src/activities/send/community.rs | 118 +------- crates/apub/src/activities/send/mod.rs | 2 - crates/apub/src/activities/send/post.rs | 139 --------- crates/apub/src/activities/voting/mod.rs | 37 +-- crates/apub/src/extensions/context.rs | 2 +- crates/apub/src/extensions/signatures.rs | 2 +- crates/apub/src/fetcher/community.rs | 2 +- crates/apub/src/fetcher/mod.rs | 2 +- crates/apub/src/fetcher/objects.rs | 6 +- crates/apub/src/fetcher/person.rs | 2 +- crates/apub/src/http/inbox_enums.rs | 19 +- crates/apub/src/lib.rs | 43 +-- crates/apub/src/migrations.rs | 12 + crates/apub/src/objects/mod.rs | 4 +- crates/db_queries/src/lib.rs | 69 +++-- crates/db_queries/src/source/activity.rs | 4 +- crates/db_queries/src/source/comment.rs | 14 +- .../db_queries/src/source/comment_report.rs | 3 +- crates/db_queries/src/source/community.rs | 16 +- crates/db_queries/src/source/local_user.rs | 4 +- crates/db_queries/src/source/moderator.rs | 36 ++- .../src/source/password_reset_request.rs | 4 +- crates/db_queries/src/source/person.rs | 7 +- .../db_queries/src/source/person_mention.rs | 4 +- crates/db_queries/src/source/post.rs | 17 +- crates/db_queries/src/source/post_report.rs | 3 +- .../db_queries/src/source/private_message.rs | 7 +- crates/db_queries/src/source/site.rs | 4 +- crates/websocket/Cargo.toml | 2 + crates/websocket/src/lib.rs | 1 + crates/websocket/src/messages.rs | 4 +- crates/websocket/src/send.rs | 162 +++++++++++ 65 files changed, 1170 insertions(+), 1620 deletions(-) delete mode 100644 crates/apub/src/activities/send/comment.rs delete mode 100644 crates/apub/src/activities/send/post.rs create mode 100644 crates/websocket/src/send.rs diff --git a/Cargo.lock b/Cargo.lock index 34431d3ad..844d49e10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1944,6 +1944,8 @@ dependencies = [ "lemmy_api_common", "lemmy_db_queries", "lemmy_db_schema", + "lemmy_db_views", + "lemmy_db_views_actor", "lemmy_utils", "log", "rand 0.8.4", diff --git a/crates/api/src/comment.rs b/crates/api/src/comment.rs index f92e679f7..06510251f 100644 --- a/crates/api/src/comment.rs +++ b/crates/api/src/comment.rs @@ -18,7 +18,7 @@ use lemmy_db_queries::{source::comment::Comment_, Likeable, Saveable}; use lemmy_db_schema::{source::comment::*, LocalUserId}; use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView}; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation}; +use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation}; use std::convert::TryInto; #[async_trait::async_trait(?Send)] @@ -206,26 +206,15 @@ impl Perform for CreateCommentLike { .await?; } - // Have to refetch the comment to get the current state - let comment_id = data.comment_id; - let person_id = local_user_view.person.id; - let liked_comment = blocking(context.pool(), move |conn| { - CommentView::read(conn, comment_id, Some(person_id)) - }) - .await??; - - let res = CommentResponse { - comment_view: liked_comment, - recipient_ids, - form_id: None, - }; - - context.chat_server().do_send(SendComment { - op: UserOperation::CreateCommentLike, - comment: res.clone(), + send_comment_ws_message( + data.comment_id, + UserOperation::CreateCommentLike, websocket_id, - }); - - Ok(res) + None, + Some(local_user_view.person.id), + recipient_ids, + context, + ) + .await } } diff --git a/crates/api/src/post.rs b/crates/api/src/post.rs index 684f64798..3de027313 100644 --- a/crates/api/src/post.rs +++ b/crates/api/src/post.rs @@ -24,7 +24,7 @@ use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable}; use lemmy_db_schema::source::{moderator::*, post::*}; use lemmy_db_views::post_view::PostView; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation}; +use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation}; use std::convert::TryInto; #[async_trait::async_trait(?Send)] @@ -96,23 +96,14 @@ impl Perform for CreatePostLike { // Mark the post as read mark_post_as_read(person_id, post_id, context.pool()).await?; - let post_id = data.post_id; - let person_id = local_user_view.person.id; - let post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, post_id, Some(person_id)) - }) - .await? - .map_err(|_| ApiError::err("couldnt_find_post"))?; - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op: UserOperation::CreatePostLike, - post: res.clone(), + send_post_ws_message( + data.post_id, + UserOperation::CreatePostLike, websocket_id, - }); - - Ok(res) + Some(local_user_view.person.id), + context, + ) + .await } } @@ -171,22 +162,14 @@ impl Perform for LockPost { ) .await?; - // Refetch the post - let post_id = data.post_id; - let post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, post_id, Some(local_user_view.person.id)) - }) - .await??; - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op: UserOperation::LockPost, - post: res.clone(), + send_post_ws_message( + data.post_id, + UserOperation::LockPost, websocket_id, - }); - - Ok(res) + Some(local_user_view.person.id), + context, + ) + .await } } @@ -249,22 +232,14 @@ impl Perform for StickyPost { ) .await?; - // Refetch the post - let post_id = data.post_id; - let post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, post_id, Some(local_user_view.person.id)) - }) - .await??; - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op: UserOperation::StickyPost, - post: res.clone(), + send_post_ws_message( + data.post_id, + UserOperation::StickyPost, websocket_id, - }); - - Ok(res) + Some(local_user_view.person.id), + context, + ) + .await } } diff --git a/crates/api/src/private_message.rs b/crates/api/src/private_message.rs index 54bc3eb0c..f23bf71cd 100644 --- a/crates/api/src/private_message.rs +++ b/crates/api/src/private_message.rs @@ -7,9 +7,8 @@ use lemmy_api_common::{ }; use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud}; use lemmy_db_schema::source::private_message::PrivateMessage; -use lemmy_db_views::{local_user_view::LocalUserView, private_message_view::PrivateMessageView}; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperation}; +use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperation}; #[async_trait::async_trait(?Send)] impl Perform for MarkPrivateMessageAsRead { @@ -43,32 +42,7 @@ impl Perform for MarkPrivateMessageAsRead { .map_err(|_| ApiError::err("couldnt_update_private_message"))?; // No need to send an apub update - let private_message_id = data.private_message_id; - let private_message_view = blocking(context.pool(), move |conn| { - PrivateMessageView::read(conn, private_message_id) - }) - .await??; - - let res = PrivateMessageResponse { - private_message_view, - }; - - // Send notifications to the local recipient, if one exists - let recipient_id = orig_private_message.recipient_id; - if let Ok(local_recipient) = blocking(context.pool(), move |conn| { - LocalUserView::read_person(conn, recipient_id) - }) - .await? - { - let local_recipient_id = local_recipient.local_user.id; - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperation::MarkPrivateMessageAsRead, - response: res.clone(), - local_recipient_id, - websocket_id, - }); - } - - Ok(res) + let op = UserOperation::MarkPrivateMessageAsRead; + send_pm_ws_message(data.private_message_id, op, websocket_id, context).await } } diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index 5a6fec81c..a479969a7 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -20,14 +20,13 @@ use lemmy_apub::{ }; use lemmy_db_queries::{source::comment::Comment_, Crud, Likeable}; use lemmy_db_schema::source::comment::*; -use lemmy_db_views::comment_view::CommentView; use lemmy_utils::{ utils::{remove_slurs, scrape_text_for_mentions}, ApiError, ConnectionId, LemmyError, }; -use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for CreateComment { @@ -137,37 +136,25 @@ impl PerformCrud for CreateComment { ) .await?; - let person_id = local_user_view.person.id; - let mut comment_view = blocking(context.pool(), move |conn| { - CommentView::read(conn, inserted_comment.id, Some(person_id)) - }) - .await??; - // If its a comment to yourself, mark it as read - let comment_id = comment_view.comment.id; - if local_user_view.person.id == comment_view.get_recipient_id() { + if local_user_view.person.id == inserted_comment.creator_id { + let comment_id = inserted_comment.id; blocking(context.pool(), move |conn| { Comment::update_read(conn, comment_id, true) }) .await? .map_err(|_| ApiError::err("couldnt_update_comment"))?; - comment_view.comment.read = true; } - let mut res = CommentResponse { - comment_view, - recipient_ids, - form_id: data.form_id.to_owned(), - }; - - context.chat_server().do_send(SendComment { - op: UserOperationCrud::CreateComment, - comment: res.clone(), + send_comment_ws_message( + inserted_comment.id, + UserOperationCrud::CreateComment, websocket_id, - }); - - res.recipient_ids = Vec::new(); // Necessary to avoid doubles - - Ok(res) + data.form_id.to_owned(), + Some(local_user_view.person.id), + recipient_ids, + context, + ) + .await } } diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs index e2ec3080e..85917846e 100644 --- a/crates/api_crud/src/comment/delete.rs +++ b/crates/api_crud/src/comment/delete.rs @@ -8,12 +8,12 @@ use lemmy_api_common::{ is_mod_or_admin, send_local_notifs, }; -use lemmy_apub::ApubObjectType; -use lemmy_db_queries::{source::comment::Comment_, Crud, DeleteableOrRemoveable}; -use lemmy_db_schema::source::{comment::*, moderator::*}; +use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove}; +use lemmy_db_queries::{source::comment::Comment_, Crud}; +use lemmy_db_schema::source::{comment::*, community::Community, moderator::*, post::Post}; use lemmy_db_views::comment_view::CommentView; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for DeleteComment { @@ -47,63 +47,48 @@ impl PerformCrud for DeleteComment { // Do the delete let deleted = data.deleted; - let mut updated_comment = blocking(context.pool(), move |conn| { + let updated_comment = blocking(context.pool(), move |conn| { Comment::update_deleted(conn, comment_id, deleted) }) .await? .map_err(|_| ApiError::err("couldnt_update_comment"))?; // Send the apub message - if deleted { - updated_comment = updated_comment.blank_out_deleted_or_removed_info(); - updated_comment - .send_delete(&local_user_view.person, context) - .await?; - } else { - updated_comment - .send_undo_delete(&local_user_view.person, context) - .await?; - } - - // Refetch it - let comment_id = data.comment_id; - let person_id = local_user_view.person.id; - let mut comment_view = blocking(context.pool(), move |conn| { - CommentView::read(conn, comment_id, Some(person_id)) + let community = blocking(context.pool(), move |conn| { + Community::read(conn, orig_comment.post.community_id) }) .await??; + send_apub_delete( + &local_user_view.person, + &community, + updated_comment.ap_id.clone().into(), + deleted, + context, + ) + .await?; - // Blank out deleted or removed info - if deleted { - comment_view.comment = comment_view.comment.blank_out_deleted_or_removed_info(); - } - - // Build the recipients - let comment_view_2 = comment_view.clone(); - let mentions = vec![]; + let post_id = updated_comment.post_id; + let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let recipient_ids = send_local_notifs( - mentions, + vec![], updated_comment, local_user_view.person.clone(), - comment_view_2.post, + post, context.pool(), false, ) .await?; - let res = CommentResponse { - comment_view, - recipient_ids, - form_id: None, // TODO a comment delete might clear forms? - }; - - context.chat_server().do_send(SendComment { - op: UserOperationCrud::DeleteComment, - comment: res.clone(), + send_comment_ws_message( + data.comment_id, + UserOperationCrud::DeleteComment, websocket_id, - }); - - Ok(res) + None, // TODO a comment delete might clear forms? + Some(local_user_view.person.id), + recipient_ids, + context, + ) + .await } } @@ -142,7 +127,7 @@ impl PerformCrud for RemoveComment { // Do the remove let removed = data.removed; - let mut updated_comment = blocking(context.pool(), move |conn| { + let updated_comment = blocking(context.pool(), move |conn| { Comment::update_removed(conn, comment_id, removed) }) .await? @@ -161,56 +146,41 @@ impl PerformCrud for RemoveComment { .await??; // Send the apub message - if removed { - updated_comment = updated_comment.blank_out_deleted_or_removed_info(); - updated_comment - .send_remove(&local_user_view.person, context) - .await?; - } else { - updated_comment - .send_undo_remove(&local_user_view.person, context) - .await?; - } - - // Refetch it - let comment_id = data.comment_id; - let person_id = local_user_view.person.id; - let mut comment_view = blocking(context.pool(), move |conn| { - CommentView::read(conn, comment_id, Some(person_id)) + let community = blocking(context.pool(), move |conn| { + Community::read(conn, orig_comment.post.community_id) }) .await??; + send_apub_remove( + &local_user_view.person, + &community, + updated_comment.ap_id.clone().into(), + data.reason.clone().unwrap_or_else(|| "".to_string()), + removed, + context, + ) + .await?; - // Blank out deleted or removed info - if removed { - comment_view.comment = comment_view.comment.blank_out_deleted_or_removed_info(); - } - - // Build the recipients - let comment_view_2 = comment_view.clone(); - - let mentions = vec![]; + let post_id = updated_comment.post_id; + let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let recipient_ids = send_local_notifs( - mentions, + vec![], updated_comment, local_user_view.person.clone(), - comment_view_2.post, + post, context.pool(), false, ) .await?; - let res = CommentResponse { - comment_view, - recipient_ids, - form_id: None, // TODO maybe this might clear other forms - }; - - context.chat_server().do_send(SendComment { - op: UserOperationCrud::RemoveComment, - comment: res.clone(), + send_comment_ws_message( + data.comment_id, + UserOperationCrud::RemoveComment, websocket_id, - }); - - Ok(res) + None, // TODO maybe this might clear other forms + Some(local_user_view.person.id), + recipient_ids, + context, + ) + .await } } diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index d0a0e3971..ee2e7e28e 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -11,7 +11,7 @@ use lemmy_apub::activities::{ comment::create_or_update::CreateOrUpdateComment, CreateOrUpdateType, }; -use lemmy_db_queries::{source::comment::Comment_, DeleteableOrRemoveable}; +use lemmy_db_queries::source::comment::Comment_; use lemmy_db_schema::source::comment::*; use lemmy_db_views::comment_view::CommentView; use lemmy_utils::{ @@ -20,7 +20,7 @@ use lemmy_utils::{ ConnectionId, LemmyError, }; -use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for EditComment { @@ -83,30 +83,15 @@ impl PerformCrud for EditComment { ) .await?; - let comment_id = data.comment_id; - let person_id = local_user_view.person.id; - let mut comment_view = blocking(context.pool(), move |conn| { - CommentView::read(conn, comment_id, Some(person_id)) - }) - .await??; - - // Blank out deleted or removed info - if comment_view.comment.deleted || comment_view.comment.removed { - comment_view.comment = comment_view.comment.blank_out_deleted_or_removed_info(); - } - - let res = CommentResponse { - comment_view, - recipient_ids, - form_id: data.form_id.to_owned(), - }; - - context.chat_server().do_send(SendComment { - op: UserOperationCrud::EditComment, - comment: res.clone(), + send_comment_ws_message( + data.comment_id, + UserOperationCrud::EditComment, websocket_id, - }); - - Ok(res) + data.form_id.to_owned(), + None, + recipient_ids, + context, + ) + .await } } diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index 62fb0f38f..b957b7d6e 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -1,18 +1,15 @@ -use crate::{community::send_community_websocket, PerformCrud}; +use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt, is_admin}; -use lemmy_apub::CommunityType; -use lemmy_db_queries::{source::community::Community_, Crud, DeleteableOrRemoveable}; +use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove}; +use lemmy_db_queries::{source::community::Community_, Crud}; use lemmy_db_schema::source::{ community::*, moderator::{ModRemoveCommunity, ModRemoveCommunityForm}, }; -use lemmy_db_views_actor::{ - community_moderator_view::CommunityModeratorView, - community_view::CommunityView, -}; +use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_utils::{utils::naive_from_unix, ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for DeleteCommunity { @@ -48,39 +45,23 @@ impl PerformCrud for DeleteCommunity { .map_err(|_| ApiError::err("couldnt_update_community"))?; // Send apub messages - if deleted { - updated_community - .blank_out_deleted_or_removed_info() - .send_delete(local_user_view.person.to_owned(), context) - .await?; - } else { - updated_community - .send_undo_delete(local_user_view.person.to_owned(), context) - .await?; - } - - let community_id = data.community_id; - let person_id = local_user_view.person.id; - let mut community_view = blocking(context.pool(), move |conn| { - CommunityView::read(conn, community_id, Some(person_id)) - }) - .await??; - - // Blank out deleted or removed info - if deleted { - community_view.community = community_view.community.blank_out_deleted_or_removed_info(); - } - - let res = CommunityResponse { community_view }; - - send_community_websocket( - &res, + send_apub_delete( + &local_user_view.person, + &updated_community, + updated_community.actor_id.clone().into(), + deleted, context, - websocket_id, - UserOperationCrud::DeleteCommunity, - ); + ) + .await?; - Ok(res) + send_community_ws_message( + data.community_id, + UserOperationCrud::DeleteCommunity, + websocket_id, + Some(local_user_view.person.id), + context, + ) + .await } } @@ -123,36 +104,23 @@ impl PerformCrud for RemoveCommunity { .await??; // Apub messages - if removed { - updated_community - .blank_out_deleted_or_removed_info() - .send_remove(context) - .await?; - } else { - updated_community.send_undo_remove(context).await?; - } - - let community_id = data.community_id; - let person_id = local_user_view.person.id; - let mut community_view = blocking(context.pool(), move |conn| { - CommunityView::read(conn, community_id, Some(person_id)) - }) - .await??; - - // Blank out deleted or removed info - if removed { - community_view.community = community_view.community.blank_out_deleted_or_removed_info(); - } - - let res = CommunityResponse { community_view }; - - send_community_websocket( - &res, + send_apub_remove( + &local_user_view.person, + &updated_community, + updated_community.actor_id.clone().into(), + data.reason.clone().unwrap_or_else(|| "".to_string()), + removed, context, - websocket_id, - UserOperationCrud::RemoveCommunity, - ); + ) + .await?; - Ok(res) + send_community_ws_message( + data.community_id, + UserOperationCrud::RemoveCommunity, + websocket_id, + Some(local_user_view.person.id), + context, + ) + .await } } diff --git a/crates/api_crud/src/community/mod.rs b/crates/api_crud/src/community/mod.rs index 9098cb541..716832376 100644 --- a/crates/api_crud/src/community/mod.rs +++ b/crates/api_crud/src/community/mod.rs @@ -1,27 +1,4 @@ -use actix_web::web::Data; -use lemmy_api_common::community::CommunityResponse; -use lemmy_utils::ConnectionId; -use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperationCrud}; - mod create; mod delete; mod read; mod update; - -pub(in crate::community) fn send_community_websocket( - res: &CommunityResponse, - context: &Data, - websocket_id: Option, - op: UserOperationCrud, -) { - // Strip out the person id and subscribed when sending to others - let mut res_sent = res.clone(); - res_sent.community_view.subscribed = false; - - context.chat_server().do_send(SendCommunityRoomMessage { - op, - response: res_sent, - community_id: res.community_view.community.id, - websocket_id, - }); -} diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index 49de1cfd7..2a99c76db 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -1,4 +1,4 @@ -use crate::{community::send_community_websocket, PerformCrud}; +use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{ blocking, @@ -6,18 +6,15 @@ use lemmy_api_common::{ get_local_user_view_from_jwt, }; use lemmy_apub::CommunityType; -use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud, DeleteableOrRemoveable}; +use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud}; use lemmy_db_schema::{ naive_now, source::community::{Community, CommunityForm}, PersonId, }; -use lemmy_db_views_actor::{ - community_moderator_view::CommunityModeratorView, - community_view::CommunityView, -}; +use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_utils::{utils::check_slurs_opt, ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for EditCommunity { @@ -76,27 +73,7 @@ impl PerformCrud for EditCommunity { .send_update(local_user_view.person.to_owned(), context) .await?; - let community_id = data.community_id; - let person_id = local_user_view.person.id; - let mut community_view = blocking(context.pool(), move |conn| { - CommunityView::read(conn, community_id, Some(person_id)) - }) - .await??; - - // Blank out deleted or removed info - if community_view.community.deleted || community_view.community.removed { - community_view.community = community_view.community.blank_out_deleted_or_removed_info(); - } - - let res = CommunityResponse { community_view }; - - send_community_websocket( - &res, - context, - websocket_id, - UserOperationCrud::EditCommunity, - ); - - Ok(res) + let op = UserOperationCrud::EditCommunity; + send_community_ws_message(data.community_id, op, websocket_id, None, context).await } } diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index f041a00b1..28c739689 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -19,7 +19,6 @@ use lemmy_apub::{ }; use lemmy_db_queries::{source::post::Post_, Crud, Likeable}; use lemmy_db_schema::source::post::*; -use lemmy_db_views::post_view::PostView; use lemmy_utils::{ request::fetch_iframely_and_pictrs_data, utils::{check_slurs, check_slurs_opt, clean_url_params, is_valid_post_title}, @@ -27,7 +26,7 @@ use lemmy_utils::{ ConnectionId, LemmyError, }; -use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for CreatePost { @@ -129,22 +128,13 @@ impl PerformCrud for CreatePost { ) .await?; - // Refetch the view - let inserted_post_id = inserted_post.id; - let post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, inserted_post_id, Some(local_user_view.person.id)) - }) - .await? - .map_err(|_| ApiError::err("couldnt_find_post"))?; - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op: UserOperationCrud::CreatePost, - post: res.clone(), + send_post_ws_message( + inserted_post.id, + UserOperationCrud::CreatePost, websocket_id, - }); - - Ok(res) + Some(local_user_view.person.id), + context, + ) + .await } } diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index cf48757d3..c0ef35122 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -7,12 +7,11 @@ use lemmy_api_common::{ is_mod_or_admin, post::*, }; -use lemmy_apub::ApubObjectType; -use lemmy_db_queries::{source::post::Post_, Crud, DeleteableOrRemoveable}; -use lemmy_db_schema::source::{moderator::*, post::*}; -use lemmy_db_views::post_view::PostView; +use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove}; +use lemmy_db_queries::{source::post::Post_, Crud}; +use lemmy_db_schema::source::{community::Community, moderator::*, post::*}; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for DeletePost { @@ -50,37 +49,27 @@ impl PerformCrud for DeletePost { .await??; // apub updates - if deleted { - updated_post - .blank_out_deleted_or_removed_info() - .send_delete(&local_user_view.person, context) - .await?; - } else { - updated_post - .send_undo_delete(&local_user_view.person, context) - .await?; - } - - // Refetch the post - let post_id = data.post_id; - let mut post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, post_id, Some(local_user_view.person.id)) + let community = blocking(context.pool(), move |conn| { + Community::read(conn, orig_post.community_id) }) .await??; + send_apub_delete( + &local_user_view.person, + &community, + updated_post.ap_id.into(), + deleted, + context, + ) + .await?; - if deleted { - post_view.post = post_view.post.blank_out_deleted_or_removed_info(); - } - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op: UserOperationCrud::DeletePost, - post: res.clone(), + send_post_ws_message( + data.post_id, + UserOperationCrud::DeletePost, websocket_id, - }); - - Ok(res) + Some(local_user_view.person.id), + context, + ) + .await } } @@ -135,38 +124,27 @@ impl PerformCrud for RemovePost { .await??; // apub updates - if removed { - updated_post - .blank_out_deleted_or_removed_info() - .send_remove(&local_user_view.person, context) - .await?; - } else { - updated_post - .send_undo_remove(&local_user_view.person, context) - .await?; - } - - // Refetch the post - let post_id = data.post_id; - let person_id = local_user_view.person.id; - let mut post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, post_id, Some(person_id)) + let community = blocking(context.pool(), move |conn| { + Community::read(conn, orig_post.community_id) }) .await??; + send_apub_remove( + &local_user_view.person, + &community, + updated_post.ap_id.into(), + data.reason.clone().unwrap_or_else(|| "".to_string()), + removed, + context, + ) + .await?; - // Blank out deleted or removed info - if removed { - post_view.post = post_view.post.blank_out_deleted_or_removed_info(); - } - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op: UserOperationCrud::RemovePost, - post: res.clone(), + send_post_ws_message( + data.post_id, + UserOperationCrud::RemovePost, websocket_id, - }); - - Ok(res) + Some(local_user_view.person.id), + context, + ) + .await } } diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index c9fe7e332..4b63ebd2c 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -2,9 +2,8 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{blocking, check_community_ban, get_local_user_view_from_jwt, post::*}; use lemmy_apub::activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType}; -use lemmy_db_queries::{source::post::Post_, Crud, DeleteableOrRemoveable}; +use lemmy_db_queries::{source::post::Post_, Crud}; use lemmy_db_schema::{naive_now, source::post::*}; -use lemmy_db_views::post_view::PostView; use lemmy_utils::{ request::fetch_iframely_and_pictrs_data, utils::{check_slurs_opt, clean_url_params, is_valid_post_title}, @@ -12,7 +11,7 @@ use lemmy_utils::{ ConnectionId, LemmyError, }; -use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for EditPost { @@ -100,25 +99,13 @@ impl PerformCrud for EditPost { ) .await?; - let post_id = data.post_id; - let mut post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, post_id, Some(local_user_view.person.id)) - }) - .await??; - - // Blank out deleted info - if post_view.post.deleted || post_view.post.removed { - post_view.post = post_view.post.blank_out_deleted_or_removed_info(); - } - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op: UserOperationCrud::EditPost, - post: res.clone(), + send_post_ws_message( + data.post_id, + UserOperationCrud::EditPost, websocket_id, - }); - - Ok(res) + Some(local_user_view.person.id), + context, + ) + .await } } diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index 1da6466df..aa0bc8f4c 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -16,9 +16,9 @@ use lemmy_apub::{ }; use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud}; use lemmy_db_schema::source::private_message::{PrivateMessage, PrivateMessageForm}; -use lemmy_db_views::{local_user_view::LocalUserView, private_message_view::PrivateMessageView}; +use lemmy_db_views::local_user_view::LocalUserView; use lemmy_utils::{utils::remove_slurs, ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for CreatePrivateMessage { @@ -78,36 +78,27 @@ impl PerformCrud for CreatePrivateMessage { ) .await?; - let private_message_view = blocking(context.pool(), move |conn| { - PrivateMessageView::read(conn, inserted_private_message.id) - }) - .await??; + let res = send_pm_ws_message( + inserted_private_message.id, + UserOperationCrud::CreatePrivateMessage, + websocket_id, + context, + ) + .await?; - let res = PrivateMessageResponse { - private_message_view, - }; - - // Send notifications to the local recipient, if one exists - let recipient_id = data.recipient_id; - if let Ok(local_recipient) = blocking(context.pool(), move |conn| { - LocalUserView::read_person(conn, recipient_id) - }) - .await? - { + // Send email to the local recipient, if one exists + if res.private_message_view.recipient.local { + let recipient_id = data.recipient_id; + let local_recipient = blocking(context.pool(), move |conn| { + LocalUserView::read_person(conn, recipient_id) + }) + .await??; send_email_to_user( &local_recipient, "Private Message from", "Private Message", &content_slurs_removed, ); - - let local_recipient_id = local_recipient.local_user.id; - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperationCrud::CreatePrivateMessage, - response: res.clone(), - local_recipient_id, - websocket_id, - }); } Ok(res) diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs index 78dcd9311..6cd2b44f2 100644 --- a/crates/api_crud/src/private_message/delete.rs +++ b/crates/api_crud/src/private_message/delete.rs @@ -11,9 +11,8 @@ use lemmy_apub::activities::private_message::{ }; use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud, DeleteableOrRemoveable}; use lemmy_db_schema::source::private_message::PrivateMessage; -use lemmy_db_views::{local_user_view::LocalUserView, private_message_view::PrivateMessageView}; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for DeletePrivateMessage { @@ -59,39 +58,7 @@ impl PerformCrud for DeletePrivateMessage { .await?; } - let private_message_id = data.private_message_id; - let mut private_message_view = blocking(context.pool(), move |conn| { - PrivateMessageView::read(conn, private_message_id) - }) - .await??; - - // Blank out deleted or removed info - if deleted { - private_message_view.private_message = private_message_view - .private_message - .blank_out_deleted_or_removed_info(); - } - - let res = PrivateMessageResponse { - private_message_view, - }; - - // Send notifications to the local recipient, if one exists - let recipient_id = orig_private_message.recipient_id; - if let Ok(local_recipient) = blocking(context.pool(), move |conn| { - LocalUserView::read_person(conn, recipient_id) - }) - .await? - { - let local_recipient_id = local_recipient.local_user.id; - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperationCrud::DeletePrivateMessage, - response: res.clone(), - local_recipient_id, - websocket_id, - }); - } - - Ok(res) + let op = UserOperationCrud::DeletePrivateMessage; + send_pm_ws_message(data.private_message_id, op, websocket_id, context).await } } diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index 5f5f7b845..72ca4fbbf 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -9,11 +9,10 @@ use lemmy_apub::activities::{ private_message::create_or_update::CreateOrUpdatePrivateMessage, CreateOrUpdateType, }; -use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud, DeleteableOrRemoveable}; +use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud}; use lemmy_db_schema::source::private_message::PrivateMessage; -use lemmy_db_views::{local_user_view::LocalUserView, private_message_view::PrivateMessageView}; use lemmy_utils::{utils::remove_slurs, ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; #[async_trait::async_trait(?Send)] impl PerformCrud for EditPrivateMessage { @@ -55,39 +54,7 @@ impl PerformCrud for EditPrivateMessage { ) .await?; - let private_message_id = data.private_message_id; - let mut private_message_view = blocking(context.pool(), move |conn| { - PrivateMessageView::read(conn, private_message_id) - }) - .await??; - - // Blank out deleted or removed info - if private_message_view.private_message.deleted { - private_message_view.private_message = private_message_view - .private_message - .blank_out_deleted_or_removed_info(); - } - - let res = PrivateMessageResponse { - private_message_view, - }; - - // Send notifications to the local recipient, if one exists - let recipient_id = orig_private_message.recipient_id; - if let Ok(local_recipient) = blocking(context.pool(), move |conn| { - LocalUserView::read_person(conn, recipient_id) - }) - .await? - { - let local_recipient_id = local_recipient.local_user.id; - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperationCrud::EditPrivateMessage, - response: res.clone(), - local_recipient_id, - websocket_id, - }); - } - - Ok(res) + let op = UserOperationCrud::EditPrivateMessage; + send_pm_ws_message(data.private_message_id, op, websocket_id, context).await } } diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index bfc1b7fe6..cb9b23497 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -1,6 +1,6 @@ use crate::{ activities::{ - comment::{collect_non_local_mentions, get_notif_recipients, send_websocket_message}, + comment::{collect_non_local_mentions, get_notif_recipients}, community::announce::AnnouncableActivities, extract_community, generate_activity_id, @@ -24,7 +24,7 @@ use lemmy_apub_lib::{ use lemmy_db_queries::Crud; use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; use serde::{Deserialize, Serialize}; use url::Url; @@ -116,7 +116,11 @@ impl ActivityHandler for CreateOrUpdateComment { CreateOrUpdateType::Create => UserOperationCrud::CreateComment, CreateOrUpdateType::Update => UserOperationCrud::EditComment, }; - send_websocket_message(comment.id, recipients, notif_type, context).await + send_comment_ws_message( + comment.id, notif_type, None, None, None, recipients, context, + ) + .await?; + Ok(()) } fn common(&self) -> &ActivityCommonFields { diff --git a/crates/apub/src/activities/comment/mod.rs b/crates/apub/src/activities/comment/mod.rs index e7499718c..228a5f804 100644 --- a/crates/apub/src/activities/comment/mod.rs +++ b/crates/apub/src/activities/comment/mod.rs @@ -5,21 +5,19 @@ use activitystreams::{ }; use anyhow::anyhow; use itertools::Itertools; -use lemmy_api_common::{blocking, comment::CommentResponse, send_local_notifs, WebFingerResponse}; +use lemmy_api_common::{blocking, send_local_notifs, WebFingerResponse}; use lemmy_db_queries::{Crud, DbPool}; use lemmy_db_schema::{ source::{comment::Comment, community::Community, person::Person, post::Post}, - CommentId, LocalUserId, }; -use lemmy_db_views::comment_view::CommentView; use lemmy_utils::{ request::{retry, RecvError}, settings::structs::Settings, utils::{scrape_text_for_mentions, MentionData}, LemmyError, }; -use lemmy_websocket::{messages::SendComment, LemmyContext}; +use lemmy_websocket::LemmyContext; use log::debug; use reqwest::Client; use url::Url; @@ -45,37 +43,6 @@ async fn get_notif_recipients( send_local_notifs(mentions, comment.clone(), actor, post, context.pool(), true).await } -// TODO: in many call sites we are setting an empty vec for recipient_ids, we should get the actual -// recipient actors from somewhere -pub(crate) async fn send_websocket_message< - OP: ToString + Send + lemmy_websocket::OperationType + 'static, ->( - comment_id: CommentId, - recipient_ids: Vec, - op: OP, - context: &LemmyContext, -) -> Result<(), LemmyError> { - // Refetch the view - let comment_view = blocking(context.pool(), move |conn| { - CommentView::read(conn, comment_id, None) - }) - .await??; - - let res = CommentResponse { - comment_view, - recipient_ids, - form_id: None, - }; - - context.chat_server().do_send(SendComment { - op, - comment: res, - websocket_id: None, - }); - - Ok(()) -} - pub struct MentionsAndAddresses { pub ccs: Vec, pub inboxes: Vec, diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index fc3bebfed..734fed00e 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -7,16 +7,10 @@ use crate::{ list_community_follower_inboxes, undo_block_user::UndoBlockUserFromCommunity, }, - deletion::{ - delete::DeletePostCommentOrCommunity, - undo_delete::UndoDeletePostCommentOrCommunity, - }, + deletion::{delete::Delete, undo_delete::UndoDelete}, generate_activity_id, post::create_or_update::CreateOrUpdatePost, - removal::{ - remove::RemovePostCommentCommunityOrMod, - undo_remove::UndoRemovePostCommentOrCommunity, - }, + removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity}, verify_activity, verify_community, voting::{undo_vote::UndoVote, vote::Vote}, @@ -43,13 +37,13 @@ pub enum AnnouncableActivities { CreateOrUpdatePost(Box), Vote(Vote), UndoVote(UndoVote), - DeletePostCommentOrCommunity(DeletePostCommentOrCommunity), - UndoDeletePostCommentOrCommunity(UndoDeletePostCommentOrCommunity), - RemovePostCommentCommunityOrMod(RemovePostCommentCommunityOrMod), + Delete(Delete), + UndoDelete(UndoDelete), UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity), BlockUserFromCommunity(BlockUserFromCommunity), UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), AddMod(AddMod), + RemoveMod(RemoveMod), } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 4151471ad..49cf9615f 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -1,10 +1,8 @@ use crate::{check_is_apub_id_valid, CommunityType}; use itertools::Itertools; -use lemmy_api_common::{blocking, community::CommunityResponse}; -use lemmy_db_schema::{source::community::Community, CommunityId}; -use lemmy_db_views_actor::community_view::CommunityView; +use lemmy_db_schema::source::community::Community; use lemmy_utils::{settings::structs::Settings, LemmyError}; -use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext}; +use lemmy_websocket::LemmyContext; use url::Url; pub mod add_mod; @@ -13,30 +11,6 @@ pub mod block_user; pub mod undo_block_user; pub mod update; -pub(crate) async fn send_websocket_message< - OP: ToString + Send + lemmy_websocket::OperationType + 'static, ->( - community_id: CommunityId, - op: OP, - context: &LemmyContext, -) -> Result<(), LemmyError> { - let community_view = blocking(context.pool(), move |conn| { - CommunityView::read(conn, community_id, None) - }) - .await??; - - let res = CommunityResponse { community_view }; - - context.chat_server().do_send(SendCommunityRoomMessage { - op, - response: res, - community_id, - websocket_id: None, - }); - - Ok(()) -} - async fn list_community_follower_inboxes( community: &Community, additional_inboxes: Vec, diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 964a42388..d38d722bf 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -1,10 +1,5 @@ use crate::{ - activities::{ - community::send_websocket_message, - verify_activity, - verify_mod_action, - verify_person_in_community, - }, + activities::{verify_activity, verify_mod_action, verify_person_in_community}, objects::community::Group, }; use activitystreams::activity::kind::UpdateType; @@ -13,7 +8,7 @@ use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; use lemmy_db_queries::{ApubObject, Crud}; use lemmy_db_schema::source::community::{Community, CommunityForm}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; use url::Url; /// This activity is received from a remote community mod, and updates the description or other @@ -71,12 +66,15 @@ impl ActivityHandler for UpdateCommunity { }) .await??; - send_websocket_message( + send_community_ws_message( updated_community.id, UserOperationCrud::EditCommunity, + None, + None, context, ) - .await + .await?; + Ok(()) } fn common(&self) -> &ActivityCommonFields { diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index d377a6696..093919b88 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -1,31 +1,48 @@ use crate::{ activities::{ - comment::send_websocket_message as send_comment_message, - community::send_websocket_message as send_community_message, - post::send_websocket_message as send_post_message, + community::announce::AnnouncableActivities, + deletion::{ + receive_delete_action, + verify_delete_activity, + DeletableObjects, + WebsocketMessages, + }, + generate_activity_id, verify_activity, - verify_mod_action, - verify_person_in_community, - }, - fetcher::{ - community::get_or_fetch_and_upsert_community, - objects::get_or_fetch_and_insert_post_or_comment, - person::get_or_fetch_and_upsert_person, }, + activity_queue::send_to_community_new, + extensions::context::lemmy_context, + fetcher::person::get_or_fetch_and_upsert_person, ActorType, - CommunityType, - PostOrComment, }; use activitystreams::activity::kind::DeleteType; +use anyhow::anyhow; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; use lemmy_db_queries::{ source::{comment::Comment_, community::Community_, post::Post_}, Crud, }; -use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; +use lemmy_db_schema::source::{ + comment::Comment, + community::Community, + moderator::{ + ModRemoveComment, + ModRemoveCommentForm, + ModRemoveCommunity, + ModRemoveCommunityForm, + ModRemovePost, + ModRemovePostForm, + }, + person::Person, + post::Post, +}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{ + send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, + LemmyContext, + UserOperationCrud, +}; use url::Url; /// This is very confusing, because there are four distinct cases to handle: @@ -38,38 +55,36 @@ use url::Url; /// wrapping it in an announce just like other activities, instead of having the community send it. #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -pub struct DeletePostCommentOrCommunity { - to: PublicUrl, +pub struct Delete { + pub(in crate::activities::deletion) to: PublicUrl, pub(in crate::activities::deletion) object: Url, - cc: [Url; 1], + pub(in crate::activities::deletion) cc: [Url; 1], #[serde(rename = "type")] - kind: DeleteType, + pub(in crate::activities::deletion) kind: DeleteType, + /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user + /// deleting their own content. + pub(in crate::activities::deletion) summary: Option, #[serde(flatten)] - common: ActivityCommonFields, + pub(in crate::activities::deletion) common: ActivityCommonFields, } #[async_trait::async_trait(?Send)] -impl ActivityHandler for DeletePostCommentOrCommunity { +impl ActivityHandler for Delete { async fn verify( &self, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self.common())?; - let object_community = - get_or_fetch_and_upsert_community(&self.object, context, request_counter).await; - // deleting a community (set counter 0 to only fetch from local db) - if object_community.is_ok() { - verify_mod_action(&self.common.actor, self.object.clone(), context).await?; - } - // deleting a post or comment - else { - verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter) - .await?; - let object_creator = - get_post_or_comment_actor_id(&self.object, context, request_counter).await?; - verify_urls_match(&self.common.actor, &object_creator)?; - } + verify_delete_activity( + &self.object, + &self.cc[0], + &self.common, + self.summary.is_some(), + context, + request_counter, + ) + .await?; Ok(()) } @@ -78,55 +93,36 @@ impl ActivityHandler for DeletePostCommentOrCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let object_community = - get_or_fetch_and_upsert_community(&self.object, context, request_counter).await; - // deleting a community - if let Ok(community) = object_community { - if community.local { - // repeat these checks just to be sure - verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter) - .await?; - verify_mod_action(&self.common.actor, self.object.clone(), context).await?; - let mod_ = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; - community.send_delete(mod_, context).await?; - } - let deleted_community = blocking(context.pool(), move |conn| { - Community::update_deleted(conn, community.id, true) - }) - .await??; - - send_community_message( - deleted_community.id, - UserOperationCrud::DeleteCommunity, + if let Some(reason) = self.summary { + // We set reason to empty string if it doesn't exist, to distinguish between delete and + // remove. Here we change it back to option, so we don't write it to db. + let reason = if reason.is_empty() { + None + } else { + Some(reason) + }; + receive_remove_action( + &self.common.actor, + &self.object, + reason, context, + request_counter, + ) + .await + } else { + receive_delete_action( + &self.object, + &self.common.actor, + WebsocketMessages { + community: UserOperationCrud::DeleteCommunity, + post: UserOperationCrud::DeletePost, + comment: UserOperationCrud::DeleteComment, + }, + true, + context, + request_counter, ) .await - } - // deleting a post or comment - else { - match get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await? { - PostOrComment::Post(post) => { - let deleted_post = blocking(context.pool(), move |conn| { - Post::update_deleted(conn, post.id, true) - }) - .await??; - send_post_message(deleted_post.id, UserOperationCrud::EditPost, context).await - } - PostOrComment::Comment(comment) => { - let deleted_comment = blocking(context.pool(), move |conn| { - Comment::update_deleted(conn, comment.id, true) - }) - .await??; - send_comment_message( - deleted_comment.id, - vec![], - UserOperationCrud::EditComment, - context, - ) - .await - } - } } } @@ -135,25 +131,102 @@ impl ActivityHandler for DeletePostCommentOrCommunity { } } -async fn get_post_or_comment_actor_id( +impl Delete { + pub(in crate::activities::deletion) async fn send( + actor: &Person, + community: &Community, + object_id: Url, + summary: Option, + context: &LemmyContext, + ) -> Result<(), LemmyError> { + let id = generate_activity_id(DeleteType::Delete)?; + let delete = Delete { + to: PublicUrl::Public, + object: object_id, + cc: [community.actor_id()], + kind: DeleteType::Delete, + summary, + common: ActivityCommonFields { + context: lemmy_context(), + id: id.clone(), + actor: actor.actor_id(), + unparsed: Default::default(), + }, + }; + + let activity = AnnouncableActivities::Delete(delete); + send_to_community_new(activity, &id, actor, community, vec![], context).await + } +} + +pub(in crate::activities) async fn receive_remove_action( + actor: &Url, object: &Url, + reason: Option, context: &LemmyContext, request_counter: &mut i32, -) -> Result { - let actor_id = - match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? { - PostOrComment::Post(post) => { - let creator_id = post.creator_id; - blocking(context.pool(), move |conn| Person::read(conn, creator_id)) - .await?? - .actor_id() +) -> Result<(), LemmyError> { + let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; + use UserOperationCrud::*; + match DeletableObjects::read_from_db(object, context).await? { + DeletableObjects::Community(community) => { + if community.local { + return Err(anyhow!("Only local admin can remove community").into()); } - PostOrComment::Comment(comment) => { - let creator_id = comment.creator_id; - blocking(context.pool(), move |conn| Person::read(conn, creator_id)) - .await?? - .actor_id() - } - }; - Ok(actor_id) + let form = ModRemoveCommunityForm { + mod_person_id: actor.id, + community_id: community.id, + removed: Some(true), + reason, + expires: None, + }; + blocking(context.pool(), move |conn| { + ModRemoveCommunity::create(conn, &form) + }) + .await??; + let deleted_community = blocking(context.pool(), move |conn| { + Community::update_removed(conn, community.id, true) + }) + .await??; + + send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?; + } + DeletableObjects::Post(post) => { + let form = ModRemovePostForm { + mod_person_id: actor.id, + post_id: post.id, + removed: Some(true), + reason, + }; + blocking(context.pool(), move |conn| { + ModRemovePost::create(conn, &form) + }) + .await??; + let removed_post = blocking(context.pool(), move |conn| { + Post::update_removed(conn, post.id, true) + }) + .await??; + + send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?; + } + DeletableObjects::Comment(comment) => { + let form = ModRemoveCommentForm { + mod_person_id: actor.id, + comment_id: comment.id, + removed: Some(true), + reason, + }; + blocking(context.pool(), move |conn| { + ModRemoveComment::create(conn, &form) + }) + .await??; + let removed_comment = blocking(context.pool(), move |conn| { + Comment::update_removed(conn, comment.id, true) + }) + .await??; + + send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?; + } + } + Ok(()) } diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index b440edd68..cf73be097 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -1,2 +1,209 @@ +use crate::{ + activities::{ + deletion::{delete::Delete, undo_delete::UndoDelete}, + verify_mod_action, + verify_person_in_community, + }, + fetcher::person::get_or_fetch_and_upsert_person, + ActorType, +}; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields}; +use lemmy_db_queries::{ + source::{comment::Comment_, community::Community_, post::Post_}, + ApubObject, +}; +use lemmy_db_schema::{ + source::{comment::Comment, community::Community, person::Person, post::Post}, + DbUrl, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::{ + send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, + LemmyContext, + UserOperationCrud, +}; +use url::Url; + pub mod delete; pub mod undo_delete; + +pub async fn send_apub_delete( + actor: &Person, + community: &Community, + object_id: Url, + deleted: bool, + context: &LemmyContext, +) -> Result<(), LemmyError> { + if deleted { + Delete::send(actor, community, object_id, None, context).await + } else { + UndoDelete::send(actor, community, object_id, None, context).await + } +} + +// TODO: remove reason is actually optional in lemmy. we set an empty string in that case, but its +// ugly +pub async fn send_apub_remove( + actor: &Person, + community: &Community, + object_id: Url, + reason: String, + removed: bool, + context: &LemmyContext, +) -> Result<(), LemmyError> { + if removed { + Delete::send(actor, community, object_id, Some(reason), context).await + } else { + UndoDelete::send(actor, community, object_id, Some(reason), context).await + } +} + +pub enum DeletableObjects { + Community(Box), + Comment(Box), + Post(Box), +} + +impl DeletableObjects { + pub(crate) async fn read_from_db( + ap_id: &Url, + context: &LemmyContext, + ) -> Result { + let id: DbUrl = ap_id.clone().into(); + + if let Some(c) = DeletableObjects::read_type_from_db::(id.clone(), context).await? { + return Ok(DeletableObjects::Community(Box::new(c))); + } + if let Some(p) = DeletableObjects::read_type_from_db::(id.clone(), context).await? { + return Ok(DeletableObjects::Post(Box::new(p))); + } + if let Some(c) = DeletableObjects::read_type_from_db::(id.clone(), context).await? { + return Ok(DeletableObjects::Comment(Box::new(c))); + } + Err(diesel::NotFound.into()) + } + + // TODO: a method like this should be provided by fetcher module + async fn read_type_from_db( + ap_id: DbUrl, + context: &LemmyContext, + ) -> Result, LemmyError> { + blocking(context.pool(), move |conn| { + Type::read_from_apub_id(conn, &ap_id).ok() + }) + .await + } +} + +pub(in crate::activities) async fn verify_delete_activity( + object: &Url, + cc: &Url, + common: &ActivityCommonFields, + is_mod_action: bool, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + let object = DeletableObjects::read_from_db(object, context).await?; + match object { + DeletableObjects::Community(c) => { + if c.local { + // can only do this check for local community, in remote case it would try to fetch the + // deleted community (which fails) + verify_person_in_community(&common.actor, cc, context, request_counter).await?; + } + // community deletion is always a mod (or admin) action + verify_mod_action(&common.actor, c.actor_id(), context).await?; + } + DeletableObjects::Post(p) => { + verify_delete_activity_post_or_comment( + cc, + common, + &p.ap_id.into(), + is_mod_action, + context, + request_counter, + ) + .await?; + } + DeletableObjects::Comment(c) => { + verify_delete_activity_post_or_comment( + cc, + common, + &c.ap_id.into(), + is_mod_action, + context, + request_counter, + ) + .await?; + } + } + Ok(()) +} + +async fn verify_delete_activity_post_or_comment( + cc: &Url, + common: &ActivityCommonFields, + object_id: &Url, + is_mod_action: bool, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + verify_person_in_community(&common.actor, cc, context, request_counter).await?; + if is_mod_action { + verify_mod_action(&common.actor, cc.clone(), context).await?; + } else { + // domain of post ap_id and post.creator ap_id are identical, so we just check the former + verify_domains_match(&common.actor, object_id)?; + } + Ok(()) +} + +struct WebsocketMessages { + community: UserOperationCrud, + post: UserOperationCrud, + comment: UserOperationCrud, +} + +/// Write deletion or restoring of an object to the database, and send websocket message. +/// TODO: we should do something similar for receive_remove_action(), but its much more complicated +/// because of the mod log +async fn receive_delete_action( + object: &Url, + actor: &Url, + ws_messages: WebsocketMessages, + deleted: bool, + context: &LemmyContext, + request_counter: &mut i32, +) -> Result<(), LemmyError> { + match DeletableObjects::read_from_db(object, context).await? { + DeletableObjects::Community(community) => { + if community.local { + let mod_ = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; + let object = community.actor_id(); + send_apub_delete(&mod_, &community.clone(), object, true, context).await?; + } + + let community = blocking(context.pool(), move |conn| { + Community::update_deleted(conn, community.id, deleted) + }) + .await??; + send_community_ws_message(community.id, ws_messages.community, None, None, context).await?; + } + DeletableObjects::Post(post) => { + let deleted_post = blocking(context.pool(), move |conn| { + Post::update_deleted(conn, post.id, deleted) + }) + .await??; + send_post_ws_message(deleted_post.id, ws_messages.post, None, None, context).await?; + } + DeletableObjects::Comment(comment) => { + let deleted_comment = blocking(context.pool(), move |conn| { + Comment::update_deleted(conn, comment.id, deleted) + }) + .await??; + send_comment_ws_message_simple(deleted_comment.id, ws_messages.comment, context).await?; + } + } + Ok(()) +} diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index e57e6b401..0114acac8 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -1,35 +1,39 @@ use crate::{ activities::{ - comment::send_websocket_message as send_comment_message, - community::send_websocket_message as send_community_message, - deletion::delete::DeletePostCommentOrCommunity, - post::send_websocket_message as send_post_message, + community::announce::AnnouncableActivities, + deletion::{ + delete::Delete, + receive_delete_action, + verify_delete_activity, + DeletableObjects, + WebsocketMessages, + }, + generate_activity_id, verify_activity, - verify_mod_action, - verify_person_in_community, }, - fetcher::{ - community::get_or_fetch_and_upsert_community, - objects::get_or_fetch_and_insert_post_or_comment, - person::get_or_fetch_and_upsert_person, - }, - CommunityType, - PostOrComment, + activity_queue::send_to_community_new, + extensions::context::lemmy_context, + ActorType, }; -use activitystreams::activity::kind::UndoType; +use activitystreams::activity::kind::{DeleteType, UndoType}; +use anyhow::anyhow; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_}; -use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post}; +use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{ + send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, + LemmyContext, + UserOperationCrud, +}; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -pub struct UndoDeletePostCommentOrCommunity { +pub struct UndoDelete { to: PublicUrl, - object: DeletePostCommentOrCommunity, + object: Delete, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, @@ -38,7 +42,7 @@ pub struct UndoDeletePostCommentOrCommunity { } #[async_trait::async_trait(?Send)] -impl ActivityHandler for UndoDeletePostCommentOrCommunity { +impl ActivityHandler for UndoDelete { async fn verify( &self, context: &LemmyContext, @@ -46,18 +50,15 @@ impl ActivityHandler for UndoDeletePostCommentOrCommunity { ) -> Result<(), LemmyError> { verify_activity(self.common())?; self.object.verify(context, request_counter).await?; - let object_community = - get_or_fetch_and_upsert_community(&self.object.object, context, request_counter).await; - // restoring a community - if object_community.is_ok() { - verify_mod_action(&self.common.actor, self.object.object.clone(), context).await?; - } - // restoring a post or comment - else { - verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter) - .await?; - verify_urls_match(&self.common.actor, &self.object.common().actor)?; - } + verify_delete_activity( + &self.object.object, + &self.cc[0], + &self.common, + self.object.summary.is_some(), + context, + request_counter, + ) + .await?; Ok(()) } @@ -66,61 +67,101 @@ impl ActivityHandler for UndoDeletePostCommentOrCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let object_community = - get_or_fetch_and_upsert_community(&self.object.object, context, request_counter).await; - // restoring a community - if let Ok(community) = object_community { - if community.local { - // repeat these checks just to be sure - verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter) - .await?; - verify_mod_action(&self.common.actor, self.object.object.clone(), context).await?; - let mod_ = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; - community.send_undo_delete(mod_, context).await?; - } - let deleted_community = blocking(context.pool(), move |conn| { - Community::update_deleted(conn, community.id, false) - }) - .await??; - - send_community_message( - deleted_community.id, - UserOperationCrud::EditCommunity, + if self.object.summary.is_some() { + UndoDelete::receive_undo_remove_action(&self.object.object, context).await + } else { + receive_delete_action( + &self.object.object, + &self.common.actor, + WebsocketMessages { + community: UserOperationCrud::EditCommunity, + post: UserOperationCrud::EditPost, + comment: UserOperationCrud::EditComment, + }, + false, context, + request_counter, ) .await } - // restoring a post or comment - else { - match get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter) - .await? - { - PostOrComment::Post(post) => { - let deleted_post = blocking(context.pool(), move |conn| { - Post::update_deleted(conn, post.id, false) - }) - .await??; - send_post_message(deleted_post.id, UserOperationCrud::EditPost, context).await - } - PostOrComment::Comment(comment) => { - let deleted_comment = blocking(context.pool(), move |conn| { - Comment::update_deleted(conn, comment.id, false) - }) - .await??; - send_comment_message( - deleted_comment.id, - vec![], - UserOperationCrud::EditComment, - context, - ) - .await - } - } - } } fn common(&self) -> &ActivityCommonFields { &self.common } } + +impl UndoDelete { + pub(in crate::activities::deletion) async fn send( + actor: &Person, + community: &Community, + object_id: Url, + summary: Option, + context: &LemmyContext, + ) -> Result<(), LemmyError> { + let delete = Delete { + to: PublicUrl::Public, + object: object_id, + cc: [community.actor_id()], + kind: DeleteType::Delete, + summary, + common: ActivityCommonFields { + context: lemmy_context(), + id: generate_activity_id(DeleteType::Delete)?, + actor: actor.actor_id(), + unparsed: Default::default(), + }, + }; + + let id = generate_activity_id(UndoType::Undo)?; + let undo = UndoDelete { + to: PublicUrl::Public, + object: delete, + cc: [community.actor_id()], + kind: UndoType::Undo, + common: ActivityCommonFields { + context: lemmy_context(), + id: id.clone(), + actor: actor.actor_id(), + unparsed: Default::default(), + }, + }; + + let activity = AnnouncableActivities::UndoDelete(undo); + send_to_community_new(activity, &id, actor, community, vec![], context).await + } + + pub(in crate::activities) async fn receive_undo_remove_action( + object: &Url, + context: &LemmyContext, + ) -> Result<(), LemmyError> { + use UserOperationCrud::*; + match DeletableObjects::read_from_db(object, context).await? { + DeletableObjects::Community(community) => { + if community.local { + return Err(anyhow!("Only local admin can restore community").into()); + } + let deleted_community = blocking(context.pool(), move |conn| { + Community::update_removed(conn, community.id, false) + }) + .await??; + send_community_ws_message(deleted_community.id, EditCommunity, None, None, context).await?; + } + DeletableObjects::Post(post) => { + let removed_post = blocking(context.pool(), move |conn| { + Post::update_removed(conn, post.id, false) + }) + .await??; + send_post_ws_message(removed_post.id, EditPost, None, None, context).await?; + } + DeletableObjects::Comment(comment) => { + let removed_comment = blocking(context.pool(), move |conn| { + Comment::update_removed(conn, comment.id, false) + }) + .await??; + send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?; + } + } + Ok(()) + } +} diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index 94838e16f..1a9c77689 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -3,7 +3,6 @@ use crate::{ community::announce::AnnouncableActivities, extract_community, generate_activity_id, - post::send_websocket_message, verify_activity, verify_mod_action, verify_person_in_community, @@ -27,7 +26,7 @@ use lemmy_apub_lib::{ use lemmy_db_queries::Crud; use lemmy_db_schema::source::{community::Community, person::Person, post::Post}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -126,7 +125,8 @@ impl ActivityHandler for CreateOrUpdatePost { CreateOrUpdateType::Create => UserOperationCrud::CreatePost, CreateOrUpdateType::Update => UserOperationCrud::EditPost, }; - send_websocket_message(post.id, notif_type, context).await + send_post_ws_message(post.id, notif_type, None, None, context).await?; + Ok(()) } fn common(&self) -> &ActivityCommonFields { diff --git a/crates/apub/src/activities/post/mod.rs b/crates/apub/src/activities/post/mod.rs index b60348ed0..e903f52ff 100644 --- a/crates/apub/src/activities/post/mod.rs +++ b/crates/apub/src/activities/post/mod.rs @@ -1,30 +1 @@ -use lemmy_api_common::{blocking, post::PostResponse}; -use lemmy_db_schema::PostId; -use lemmy_db_views::post_view::PostView; -use lemmy_utils::LemmyError; -use lemmy_websocket::{messages::SendPost, LemmyContext}; - pub mod create_or_update; - -pub(crate) async fn send_websocket_message< - OP: ToString + Send + lemmy_websocket::OperationType + 'static, ->( - post_id: PostId, - op: OP, - context: &LemmyContext, -) -> Result<(), LemmyError> { - let post_view = blocking(context.pool(), move |conn| { - PostView::read(conn, post_id, None) - }) - .await??; - - let res = PostResponse { post_view }; - - context.chat_server().do_send(SendPost { - op, - post: res, - websocket_id: None, - }); - - Ok(()) -} diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index 09582689b..cd3c65744 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -1,11 +1,5 @@ use crate::{ - activities::{ - generate_activity_id, - private_message::send_websocket_message, - verify_activity, - verify_person, - CreateOrUpdateType, - }, + activities::{generate_activity_id, verify_activity, verify_person, CreateOrUpdateType}, activity_queue::send_activity_new, extensions::context::lemmy_context, objects::{private_message::Note, FromApub, ToApub}, @@ -16,7 +10,7 @@ use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler use lemmy_db_queries::Crud; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -83,7 +77,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage, CreateOrUpdateType::Update => UserOperationCrud::EditPrivateMessage, }; - send_websocket_message(private_message.id, notif_type, context).await?; + send_pm_ws_message(private_message.id, notif_type, None, context).await?; Ok(()) } diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs index 4c990a2d1..e6c03093b 100644 --- a/crates/apub/src/activities/private_message/delete.rs +++ b/crates/apub/src/activities/private_message/delete.rs @@ -1,10 +1,5 @@ use crate::{ - activities::{ - generate_activity_id, - private_message::send_websocket_message, - verify_activity, - verify_person, - }, + activities::{generate_activity_id, verify_activity, verify_person}, activity_queue::send_activity_new, extensions::context::lemmy_context, ActorType, @@ -15,7 +10,7 @@ use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -84,9 +79,10 @@ impl ActivityHandler for DeletePrivateMessage { }) .await??; - send_websocket_message( + send_pm_ws_message( deleted_private_message.id, UserOperationCrud::DeletePrivateMessage, + None, context, ) .await?; diff --git a/crates/apub/src/activities/private_message/mod.rs b/crates/apub/src/activities/private_message/mod.rs index 0c0557286..4bcda7c7b 100644 --- a/crates/apub/src/activities/private_message/mod.rs +++ b/crates/apub/src/activities/private_message/mod.rs @@ -1,41 +1,3 @@ -use lemmy_api_common::{blocking, person::PrivateMessageResponse}; -use lemmy_db_schema::PrivateMessageId; -use lemmy_db_views::{local_user_view::LocalUserView, private_message_view::PrivateMessageView}; -use lemmy_utils::LemmyError; -use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperationCrud}; - pub mod create_or_update; pub mod delete; pub mod undo_delete; - -async fn send_websocket_message( - private_message_id: PrivateMessageId, - op: UserOperationCrud, - context: &LemmyContext, -) -> Result<(), LemmyError> { - let message = blocking(context.pool(), move |conn| { - PrivateMessageView::read(conn, private_message_id) - }) - .await??; - let res = PrivateMessageResponse { - private_message_view: message, - }; - - // Send notifications to the local recipient, if one exists - let recipient_id = res.private_message_view.recipient.id; - let local_recipient_id = blocking(context.pool(), move |conn| { - LocalUserView::read_person(conn, recipient_id) - }) - .await?? - .local_user - .id; - - context.chat_server().do_send(SendUserRoomMessage { - op, - response: res, - local_recipient_id, - websocket_id: None, - }); - - Ok(()) -} diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs index 5bbca90e6..4cd2a139d 100644 --- a/crates/apub/src/activities/private_message/undo_delete.rs +++ b/crates/apub/src/activities/private_message/undo_delete.rs @@ -1,7 +1,7 @@ use crate::{ activities::{ generate_activity_id, - private_message::{delete::DeletePrivateMessage, send_websocket_message}, + private_message::delete::DeletePrivateMessage, verify_activity, verify_person, }, @@ -20,7 +20,7 @@ use lemmy_apub_lib::{ use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -104,9 +104,10 @@ impl ActivityHandler for UndoDeletePrivateMessage { }) .await??; - send_websocket_message( + send_pm_ws_message( deleted_private_message.id, UserOperationCrud::EditPrivateMessage, + None, context, ) .await?; diff --git a/crates/apub/src/activities/removal/remove.rs b/crates/apub/src/activities/removal/remove.rs index 83aaabc6f..1bd6e0a03 100644 --- a/crates/apub/src/activities/removal/remove.rs +++ b/crates/apub/src/activities/removal/remove.rs @@ -1,77 +1,59 @@ use crate::{ activities::{ - comment::send_websocket_message as send_comment_message, - community::send_websocket_message as send_community_message, - post::send_websocket_message as send_post_message, + deletion::{delete::receive_remove_action, verify_delete_activity}, verify_activity, verify_add_remove_moderator_target, verify_mod_action, verify_person_in_community, }, - fetcher::{ - community::get_or_fetch_and_upsert_community, - objects::get_or_fetch_and_insert_post_or_comment, - person::get_or_fetch_and_upsert_person, - }, + fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, CommunityType, - PostOrComment, }; use activitystreams::{activity::kind::RemoveType, base::AnyBase}; -use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; -use lemmy_db_queries::{ - source::{comment::Comment_, community::Community_, post::Post_}, - Joinable, -}; -use lemmy_db_schema::source::{ - comment::Comment, - community::{Community, CommunityModerator, CommunityModeratorForm}, - post::Post, -}; +use lemmy_db_queries::Joinable; +use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::LemmyContext; use url::Url; -// TODO: we can probably deduplicate a bunch of code between this and DeletePostCommentOrCommunity #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -pub struct RemovePostCommentCommunityOrMod { +pub struct RemoveMod { to: PublicUrl, pub(in crate::activities::removal) object: Url, cc: [Url; 1], #[serde(rename = "type")] kind: RemoveType, // if target is set, this is means remove mod from community - target: Option, + pub(in crate::activities::removal) target: Option, #[serde(flatten)] common: ActivityCommonFields, } #[async_trait::async_trait(?Send)] -impl ActivityHandler for RemovePostCommentCommunityOrMod { +impl ActivityHandler for RemoveMod { async fn verify( &self, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self.common())?; - let object_community = - get_or_fetch_and_upsert_community(&self.object, context, request_counter).await; - // removing a community - if object_community.is_ok() { - verify_mod_action(&self.common.actor, self.object.clone(), context).await?; - } - // removing community mod - else if let Some(target) = &self.target { + if let Some(target) = &self.target { verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_add_remove_moderator_target(target, self.cc[0].clone())?; - } - // removing a post or comment - else { - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; + } else { + verify_delete_activity( + &self.object, + &self.cc[0], + self.common(), + true, + context, + request_counter, + ) + .await?; } Ok(()) } @@ -81,27 +63,7 @@ impl ActivityHandler for RemovePostCommentCommunityOrMod { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let object_community = - get_or_fetch_and_upsert_community(&self.object, context, request_counter).await; - // removing a community - if let Ok(community) = object_community { - if community.local { - return Err(anyhow!("Only local admin can remove community").into()); - } - let deleted_community = blocking(context.pool(), move |conn| { - Community::update_removed(conn, community.id, true) - }) - .await??; - - send_community_message( - deleted_community.id, - UserOperationCrud::RemoveCommunity, - context, - ) - .await - } - // removing community mod - else if self.target.is_some() { + if self.target.is_some() { let community = get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?; let remove_mod = @@ -121,31 +83,15 @@ impl ActivityHandler for RemovePostCommentCommunityOrMod { .await?; // TODO: send websocket notification about removed mod Ok(()) - } - // removing a post or comment - else { - match get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await? { - PostOrComment::Post(post) => { - let removed_post = blocking(context.pool(), move |conn| { - Post::update_removed(conn, post.id, true) - }) - .await??; - send_post_message(removed_post.id, UserOperationCrud::EditPost, context).await - } - PostOrComment::Comment(comment) => { - let removed_comment = blocking(context.pool(), move |conn| { - Comment::update_removed(conn, comment.id, true) - }) - .await??; - send_comment_message( - removed_comment.id, - vec![], - UserOperationCrud::EditComment, - context, - ) - .await - } - } + } else { + receive_remove_action( + &self.common.actor, + &self.object, + None, + context, + request_counter, + ) + .await } } diff --git a/crates/apub/src/activities/removal/undo_remove.rs b/crates/apub/src/activities/removal/undo_remove.rs index 003eb4aaa..e2c7ef76f 100644 --- a/crates/apub/src/activities/removal/undo_remove.rs +++ b/crates/apub/src/activities/removal/undo_remove.rs @@ -1,34 +1,20 @@ -use crate::{ - activities::{ - comment::send_websocket_message as send_comment_message, - community::send_websocket_message as send_community_message, - post::send_websocket_message as send_post_message, - removal::remove::RemovePostCommentCommunityOrMod, - verify_activity, - verify_mod_action, - verify_person_in_community, - }, - fetcher::{ - community::get_or_fetch_and_upsert_community, - objects::get_or_fetch_and_insert_post_or_comment, - }, - PostOrComment, +use crate::activities::{ + deletion::{undo_delete::UndoDelete, verify_delete_activity}, + removal::remove::RemoveMod, + verify_activity, }; use activitystreams::activity::kind::UndoType; -use anyhow::anyhow; -use lemmy_api_common::blocking; use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; -use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_}; -use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post}; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperationCrud}; +use lemmy_websocket::LemmyContext; use url::Url; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct UndoRemovePostCommentOrCommunity { to: PublicUrl, - object: RemovePostCommentCommunityOrMod, + // Note, there is no such thing as Undo/Remove/Mod, so we ignore that + object: RemoveMod, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, @@ -44,74 +30,26 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity { request_counter: &mut i32, ) -> Result<(), LemmyError> { verify_activity(self.common())?; - let object_community = - get_or_fetch_and_upsert_community(&self.object.object, context, request_counter).await; - // removing a community - if object_community.is_ok() { - verify_mod_action(&self.common.actor, self.object.object.clone(), context).await?; - } - // removing a post or comment - else { - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; - } self.object.verify(context, request_counter).await?; - // dont check that actor and object.actor are identical, so that one mod can - // undo the action of another + + verify_delete_activity( + &self.object.object, + &self.cc[0], + self.common(), + true, + context, + request_counter, + ) + .await?; Ok(()) } async fn receive( self, context: &LemmyContext, - request_counter: &mut i32, + _request_counter: &mut i32, ) -> Result<(), LemmyError> { - let object_community = - get_or_fetch_and_upsert_community(&self.object.object, context, request_counter).await; - // restoring a community - if let Ok(community) = object_community { - if community.local { - return Err(anyhow!("Only local admin can undo remove community").into()); - } - let deleted_community = blocking(context.pool(), move |conn| { - Community::update_removed(conn, community.id, false) - }) - .await??; - - send_community_message( - deleted_community.id, - UserOperationCrud::EditCommunity, - context, - ) - .await - } - // restoring a post or comment - else { - match get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter) - .await? - { - PostOrComment::Post(post) => { - let removed_post = blocking(context.pool(), move |conn| { - Post::update_removed(conn, post.id, false) - }) - .await??; - send_post_message(removed_post.id, UserOperationCrud::EditPost, context).await - } - PostOrComment::Comment(comment) => { - let removed_comment = blocking(context.pool(), move |conn| { - Comment::update_removed(conn, comment.id, false) - }) - .await??; - send_comment_message( - removed_comment.id, - vec![], - UserOperationCrud::EditComment, - context, - ) - .await - } - } - } + UndoDelete::receive_undo_remove_action(&self.object.object, context).await } fn common(&self) -> &ActivityCommonFields { diff --git a/crates/apub/src/activities/send/comment.rs b/crates/apub/src/activities/send/comment.rs deleted file mode 100644 index 8b446860a..000000000 --- a/crates/apub/src/activities/send/comment.rs +++ /dev/null @@ -1,153 +0,0 @@ -use crate::{ - activities::generate_activity_id, - activity_queue::send_to_community, - extensions::context::lemmy_context, - ActorType, - ApubObjectType, -}; -use activitystreams::{ - activity::{ - kind::{DeleteType, RemoveType, UndoType}, - Delete, - Remove, - Undo, - }, - prelude::*, - public, -}; -use lemmy_api_common::blocking; -use lemmy_db_queries::Crud; -use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; - -#[async_trait::async_trait(?Send)] -impl ApubObjectType for Comment { - async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let post_id = self.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - - let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut delete = Delete::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(delete, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_undo_delete( - &self, - creator: &Person, - context: &LemmyContext, - ) -> Result<(), LemmyError> { - let post_id = self.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - - let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - // Generate a fake delete activity, with the correct object - let mut delete = Delete::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - // Undo that fake activity - let mut undo = Undo::new( - creator.actor_id.to_owned().into_inner(), - delete.into_any_base()?, - ); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(undo, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let post_id = self.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - - let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut remove = Remove::new( - mod_.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - remove - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(RemoveType::Remove)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(remove, mod_, &community, None, context).await?; - Ok(()) - } - - async fn send_undo_remove( - &self, - mod_: &Person, - context: &LemmyContext, - ) -> Result<(), LemmyError> { - let post_id = self.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - - let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - // Generate a fake delete activity, with the correct object - let mut remove = Remove::new( - mod_.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - remove - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(RemoveType::Remove)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - // Undo that fake activity - let mut undo = Undo::new( - mod_.actor_id.to_owned().into_inner(), - remove.into_any_base()?, - ); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(undo, mod_, &community, None, context).await?; - Ok(()) - } -} diff --git a/crates/apub/src/activities/send/community.rs b/crates/apub/src/activities/send/community.rs index 6f88dd36f..a103f95c6 100644 --- a/crates/apub/src/activities/send/community.rs +++ b/crates/apub/src/activities/send/community.rs @@ -12,20 +12,10 @@ use crate::{ }; use activitystreams::{ activity::{ - kind::{ - AddType, - AnnounceType, - BlockType, - DeleteType, - LikeType, - RemoveType, - UndoType, - UpdateType, - }, + kind::{AddType, AnnounceType, BlockType, RemoveType, UndoType, UpdateType}, Add, Announce, Block, - Delete, OptTargetRefExt, Remove, Undo, @@ -97,112 +87,6 @@ impl CommunityType for Community { Ok(()) } - /// If the creator of a community deletes the community, send this to all followers. - /// - /// We need to handle deletion by a remote mod separately. - async fn send_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> { - // Local mod, send directly from community to followers - if self.local { - let mut delete = Delete::new(self.actor_id(), self.actor_id()); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![self.followers_url()]); - - send_to_community_followers(delete, self, None, context).await?; - } - // Remote mod, send from mod to community - else { - let mut delete = Delete::new(mod_.actor_id(), self.actor_id()); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![self.actor_id()]); - - send_to_community(delete, &mod_, self, None, context).await?; - } - Ok(()) - } - - /// If the creator of a community reverts the deletion of a community, send this to all followers. - /// - /// We need to handle undelete by a remote mod separately. - async fn send_undo_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> { - // Local mod, send directly from community to followers - if self.local { - let mut delete = Delete::new(self.actor_id(), self.actor_id()); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![self.followers_url()]); - - let mut undo = Undo::new(self.actor_id(), delete.into_any_base()?); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![self.followers_url()]); - - send_to_community_followers(undo, self, None, context).await?; - } - // Remote mod, send from mod to community - else { - let mut delete = Delete::new(mod_.actor_id(), self.actor_id()); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![self.actor_id()]); - - let mut undo = Undo::new(mod_.actor_id(), delete.into_any_base()?); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![self.actor_id()]); - - send_to_community(undo, &mod_, self, None, context).await?; - } - Ok(()) - } - - /// If an admin removes a community, send this to all followers. - async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> { - let mut remove = Remove::new(self.actor_id(), self.actor_id()); - remove - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(RemoveType::Remove)?) - .set_to(public()) - .set_many_ccs(vec![self.followers_url()]); - - send_to_community_followers(remove, self, None, context).await?; - Ok(()) - } - - /// If an admin reverts the removal of a community, send this to all followers. - async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> { - let mut remove = Remove::new(self.actor_id(), self.actor_id()); - remove - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(RemoveType::Remove)?) - .set_to(public()) - .set_many_ccs(vec![self.followers_url()]); - - // Undo that fake activity - let mut undo = Undo::new(self.actor_id(), remove.into_any_base()?); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(LikeType::Like)?) - .set_to(public()) - .set_many_ccs(vec![self.followers_url()]); - - send_to_community_followers(undo, self, None, context).await?; - Ok(()) - } - /// Wraps an activity sent to the community in an announce, and then sends the announce to all /// community followers. /// diff --git a/crates/apub/src/activities/send/mod.rs b/crates/apub/src/activities/send/mod.rs index c00ecb1a7..a01cddd6d 100644 --- a/crates/apub/src/activities/send/mod.rs +++ b/crates/apub/src/activities/send/mod.rs @@ -1,4 +1,2 @@ -pub(crate) mod comment; pub(crate) mod community; pub(crate) mod person; -pub(crate) mod post; diff --git a/crates/apub/src/activities/send/post.rs b/crates/apub/src/activities/send/post.rs deleted file mode 100644 index b9713661b..000000000 --- a/crates/apub/src/activities/send/post.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::{ - activities::generate_activity_id, - activity_queue::send_to_community, - extensions::context::lemmy_context, - ActorType, - ApubObjectType, -}; -use activitystreams::{ - activity::{ - kind::{DeleteType, RemoveType, UndoType}, - Delete, - Remove, - Undo, - }, - prelude::*, - public, -}; -use lemmy_api_common::blocking; -use lemmy_db_queries::Crud; -use lemmy_db_schema::source::{community::Community, person::Person, post::Post}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; - -#[async_trait::async_trait(?Send)] -impl ApubObjectType for Post { - async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let community_id = self.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut delete = Delete::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(delete, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_undo_delete( - &self, - creator: &Person, - context: &LemmyContext, - ) -> Result<(), LemmyError> { - let community_id = self.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut delete = Delete::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - delete - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DeleteType::Delete)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - // Undo that fake activity - let mut undo = Undo::new( - creator.actor_id.to_owned().into_inner(), - delete.into_any_base()?, - ); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(undo, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let community_id = self.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut remove = Remove::new( - mod_.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - remove - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(RemoveType::Remove)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(remove, mod_, &community, None, context).await?; - Ok(()) - } - - async fn send_undo_remove( - &self, - mod_: &Person, - context: &LemmyContext, - ) -> Result<(), LemmyError> { - let community_id = self.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut remove = Remove::new( - mod_.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - remove - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(RemoveType::Remove)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - // Undo that fake activity - let mut undo = Undo::new( - mod_.actor_id.to_owned().into_inner(), - remove.into_any_base()?, - ); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(undo, mod_, &community, None, context).await?; - Ok(()) - } -} diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs index 48d9446f8..48d44c0ee 100644 --- a/crates/apub/src/activities/voting/mod.rs +++ b/crates/apub/src/activities/voting/mod.rs @@ -1,8 +1,4 @@ -use crate::activities::{ - comment::send_websocket_message as send_comment_message, - post::send_websocket_message as send_post_message, - voting::vote::VoteType, -}; +use crate::activities::voting::vote::VoteType; use lemmy_api_common::blocking; use lemmy_db_queries::Likeable; use lemmy_db_schema::source::{ @@ -11,7 +7,11 @@ use lemmy_db_schema::source::{ post::{Post, PostLike, PostLikeForm}, }; use lemmy_utils::LemmyError; -use lemmy_websocket::{LemmyContext, UserOperation}; +use lemmy_websocket::{ + send::{send_comment_ws_message_simple, send_post_ws_message}, + LemmyContext, + UserOperation, +}; pub mod undo_vote; pub mod vote; @@ -36,13 +36,8 @@ async fn vote_comment( }) .await??; - send_comment_message( - comment_id, - vec![], - UserOperation::CreateCommentLike, - context, - ) - .await + send_comment_ws_message_simple(comment_id, UserOperation::CreateCommentLike, context).await?; + Ok(()) } async fn vote_post( @@ -64,7 +59,8 @@ async fn vote_post( }) .await??; - send_post_message(post.id, UserOperation::CreatePostLike, context).await + send_post_ws_message(post.id, UserOperation::CreatePostLike, None, None, context).await?; + Ok(()) } async fn undo_vote_comment( @@ -79,13 +75,8 @@ async fn undo_vote_comment( }) .await??; - send_comment_message( - comment.id, - vec![], - UserOperation::CreateCommentLike, - context, - ) - .await + send_comment_ws_message_simple(comment_id, UserOperation::CreateCommentLike, context).await?; + Ok(()) } async fn undo_vote_post( @@ -99,5 +90,7 @@ async fn undo_vote_post( PostLike::remove(conn, person_id, post_id) }) .await??; - send_post_message(post.id, UserOperation::CreatePostLike, context).await + + send_post_ws_message(post_id, UserOperation::CreatePostLike, None, None, context).await?; + Ok(()) } diff --git a/crates/apub/src/extensions/context.rs b/crates/apub/src/extensions/context.rs index 1d6f83bd7..bc58052f0 100644 --- a/crates/apub/src/extensions/context.rs +++ b/crates/apub/src/extensions/context.rs @@ -2,7 +2,7 @@ use activitystreams::{base::AnyBase, context, primitives::OneOrMany}; use serde_json::json; use url::Url; -pub fn lemmy_context() -> OneOrMany { +pub(crate) fn lemmy_context() -> OneOrMany { let context_ext = AnyBase::from_arbitrary_json(json!( { "sc": "http://schema.org#", diff --git a/crates/apub/src/extensions/signatures.rs b/crates/apub/src/extensions/signatures.rs index 8fded5152..675740365 100644 --- a/crates/apub/src/extensions/signatures.rs +++ b/crates/apub/src/extensions/signatures.rs @@ -62,7 +62,7 @@ pub(crate) async fn sign_and_send( } /// Verifies the HTTP signature on an incoming inbox request. -pub fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), LemmyError> { +pub(crate) fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), LemmyError> { let verified = CONFIG2 .begin_verify( request.method(), diff --git a/crates/apub/src/fetcher/community.rs b/crates/apub/src/fetcher/community.rs index ff94129be..83aa7b51e 100644 --- a/crates/apub/src/fetcher/community.rs +++ b/crates/apub/src/fetcher/community.rs @@ -25,7 +25,7 @@ use url::Url; /// /// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database. /// Otherwise it is fetched from the remote instance, stored and returned. -pub async fn get_or_fetch_and_upsert_community( +pub(crate) async fn get_or_fetch_and_upsert_community( apub_id: &Url, context: &LemmyContext, recursion_counter: &mut i32, diff --git a/crates/apub/src/fetcher/mod.rs b/crates/apub/src/fetcher/mod.rs index 477cecc87..9d9e2b8e3 100644 --- a/crates/apub/src/fetcher/mod.rs +++ b/crates/apub/src/fetcher/mod.rs @@ -42,7 +42,7 @@ where /// /// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database. /// Otherwise it is fetched from the remote instance, stored and returned. -pub async fn get_or_fetch_and_upsert_actor( +pub(crate) async fn get_or_fetch_and_upsert_actor( apub_id: &Url, context: &LemmyContext, recursion_counter: &mut i32, diff --git a/crates/apub/src/fetcher/objects.rs b/crates/apub/src/fetcher/objects.rs index 835bf63a3..e538982db 100644 --- a/crates/apub/src/fetcher/objects.rs +++ b/crates/apub/src/fetcher/objects.rs @@ -17,7 +17,7 @@ use url::Url; /// pulled from its apub ID, inserted and returned. /// /// The parent community is also pulled if necessary. Comments are not pulled. -pub async fn get_or_fetch_and_insert_post( +pub(crate) async fn get_or_fetch_and_insert_post( post_ap_id: &Url, context: &LemmyContext, recursion_counter: &mut i32, @@ -46,7 +46,7 @@ pub async fn get_or_fetch_and_insert_post( /// pulled from its apub ID, inserted and returned. /// /// The parent community, post and comment are also pulled if necessary. -pub async fn get_or_fetch_and_insert_comment( +pub(crate) async fn get_or_fetch_and_insert_comment( comment_ap_id: &Url, context: &LemmyContext, recursion_counter: &mut i32, @@ -80,7 +80,7 @@ pub async fn get_or_fetch_and_insert_comment( } } -pub async fn get_or_fetch_and_insert_post_or_comment( +pub(crate) async fn get_or_fetch_and_insert_post_or_comment( ap_id: &Url, context: &LemmyContext, recursion_counter: &mut i32, diff --git a/crates/apub/src/fetcher/person.rs b/crates/apub/src/fetcher/person.rs index 3acedb086..ed3ca057f 100644 --- a/crates/apub/src/fetcher/person.rs +++ b/crates/apub/src/fetcher/person.rs @@ -16,7 +16,7 @@ use url::Url; /// /// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database. /// Otherwise it is fetched from the remote instance, stored and returned. -pub async fn get_or_fetch_and_upsert_person( +pub(crate) async fn get_or_fetch_and_upsert_person( apub_id: &Url, context: &LemmyContext, recursion_counter: &mut i32, diff --git a/crates/apub/src/http/inbox_enums.rs b/crates/apub/src/http/inbox_enums.rs index 034048777..1ed210bb5 100644 --- a/crates/apub/src/http/inbox_enums.rs +++ b/crates/apub/src/http/inbox_enums.rs @@ -7,7 +7,7 @@ use crate::activities::{ undo_block_user::UndoBlockUserFromCommunity, update::UpdateCommunity, }, - deletion::{delete::DeletePostCommentOrCommunity, undo_delete::UndoDeletePostCommentOrCommunity}, + deletion::{delete::Delete, undo_delete::UndoDelete}, following::{accept::AcceptFollowCommunity, follow::FollowCommunity, undo::UndoFollowCommunity}, post::create_or_update::CreateOrUpdatePost, private_message::{ @@ -15,10 +15,7 @@ use crate::activities::{ delete::DeletePrivateMessage, undo_delete::UndoDeletePrivateMessage, }, - removal::{ - remove::RemovePostCommentCommunityOrMod, - undo_remove::UndoRemovePostCommentOrCommunity, - }, + removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity}, voting::{undo_vote::UndoVote, vote::Vote}, }; use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler}; @@ -45,14 +42,14 @@ pub enum GroupInboxActivities { CreateOrUpdatePost(Box), Vote(Vote), UndoVote(UndoVote), - DeletePostCommentOrCommunity(DeletePostCommentOrCommunity), - UndoDeletePostCommentOrCommunity(UndoDeletePostCommentOrCommunity), - RemovePostCommentOrCommunity(RemovePostCommentCommunityOrMod), + DeletePostCommentOrCommunity(Delete), + UndoDeletePostCommentOrCommunity(UndoDelete), UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity), UpdateCommunity(Box), BlockUserFromCommunity(BlockUserFromCommunity), UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), AddMod(AddMod), + RemoveMod(RemoveMod), } #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] @@ -65,14 +62,14 @@ pub enum SharedInboxActivities { CreateOrUpdatePost(Box), Vote(Vote), UndoVote(UndoVote), - DeletePostCommentOrCommunity(DeletePostCommentOrCommunity), - UndoDeletePostCommentOrCommunity(UndoDeletePostCommentOrCommunity), - RemovePostCommentOrCommunity(RemovePostCommentCommunityOrMod), + Delete(Delete), + UndoDelete(UndoDelete), UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity), UpdateCommunity(Box), BlockUserFromCommunity(BlockUserFromCommunity), UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), AddMod(AddMod), + RemoveMod(RemoveMod), // received by person AcceptFollowCommunity(AcceptFollowCommunity), // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 950f11dc4..477143009 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -34,7 +34,7 @@ use serde::Serialize; use std::net::IpAddr; use url::{ParseError, Url}; -pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json"; +static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json"; /// Checks if the ID is allowed for sending or receiving. /// @@ -102,28 +102,9 @@ pub(crate) fn check_is_apub_id_valid( Ok(()) } -/// Common functions for ActivityPub objects, which are implemented by most (but not all) objects -/// and actors in Lemmy. -#[async_trait::async_trait(?Send)] -pub trait ApubObjectType { - async fn send_delete(&self, creator: &DbPerson, context: &LemmyContext) - -> Result<(), LemmyError>; - async fn send_undo_delete( - &self, - creator: &DbPerson, - context: &LemmyContext, - ) -> Result<(), LemmyError>; - async fn send_remove(&self, mod_: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>; - async fn send_undo_remove( - &self, - mod_: &DbPerson, - context: &LemmyContext, - ) -> Result<(), LemmyError>; -} - /// Common methods provided by ActivityPub actors (community and person). Not all methods are /// implemented by all actors. -pub trait ActorType { +trait ActorType { fn is_local(&self) -> bool; fn actor_id(&self) -> Url; fn name(&self) -> String; @@ -160,11 +141,6 @@ pub trait CommunityType { async fn get_follower_inboxes(&self, pool: &DbPool) -> Result, LemmyError>; async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>; - async fn send_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>; - async fn send_undo_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>; - - async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>; - async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_announce( &self, @@ -209,7 +185,7 @@ pub enum EndpointType { } /// Generates an apub endpoint for a given domain, IE xyz.tld -pub(crate) fn generate_apub_endpoint_for_domain( +fn generate_apub_endpoint_for_domain( endpoint_type: EndpointType, name: &str, domain: &str, @@ -260,7 +236,7 @@ pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result Ok(Url::parse(&url)?.into()) } -pub fn generate_moderators_url(community_id: &DbUrl) -> Result { +fn generate_moderators_url(community_id: &DbUrl) -> Result { Ok(Url::parse(&format!("{}/moderators", community_id))?.into()) } @@ -286,7 +262,7 @@ pub fn build_actor_id_from_shortname( /// Store a sent or received activity in the database, for logging purposes. These records are not /// persistent. -pub(crate) async fn insert_activity( +async fn insert_activity( ap_id: &Url, activity: T, local: bool, @@ -348,7 +324,7 @@ pub(crate) async fn find_post_or_comment_by_id( } #[derive(Debug)] -pub enum Object { +enum Object { Comment(Box), Post(Box), Community(Box), @@ -356,10 +332,7 @@ pub enum Object { PrivateMessage(Box), } -pub(crate) async fn find_object_by_id( - context: &LemmyContext, - apub_id: Url, -) -> Result { +async fn find_object_by_id(context: &LemmyContext, apub_id: Url) -> Result { let ap_id = apub_id.clone(); if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await { return Ok(match pc { @@ -397,7 +370,7 @@ pub(crate) async fn find_object_by_id( Err(NotFound.into()) } -pub(crate) async fn check_community_or_site_ban( +async fn check_community_or_site_ban( person: &Person, community_id: CommunityId, pool: &DbPool, diff --git a/crates/apub/src/migrations.rs b/crates/apub/src/migrations.rs index 0f18d2f37..59eeacce9 100644 --- a/crates/apub/src/migrations.rs +++ b/crates/apub/src/migrations.rs @@ -15,3 +15,15 @@ pub enum CommentInReplyToMigration { Old(Vec), New(Url), } + +// Another migration we are doing is to handle all deletions and removals using Delete activity. +// This is because Remove is for removing an object from a collection, so using it that way doesn't +// really make sense. It is also a problem because we have a RemoveMod activity, which was awkward +// to handle together with removing posts etc. +// +// v0.11: send and receive mod removals as Remove +// v0.12: receive removals as Remove, send as Delete (compatible with v0.11) +// v0.13: send and receive mod removals as Delete (compatible with v0.12) +// +// For v0.13, delete [`UndoRemovePostCommentOrCommunity`], and don't handle object deletion in +// [`RemoveMod`] handler. diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs index 2114a1d89..33a93915a 100644 --- a/crates/apub/src/objects/mod.rs +++ b/crates/apub/src/objects/mod.rs @@ -19,14 +19,14 @@ pub(crate) mod private_message; /// Trait for converting an object or actor into the respective ActivityPub type. #[async_trait::async_trait(?Send)] -pub trait ToApub { +pub(crate) trait ToApub { type ApubType; async fn to_apub(&self, pool: &DbPool) -> Result; fn to_tombstone(&self) -> Result; } #[async_trait::async_trait(?Send)] -pub trait FromApub { +pub(crate) trait FromApub { type ApubType; /// Converts an object from ActivityPub type to Lemmy internal type. /// diff --git a/crates/db_queries/src/lib.rs b/crates/db_queries/src/lib.rs index adc0717ac..fb5b8cd5d 100644 --- a/crates/db_queries/src/lib.rs +++ b/crates/db_queries/src/lib.rs @@ -25,17 +25,19 @@ pub mod source; pub type DbPool = diesel::r2d2::Pool>; -pub trait Crud { - fn create(conn: &PgConnection, form: &Form) -> Result +pub trait Crud { + type Form; + type IdType; + fn create(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; - fn read(conn: &PgConnection, id: IdType) -> Result + fn read(conn: &PgConnection, id: Self::IdType) -> Result where Self: Sized; - fn update(conn: &PgConnection, id: IdType, form: &Form) -> Result + fn update(conn: &PgConnection, id: Self::IdType, form: &Self::Form) -> Result where Self: Sized; - fn delete(_conn: &PgConnection, _id: IdType) -> Result + fn delete(_conn: &PgConnection, _id: Self::IdType) -> Result where Self: Sized, { @@ -43,8 +45,9 @@ pub trait Crud { } } -pub trait Followable
{ - fn follow(conn: &PgConnection, form: &Form) -> Result +pub trait Followable { + type Form; + fn follow(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; fn follow_accepted( @@ -54,59 +57,70 @@ pub trait Followable { ) -> Result where Self: Sized; - fn unfollow(conn: &PgConnection, form: &Form) -> Result + fn unfollow(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; fn has_local_followers(conn: &PgConnection, community_id: CommunityId) -> Result; } -pub trait Joinable { - fn join(conn: &PgConnection, form: &Form) -> Result +pub trait Joinable { + type Form; + fn join(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; - fn leave(conn: &PgConnection, form: &Form) -> Result + fn leave(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; } -pub trait Likeable { - fn like(conn: &PgConnection, form: &Form) -> Result +pub trait Likeable { + type Form; + type IdType; + fn like(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; - fn remove(conn: &PgConnection, person_id: PersonId, item_id: IdType) -> Result + fn remove( + conn: &PgConnection, + person_id: PersonId, + item_id: Self::IdType, + ) -> Result where Self: Sized; } -pub trait Bannable { - fn ban(conn: &PgConnection, form: &Form) -> Result +pub trait Bannable { + type Form; + fn ban(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; - fn unban(conn: &PgConnection, form: &Form) -> Result + fn unban(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; } -pub trait Saveable { - fn save(conn: &PgConnection, form: &Form) -> Result +pub trait Saveable { + type Form; + fn save(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; - fn unsave(conn: &PgConnection, form: &Form) -> Result + fn unsave(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; } -pub trait Readable { - fn mark_as_read(conn: &PgConnection, form: &Form) -> Result +pub trait Readable { + type Form; + fn mark_as_read(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; - fn mark_as_unread(conn: &PgConnection, form: &Form) -> Result + fn mark_as_unread(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; } -pub trait Reportable { - fn report(conn: &PgConnection, form: &Form) -> Result +pub trait Reportable { + type Form; + fn report(conn: &PgConnection, form: &Self::Form) -> Result where Self: Sized; fn resolve(conn: &PgConnection, report_id: i32, resolver_id: PersonId) -> Result @@ -121,11 +135,12 @@ pub trait DeleteableOrRemoveable { fn blank_out_deleted_or_removed_info(self) -> Self; } -pub trait ApubObject { +pub trait ApubObject { + type Form; fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result where Self: Sized; - fn upsert(conn: &PgConnection, user_form: &Form) -> Result + fn upsert(conn: &PgConnection, user_form: &Self::Form) -> Result where Self: Sized; } diff --git a/crates/db_queries/src/source/activity.rs b/crates/db_queries/src/source/activity.rs index f87cf7330..1f158370e 100644 --- a/crates/db_queries/src/source/activity.rs +++ b/crates/db_queries/src/source/activity.rs @@ -9,7 +9,9 @@ use std::{ io::{Error as IoError, ErrorKind}, }; -impl Crud for Activity { +impl Crud for Activity { + type Form = ActivityForm; + type IdType = i32; fn read(conn: &PgConnection, activity_id: i32) -> Result { use lemmy_db_schema::schema::activity::dsl::*; activity.find(activity_id).first::(conn) diff --git a/crates/db_queries/src/source/comment.rs b/crates/db_queries/src/source/comment.rs index d0171cb8d..1e9722714 100644 --- a/crates/db_queries/src/source/comment.rs +++ b/crates/db_queries/src/source/comment.rs @@ -135,7 +135,9 @@ impl Comment_ for Comment { } } -impl Crud for Comment { +impl Crud for Comment { + type Form = CommentForm; + type IdType = CommentId; fn read(conn: &PgConnection, comment_id: CommentId) -> Result { use lemmy_db_schema::schema::comment::dsl::*; comment.find(comment_id).first::(conn) @@ -165,7 +167,8 @@ impl Crud for Comment { } } -impl ApubObject for Comment { +impl ApubObject for Comment { + type Form = CommentForm; fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result { use lemmy_db_schema::schema::comment::dsl::*; comment.filter(ap_id.eq(object_id)).first::(conn) @@ -182,7 +185,9 @@ impl ApubObject for Comment { } } -impl Likeable for CommentLike { +impl Likeable for CommentLike { + type Form = CommentLikeForm; + type IdType = CommentId; fn like(conn: &PgConnection, comment_like_form: &CommentLikeForm) -> Result { use lemmy_db_schema::schema::comment_like::dsl::*; insert_into(comment_like) @@ -207,7 +212,8 @@ impl Likeable for CommentLike { } } -impl Saveable for CommentSaved { +impl Saveable for CommentSaved { + type Form = CommentSavedForm; fn save(conn: &PgConnection, comment_saved_form: &CommentSavedForm) -> Result { use lemmy_db_schema::schema::comment_saved::dsl::*; insert_into(comment_saved) diff --git a/crates/db_queries/src/source/comment_report.rs b/crates/db_queries/src/source/comment_report.rs index 87b936062..738ec3e29 100644 --- a/crates/db_queries/src/source/comment_report.rs +++ b/crates/db_queries/src/source/comment_report.rs @@ -6,7 +6,8 @@ use lemmy_db_schema::{ PersonId, }; -impl Reportable for CommentReport { +impl Reportable for CommentReport { + type Form = CommentReportForm; /// creates a comment report and returns it /// /// * `conn` - the postgres connection diff --git a/crates/db_queries/src/source/community.rs b/crates/db_queries/src/source/community.rs index 2a6219339..eaaa2ba29 100644 --- a/crates/db_queries/src/source/community.rs +++ b/crates/db_queries/src/source/community.rs @@ -60,7 +60,9 @@ mod safe_type { } } -impl Crud for Community { +impl Crud for Community { + type Form = CommunityForm; + type IdType = CommunityId; fn read(conn: &PgConnection, community_id: CommunityId) -> Result { use lemmy_db_schema::schema::community::dsl::*; community.find(community_id).first::(conn) @@ -90,7 +92,8 @@ impl Crud for Community { } } -impl ApubObject for Community { +impl ApubObject for Community { + type Form = CommunityForm; fn read_from_apub_id(conn: &PgConnection, for_actor_id: &DbUrl) -> Result { use lemmy_db_schema::schema::community::dsl::*; community @@ -175,7 +178,8 @@ impl Community_ for Community { } } -impl Joinable for CommunityModerator { +impl Joinable for CommunityModerator { + type Form = CommunityModeratorForm; fn join( conn: &PgConnection, community_moderator_form: &CommunityModeratorForm, @@ -252,7 +256,8 @@ impl CommunityModerator_ for CommunityModerator { } } -impl Bannable for CommunityPersonBan { +impl Bannable for CommunityPersonBan { + type Form = CommunityPersonBanForm; fn ban( conn: &PgConnection, community_person_ban_form: &CommunityPersonBanForm, @@ -277,7 +282,8 @@ impl Bannable for CommunityPersonBan { } } -impl Followable for CommunityFollower { +impl Followable for CommunityFollower { + type Form = CommunityFollowerForm; fn follow( conn: &PgConnection, community_follower_form: &CommunityFollowerForm, diff --git a/crates/db_queries/src/source/local_user.rs b/crates/db_queries/src/source/local_user.rs index b711c82aa..91eac3308 100644 --- a/crates/db_queries/src/source/local_user.rs +++ b/crates/db_queries/src/source/local_user.rs @@ -91,7 +91,9 @@ impl LocalUser_ for LocalUser { } } -impl Crud for LocalUser { +impl Crud for LocalUser { + type Form = LocalUserForm; + type IdType = LocalUserId; fn read(conn: &PgConnection, local_user_id: LocalUserId) -> Result { local_user.find(local_user_id).first::(conn) } diff --git a/crates/db_queries/src/source/moderator.rs b/crates/db_queries/src/source/moderator.rs index 14bb07f1c..25ef1416d 100644 --- a/crates/db_queries/src/source/moderator.rs +++ b/crates/db_queries/src/source/moderator.rs @@ -2,7 +2,9 @@ use crate::Crud; use diesel::{dsl::*, result::Error, *}; use lemmy_db_schema::source::moderator::*; -impl Crud for ModRemovePost { +impl Crud for ModRemovePost { + type Form = ModRemovePostForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_remove_post::dsl::*; mod_remove_post.find(from_id).first::(conn) @@ -23,7 +25,9 @@ impl Crud for ModRemovePost { } } -impl Crud for ModLockPost { +impl Crud for ModLockPost { + type Form = ModLockPostForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_lock_post::dsl::*; mod_lock_post.find(from_id).first::(conn) @@ -44,7 +48,9 @@ impl Crud for ModLockPost { } } -impl Crud for ModStickyPost { +impl Crud for ModStickyPost { + type Form = ModStickyPostForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_sticky_post::dsl::*; mod_sticky_post.find(from_id).first::(conn) @@ -65,7 +71,9 @@ impl Crud for ModStickyPost { } } -impl Crud for ModRemoveComment { +impl Crud for ModRemoveComment { + type Form = ModRemoveCommentForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_remove_comment::dsl::*; mod_remove_comment.find(from_id).first::(conn) @@ -86,7 +94,9 @@ impl Crud for ModRemoveComment { } } -impl Crud for ModRemoveCommunity { +impl Crud for ModRemoveCommunity { + type Form = ModRemoveCommunityForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_remove_community::dsl::*; mod_remove_community.find(from_id).first::(conn) @@ -111,7 +121,9 @@ impl Crud for ModRemoveCommunity { } } -impl Crud for ModBanFromCommunity { +impl Crud for ModBanFromCommunity { + type Form = ModBanFromCommunityForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_ban_from_community::dsl::*; mod_ban_from_community.find(from_id).first::(conn) @@ -136,7 +148,9 @@ impl Crud for ModBanFromCommunity { } } -impl Crud for ModBan { +impl Crud for ModBan { + type Form = ModBanForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_ban::dsl::*; mod_ban.find(from_id).first::(conn) @@ -155,7 +169,9 @@ impl Crud for ModBan { } } -impl Crud for ModAddCommunity { +impl Crud for ModAddCommunity { + type Form = ModAddCommunityForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_add_community::dsl::*; mod_add_community.find(from_id).first::(conn) @@ -176,7 +192,9 @@ impl Crud for ModAddCommunity { } } -impl Crud for ModAdd { +impl Crud for ModAdd { + type Form = ModAddForm; + type IdType = i32; fn read(conn: &PgConnection, from_id: i32) -> Result { use lemmy_db_schema::schema::mod_add::dsl::*; mod_add.find(from_id).first::(conn) diff --git a/crates/db_queries/src/source/password_reset_request.rs b/crates/db_queries/src/source/password_reset_request.rs index d04a1ac4b..a71cdae26 100644 --- a/crates/db_queries/src/source/password_reset_request.rs +++ b/crates/db_queries/src/source/password_reset_request.rs @@ -7,7 +7,9 @@ use lemmy_db_schema::{ }; use sha2::{Digest, Sha256}; -impl Crud for PasswordResetRequest { +impl Crud for PasswordResetRequest { + type Form = PasswordResetRequestForm; + type IdType = i32; fn read(conn: &PgConnection, password_reset_request_id: i32) -> Result { password_reset_request .find(password_reset_request_id) diff --git a/crates/db_queries/src/source/person.rs b/crates/db_queries/src/source/person.rs index a7266aa27..6172b4e1e 100644 --- a/crates/db_queries/src/source/person.rs +++ b/crates/db_queries/src/source/person.rs @@ -158,7 +158,9 @@ mod safe_type_alias_2 { } } -impl Crud for Person { +impl Crud for Person { + type Form = PersonForm; + type IdType = PersonId; fn read(conn: &PgConnection, person_id: PersonId) -> Result { person .filter(deleted.eq(false)) @@ -178,7 +180,8 @@ impl Crud for Person { } } -impl ApubObject for Person { +impl ApubObject for Person { + type Form = PersonForm; fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result { use lemmy_db_schema::schema::person::dsl::*; person diff --git a/crates/db_queries/src/source/person_mention.rs b/crates/db_queries/src/source/person_mention.rs index 456894200..774f79185 100644 --- a/crates/db_queries/src/source/person_mention.rs +++ b/crates/db_queries/src/source/person_mention.rs @@ -2,7 +2,9 @@ use crate::Crud; use diesel::{dsl::*, result::Error, *}; use lemmy_db_schema::{source::person_mention::*, PersonId, PersonMentionId}; -impl Crud for PersonMention { +impl Crud for PersonMention { + type Form = PersonMentionForm; + type IdType = PersonMentionId; fn read(conn: &PgConnection, person_mention_id: PersonMentionId) -> Result { use lemmy_db_schema::schema::person_mention::dsl::*; person_mention.find(person_mention_id).first::(conn) diff --git a/crates/db_queries/src/source/post.rs b/crates/db_queries/src/source/post.rs index f0cf10935..02ae4d6e2 100644 --- a/crates/db_queries/src/source/post.rs +++ b/crates/db_queries/src/source/post.rs @@ -18,7 +18,9 @@ use lemmy_db_schema::{ PostId, }; -impl Crud for Post { +impl Crud for Post { + type Form = PostForm; + type IdType = PostId; fn read(conn: &PgConnection, post_id: PostId) -> Result { use lemmy_db_schema::schema::post::dsl::*; post.find(post_id).first::(conn) @@ -179,7 +181,8 @@ impl Post_ for Post { } } -impl ApubObject for Post { +impl ApubObject for Post { + type Form = PostForm; fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result { use lemmy_db_schema::schema::post::dsl::*; post.filter(ap_id.eq(object_id)).first::(conn) @@ -196,7 +199,9 @@ impl ApubObject for Post { } } -impl Likeable for PostLike { +impl Likeable for PostLike { + type Form = PostLikeForm; + type IdType = PostId; fn like(conn: &PgConnection, post_like_form: &PostLikeForm) -> Result { use lemmy_db_schema::schema::post_like::dsl::*; insert_into(post_like) @@ -217,7 +222,8 @@ impl Likeable for PostLike { } } -impl Saveable for PostSaved { +impl Saveable for PostSaved { + type Form = PostSavedForm; fn save(conn: &PgConnection, post_saved_form: &PostSavedForm) -> Result { use lemmy_db_schema::schema::post_saved::dsl::*; insert_into(post_saved) @@ -238,7 +244,8 @@ impl Saveable for PostSaved { } } -impl Readable for PostRead { +impl Readable for PostRead { + type Form = PostReadForm; fn mark_as_read(conn: &PgConnection, post_read_form: &PostReadForm) -> Result { use lemmy_db_schema::schema::post_read::dsl::*; insert_into(post_read) diff --git a/crates/db_queries/src/source/post_report.rs b/crates/db_queries/src/source/post_report.rs index b4e855731..19cd5dfea 100644 --- a/crates/db_queries/src/source/post_report.rs +++ b/crates/db_queries/src/source/post_report.rs @@ -2,7 +2,8 @@ use crate::Reportable; use diesel::{dsl::*, result::Error, *}; use lemmy_db_schema::{naive_now, source::post_report::*, PersonId}; -impl Reportable for PostReport { +impl Reportable for PostReport { + type Form = PostReportForm; /// creates a post report and returns it /// /// * `conn` - the postgres connection diff --git a/crates/db_queries/src/source/private_message.rs b/crates/db_queries/src/source/private_message.rs index da1c5abd5..c1138b979 100644 --- a/crates/db_queries/src/source/private_message.rs +++ b/crates/db_queries/src/source/private_message.rs @@ -2,7 +2,9 @@ use crate::{ApubObject, Crud, DeleteableOrRemoveable}; use diesel::{dsl::*, result::Error, *}; use lemmy_db_schema::{naive_now, source::private_message::*, DbUrl, PersonId, PrivateMessageId}; -impl Crud for PrivateMessage { +impl Crud for PrivateMessage { + type Form = PrivateMessageForm; + type IdType = PrivateMessageId; fn read(conn: &PgConnection, private_message_id: PrivateMessageId) -> Result { use lemmy_db_schema::schema::private_message::dsl::*; private_message.find(private_message_id).first::(conn) @@ -27,7 +29,8 @@ impl Crud for PrivateMessage { } } -impl ApubObject for PrivateMessage { +impl ApubObject for PrivateMessage { + type Form = PrivateMessageForm; fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result where Self: Sized, diff --git a/crates/db_queries/src/source/site.rs b/crates/db_queries/src/source/site.rs index d688546f8..b2bad7138 100644 --- a/crates/db_queries/src/source/site.rs +++ b/crates/db_queries/src/source/site.rs @@ -2,7 +2,9 @@ use crate::Crud; use diesel::{dsl::*, result::Error, *}; use lemmy_db_schema::{naive_now, source::site::*, PersonId}; -impl Crud for Site { +impl Crud for Site { + type Form = SiteForm; + type IdType = i32; fn read(conn: &PgConnection, _site_id: i32) -> Result { use lemmy_db_schema::schema::site::dsl::*; site.first::(conn) diff --git a/crates/websocket/Cargo.toml b/crates/websocket/Cargo.toml index 3571d0c32..6262beb50 100644 --- a/crates/websocket/Cargo.toml +++ b/crates/websocket/Cargo.toml @@ -15,6 +15,8 @@ lemmy_utils = { version = "=0.11.3", path = "../utils" } lemmy_api_common = { version = "=0.11.3", path = "../api_common" } lemmy_db_queries = { version = "=0.11.3", path = "../db_queries" } lemmy_db_schema = { version = "=0.11.3", path = "../db_schema" } +lemmy_db_views = { version = "=0.11.3", path = "../db_views" } +lemmy_db_views_actor = { version = "=0.11.3", path = "../db_views_actor" } reqwest = { version = "0.11.4", features = ["json"] } log = "0.4.14" rand = "0.8.4" diff --git a/crates/websocket/src/lib.rs b/crates/websocket/src/lib.rs index f5f5f0804..b866a2a21 100644 --- a/crates/websocket/src/lib.rs +++ b/crates/websocket/src/lib.rs @@ -13,6 +13,7 @@ pub mod chat_server; pub mod handlers; pub mod messages; pub mod routes; +pub mod send; pub struct LemmyContext { pub pool: DbPool, diff --git a/crates/websocket/src/messages.rs b/crates/websocket/src/messages.rs index 1ef3da5b5..3c16ec273 100644 --- a/crates/websocket/src/messages.rs +++ b/crates/websocket/src/messages.rs @@ -75,7 +75,7 @@ pub struct SendModRoomMessage { #[derive(Message)] #[rtype(result = "()")] -pub struct SendPost { +pub(crate) struct SendPost { pub op: OP, pub post: PostResponse, pub websocket_id: Option, @@ -83,7 +83,7 @@ pub struct SendPost { #[derive(Message)] #[rtype(result = "()")] -pub struct SendComment { +pub(crate) struct SendComment { pub op: OP, pub comment: CommentResponse, pub websocket_id: Option, diff --git a/crates/websocket/src/send.rs b/crates/websocket/src/send.rs new file mode 100644 index 000000000..130d94a48 --- /dev/null +++ b/crates/websocket/src/send.rs @@ -0,0 +1,162 @@ +use crate::{ + messages::{SendComment, SendCommunityRoomMessage, SendPost, SendUserRoomMessage}, + LemmyContext, + OperationType, +}; +use lemmy_api_common::{ + blocking, + comment::CommentResponse, + community::CommunityResponse, + person::PrivateMessageResponse, + post::PostResponse, +}; +use lemmy_db_queries::DeleteableOrRemoveable; +use lemmy_db_schema::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId}; +use lemmy_db_views::{ + comment_view::CommentView, + local_user_view::LocalUserView, + post_view::PostView, + private_message_view::PrivateMessageView, +}; +use lemmy_db_views_actor::community_view::CommunityView; +use lemmy_utils::{ConnectionId, LemmyError}; + +pub async fn send_post_ws_message( + post_id: PostId, + op: OP, + websocket_id: Option, + person_id: Option, + context: &LemmyContext, +) -> Result { + let mut post_view = blocking(context.pool(), move |conn| { + PostView::read(conn, post_id, person_id) + }) + .await??; + + if post_view.post.deleted || post_view.post.removed { + post_view.post = post_view.post.blank_out_deleted_or_removed_info(); + } + + let res = PostResponse { post_view }; + + context.chat_server().do_send(SendPost { + op, + post: res.clone(), + websocket_id, + }); + + Ok(res) +} + +// TODO: in many call sites in apub crate, we are setting an empty vec for recipient_ids, +// we should get the actual recipient actors from somewhere +pub async fn send_comment_ws_message_simple( + comment_id: CommentId, + op: OP, + context: &LemmyContext, +) -> Result { + send_comment_ws_message(comment_id, op, None, None, None, vec![], context).await +} + +pub async fn send_comment_ws_message( + comment_id: CommentId, + op: OP, + websocket_id: Option, + form_id: Option, + person_id: Option, + recipient_ids: Vec, + context: &LemmyContext, +) -> Result { + let mut view = blocking(context.pool(), move |conn| { + CommentView::read(conn, comment_id, person_id) + }) + .await??; + + if view.comment.deleted || view.comment.removed { + view.comment = view.comment.blank_out_deleted_or_removed_info(); + } + + let res = CommentResponse { + comment_view: view, + recipient_ids, + form_id, + }; + + context.chat_server().do_send(SendComment { + op, + comment: res.clone(), + websocket_id, + }); + + Ok(res) +} + +pub async fn send_community_ws_message( + community_id: CommunityId, + op: OP, + websocket_id: Option, + person_id: Option, + context: &LemmyContext, +) -> Result { + let mut community_view = blocking(context.pool(), move |conn| { + CommunityView::read(conn, community_id, person_id) + }) + .await??; + // Blank out deleted or removed info + if community_view.community.deleted || community_view.community.removed { + community_view.community = community_view.community.blank_out_deleted_or_removed_info(); + } + + let res = CommunityResponse { community_view }; + + // Strip out the person id and subscribed when sending to others + let mut res_mut = res.clone(); + res_mut.community_view.subscribed = false; + + context.chat_server().do_send(SendCommunityRoomMessage { + op, + response: res_mut, + community_id: res.community_view.community.id, + websocket_id, + }); + + Ok(res) +} + +pub async fn send_pm_ws_message( + private_message_id: PrivateMessageId, + op: OP, + websocket_id: Option, + context: &LemmyContext, +) -> Result { + let mut view = blocking(context.pool(), move |conn| { + PrivateMessageView::read(conn, private_message_id) + }) + .await??; + + // Blank out deleted or removed info + if view.private_message.deleted { + view.private_message = view.private_message.blank_out_deleted_or_removed_info(); + } + + let res = PrivateMessageResponse { + private_message_view: view, + }; + + // Send notifications to the local recipient, if one exists + if res.private_message_view.recipient.local { + let recipient_id = res.private_message_view.recipient.id; + let local_recipient = blocking(context.pool(), move |conn| { + LocalUserView::read_person(conn, recipient_id) + }) + .await??; + context.chat_server().do_send(SendUserRoomMessage { + op, + response: res.clone(), + local_recipient_id: local_recipient.local_user.id, + websocket_id, + }); + } + + Ok(res) +}