mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-26 22:31:20 +00:00
Merge pull request #1874 from LemmyNet/protocol-testing
Protocol testing
This commit is contained in:
commit
1bec551945
148 changed files with 3216 additions and 2015 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1728,6 +1728,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lemmy_api_common",
|
"lemmy_api_common",
|
||||||
"lemmy_apub",
|
"lemmy_apub",
|
||||||
|
"lemmy_apub_lib",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
"lemmy_db_views_actor",
|
"lemmy_db_views_actor",
|
||||||
|
|
|
@ -14,6 +14,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lemmy_apub = { version = "=0.13.5-rc.7", path = "../apub" }
|
lemmy_apub = { version = "=0.13.5-rc.7", path = "../apub" }
|
||||||
|
lemmy_apub_lib = { version = "=0.13.5-rc.7", path = "../apub_lib" }
|
||||||
lemmy_utils = { version = "=0.13.5-rc.7", path = "../utils" }
|
lemmy_utils = { version = "=0.13.5-rc.7", path = "../utils" }
|
||||||
lemmy_db_schema = { version = "=0.13.5-rc.7", path = "../db_schema" }
|
lemmy_db_schema = { version = "=0.13.5-rc.7", path = "../db_schema" }
|
||||||
lemmy_db_views = { version = "=0.13.5-rc.7", path = "../db_views" }
|
lemmy_db_views = { version = "=0.13.5-rc.7", path = "../db_views" }
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::Perform;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -9,11 +11,11 @@ use lemmy_api_common::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
activities::voting::{
|
fetcher::post_or_comment::PostOrComment,
|
||||||
|
protocol::activities::voting::{
|
||||||
undo_vote::UndoVote,
|
undo_vote::UndoVote,
|
||||||
vote::{Vote, VoteType},
|
vote::{Vote, VoteType},
|
||||||
},
|
},
|
||||||
fetcher::post_or_comment::PostOrComment,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::LocalUserId,
|
newtypes::LocalUserId,
|
||||||
|
@ -23,7 +25,8 @@ use lemmy_db_schema::{
|
||||||
use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
|
use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
|
||||||
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
|
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
|
||||||
use std::convert::TryInto;
|
|
||||||
|
use crate::Perform;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for MarkCommentAsRead {
|
impl Perform for MarkCommentAsRead {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::Perform;
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -7,7 +7,7 @@ use lemmy_api_common::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_mod_or_admin,
|
is_mod_or_admin,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{activities::report::Report, fetcher::object_id::ObjectId};
|
use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report};
|
||||||
use lemmy_db_schema::{source::comment_report::*, traits::Reportable};
|
use lemmy_db_schema::{source::comment_report::*, traits::Reportable};
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
comment_report_view::{CommentReportQueryBuilder, CommentReportView},
|
comment_report_view::{CommentReportQueryBuilder, CommentReportView},
|
||||||
|
@ -16,6 +16,8 @@ use lemmy_db_views::{
|
||||||
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
||||||
|
|
||||||
|
use crate::Perform;
|
||||||
|
|
||||||
/// Creates a comment report and notifies the moderators of the community
|
/// Creates a comment report and notifies the moderators of the community
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for CreateCommentReport {
|
impl Perform for CreateCommentReport {
|
||||||
|
|
|
@ -10,16 +10,16 @@ use lemmy_api_common::{
|
||||||
is_mod_or_admin,
|
is_mod_or_admin,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
activities::{
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::{
|
||||||
community::{
|
community::{
|
||||||
add_mod::AddMod,
|
add_mod::AddMod,
|
||||||
block_user::BlockUserFromCommunity,
|
block_user::BlockUserFromCommunity,
|
||||||
remove_mod::RemoveMod,
|
remove_mod::RemoveMod,
|
||||||
undo_block_user::UndoBlockUserFromCommunity,
|
undo_block_user::UndoBlockUserFromCommunity,
|
||||||
},
|
},
|
||||||
following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
|
following::{follow::FollowCommunity as FollowCommunityApub, undo_follow::UndoFollowCommunity},
|
||||||
},
|
},
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -12,16 +12,16 @@ use lemmy_api_common::{
|
||||||
post::*,
|
post::*,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
activities::{
|
fetcher::post_or_comment::PostOrComment,
|
||||||
post::create_or_update::CreateOrUpdatePost,
|
objects::post::ApubPost,
|
||||||
|
protocol::activities::{
|
||||||
|
create_or_update::post::CreateOrUpdatePost,
|
||||||
voting::{
|
voting::{
|
||||||
undo_vote::UndoVote,
|
undo_vote::UndoVote,
|
||||||
vote::{Vote, VoteType},
|
vote::{Vote, VoteType},
|
||||||
},
|
},
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
fetcher::post_or_comment::PostOrComment,
|
|
||||||
objects::post::ApubPost,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{moderator::*, post::*},
|
source::{moderator::*, post::*},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::Perform;
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -13,7 +13,7 @@ use lemmy_api_common::{
|
||||||
ResolvePostReport,
|
ResolvePostReport,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_apub::{activities::report::Report, fetcher::object_id::ObjectId};
|
use lemmy_apub::{fetcher::object_id::ObjectId, protocol::activities::community::report::Report};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::post_report::{PostReport, PostReportForm},
|
source::post_report::{PostReport, PostReportForm},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
|
@ -25,6 +25,8 @@ use lemmy_db_views::{
|
||||||
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
||||||
|
|
||||||
|
use crate::Perform;
|
||||||
|
|
||||||
/// Creates a post report and notifies the moderators of the community
|
/// Creates a post report and notifies the moderators of the community
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for CreatePostReport {
|
impl Perform for CreatePostReport {
|
||||||
|
|
|
@ -11,10 +11,10 @@ use lemmy_api_common::{
|
||||||
site::*,
|
site::*,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
build_actor_id_from_shortname,
|
|
||||||
fetcher::search::{search_by_apub_id, SearchableObjects},
|
fetcher::search::{search_by_apub_id, SearchableObjects},
|
||||||
EndpointType,
|
get_actor_id_from_name,
|
||||||
};
|
};
|
||||||
|
use lemmy_apub_lib::webfinger::WebfingerType;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
from_opt_str_to_opt_enum,
|
from_opt_str_to_opt_enum,
|
||||||
newtypes::PersonId,
|
newtypes::PersonId,
|
||||||
|
@ -174,11 +174,13 @@ impl Perform for Search {
|
||||||
let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.listing_type);
|
let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.listing_type);
|
||||||
let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All);
|
let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All);
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let community_actor_id = data
|
let community_actor_id = if let Some(name) = &data.community_name {
|
||||||
.community_name
|
get_actor_id_from_name(WebfingerType::Group, name, context)
|
||||||
.as_ref()
|
.await
|
||||||
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok())
|
.ok()
|
||||||
.unwrap_or(None);
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let creator_id = data.creator_id;
|
let creator_id = data.creator_id;
|
||||||
match search_type {
|
match search_type {
|
||||||
SearchType::Posts => {
|
SearchType::Posts => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::PerformCrud;
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -11,13 +11,13 @@ use lemmy_api_common::{
|
||||||
get_post,
|
get_post,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
activities::{
|
fetcher::post_or_comment::PostOrComment,
|
||||||
comment::create_or_update::CreateOrUpdateComment,
|
generate_local_apub_endpoint,
|
||||||
|
protocol::activities::{
|
||||||
|
create_or_update::comment::CreateOrUpdateComment,
|
||||||
voting::vote::{Vote, VoteType},
|
voting::vote::{Vote, VoteType},
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
fetcher::post_or_comment::PostOrComment,
|
|
||||||
generate_apub_endpoint,
|
|
||||||
EndpointType,
|
EndpointType,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -40,6 +40,8 @@ use lemmy_websocket::{
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::PerformCrud;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl PerformCrud for CreateComment {
|
impl PerformCrud for CreateComment {
|
||||||
type Response = CommentResponse;
|
type Response = CommentResponse;
|
||||||
|
@ -109,7 +111,7 @@ impl PerformCrud for CreateComment {
|
||||||
|
|
||||||
let updated_comment: Comment =
|
let updated_comment: Comment =
|
||||||
blocking(context.pool(), move |conn| -> Result<Comment, LemmyError> {
|
blocking(context.pool(), move |conn| -> Result<Comment, LemmyError> {
|
||||||
let apub_id = generate_apub_endpoint(
|
let apub_id = generate_local_apub_endpoint(
|
||||||
EndpointType::Comment,
|
EndpointType::Comment,
|
||||||
&inserted_comment_id.to_string(),
|
&inserted_comment_id.to_string(),
|
||||||
&protocol_and_hostname,
|
&protocol_and_hostname,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::PerformCrud;
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, comment::*, get_local_user_view_from_jwt_opt};
|
use lemmy_api_common::{blocking, comment::*, get_local_user_view_from_jwt_opt};
|
||||||
use lemmy_apub::{build_actor_id_from_shortname, EndpointType};
|
use lemmy_apub::get_actor_id_from_name;
|
||||||
|
use lemmy_apub_lib::webfinger::WebfingerType;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
from_opt_str_to_opt_enum,
|
from_opt_str_to_opt_enum,
|
||||||
traits::DeleteableOrRemoveable,
|
traits::DeleteableOrRemoveable,
|
||||||
|
@ -34,11 +35,13 @@ impl PerformCrud for GetComments {
|
||||||
let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
|
let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let community_actor_id = data
|
let community_actor_id = if let Some(name) = &data.community_name {
|
||||||
.community_name
|
get_actor_id_from_name(WebfingerType::Group, name, context)
|
||||||
.as_ref()
|
.await
|
||||||
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok())
|
.ok()
|
||||||
.unwrap_or(None);
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::PerformCrud;
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -8,8 +8,8 @@ use lemmy_api_common::{
|
||||||
comment::*,
|
comment::*,
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
use lemmy_apub::activities::{
|
use lemmy_apub::protocol::activities::{
|
||||||
comment::create_or_update::CreateOrUpdateComment,
|
create_or_update::comment::CreateOrUpdateComment,
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::comment::Comment;
|
use lemmy_db_schema::source::comment::Comment;
|
||||||
|
@ -26,6 +26,8 @@ use lemmy_websocket::{
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::PerformCrud;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl PerformCrud for EditComment {
|
impl PerformCrud for EditComment {
|
||||||
type Response = CommentResponse;
|
type Response = CommentResponse;
|
||||||
|
|
|
@ -8,9 +8,9 @@ use lemmy_api_common::{
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
generate_apub_endpoint,
|
|
||||||
generate_followers_url,
|
generate_followers_url,
|
||||||
generate_inbox_url,
|
generate_inbox_url,
|
||||||
|
generate_local_apub_endpoint,
|
||||||
generate_shared_inbox_url,
|
generate_shared_inbox_url,
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
EndpointType,
|
EndpointType,
|
||||||
|
@ -67,7 +67,7 @@ impl PerformCrud for CreateCommunity {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double check for duplicate community actor_ids
|
// Double check for duplicate community actor_ids
|
||||||
let community_actor_id = generate_apub_endpoint(
|
let community_actor_id = generate_local_apub_endpoint(
|
||||||
EndpointType::Community,
|
EndpointType::Community,
|
||||||
&data.name,
|
&data.name,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
|
|
@ -2,11 +2,11 @@ use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
|
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
build_actor_id_from_shortname,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
|
get_actor_id_from_name,
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
EndpointType,
|
|
||||||
};
|
};
|
||||||
|
use lemmy_apub_lib::webfinger::WebfingerType;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
from_opt_str_to_opt_enum,
|
from_opt_str_to_opt_enum,
|
||||||
traits::DeleteableOrRemoveable,
|
traits::DeleteableOrRemoveable,
|
||||||
|
@ -39,7 +39,7 @@ impl PerformCrud for GetCommunity {
|
||||||
None => {
|
None => {
|
||||||
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
|
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
|
||||||
let community_actor_id =
|
let community_actor_id =
|
||||||
build_actor_id_from_shortname(EndpointType::Community, &name, &context.settings())?;
|
get_actor_id_from_name(WebfingerType::Group, &name, context).await?;
|
||||||
|
|
||||||
ObjectId::<ApubCommunity>::new(community_actor_id)
|
ObjectId::<ApubCommunity>::new(community_actor_id)
|
||||||
.dereference(context, &mut 0)
|
.dereference(context, &mut 0)
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
||||||
community::{CommunityResponse, EditCommunity},
|
community::{CommunityResponse, EditCommunity},
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
use lemmy_apub::activities::community::update::UpdateCommunity;
|
use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
diesel_option_overwrite_to_url,
|
diesel_option_overwrite_to_url,
|
||||||
naive_now,
|
naive_now,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::PerformCrud;
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
use log::warn;
|
||||||
|
use webmention::{Webmention, WebmentionError};
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -10,13 +12,13 @@ use lemmy_api_common::{
|
||||||
post::*,
|
post::*,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
activities::{
|
fetcher::post_or_comment::PostOrComment,
|
||||||
post::create_or_update::CreateOrUpdatePost,
|
generate_local_apub_endpoint,
|
||||||
|
protocol::activities::{
|
||||||
|
create_or_update::post::CreateOrUpdatePost,
|
||||||
voting::vote::{Vote, VoteType},
|
voting::vote::{Vote, VoteType},
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
fetcher::post_or_comment::PostOrComment,
|
|
||||||
generate_apub_endpoint,
|
|
||||||
EndpointType,
|
EndpointType,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -31,8 +33,8 @@ use lemmy_utils::{
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
||||||
use log::warn;
|
|
||||||
use webmention::{Webmention, WebmentionError};
|
use crate::PerformCrud;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl PerformCrud for CreatePost {
|
impl PerformCrud for CreatePost {
|
||||||
|
@ -98,7 +100,7 @@ impl PerformCrud for CreatePost {
|
||||||
let inserted_post_id = inserted_post.id;
|
let inserted_post_id = inserted_post.id;
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
let updated_post = blocking(context.pool(), move |conn| -> Result<Post, LemmyError> {
|
let updated_post = blocking(context.pool(), move |conn| -> Result<Post, LemmyError> {
|
||||||
let apub_id = generate_apub_endpoint(
|
let apub_id = generate_local_apub_endpoint(
|
||||||
EndpointType::Post,
|
EndpointType::Post,
|
||||||
&inserted_post_id.to_string(),
|
&inserted_post_id.to_string(),
|
||||||
&protocol_and_hostname,
|
&protocol_and_hostname,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::PerformCrud;
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, mark_post_as_read, post::*};
|
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, mark_post_as_read, post::*};
|
||||||
use lemmy_apub::{build_actor_id_from_shortname, EndpointType};
|
use lemmy_apub::get_actor_id_from_name;
|
||||||
|
use lemmy_apub_lib::webfinger::WebfingerType;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
from_opt_str_to_opt_enum,
|
from_opt_str_to_opt_enum,
|
||||||
traits::DeleteableOrRemoveable,
|
traits::DeleteableOrRemoveable,
|
||||||
|
@ -136,11 +137,13 @@ impl PerformCrud for GetPosts {
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let community_actor_id = data
|
let community_actor_id = if let Some(name) = &data.community_name {
|
||||||
.community_name
|
get_actor_id_from_name(WebfingerType::Group, name, context)
|
||||||
.as_ref()
|
.await
|
||||||
.map(|t| build_actor_id_from_shortname(EndpointType::Community, t, &context.settings()).ok())
|
.ok()
|
||||||
.unwrap_or(None);
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
|
|
||||||
let mut posts = blocking(context.pool(), move |conn| {
|
let mut posts = blocking(context.pool(), move |conn| {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::PerformCrud;
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
@ -7,7 +7,10 @@ use lemmy_api_common::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
post::*,
|
post::*,
|
||||||
};
|
};
|
||||||
use lemmy_apub::activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType};
|
use lemmy_apub::protocol::activities::{
|
||||||
|
create_or_update::post::CreateOrUpdatePost,
|
||||||
|
CreateOrUpdateType,
|
||||||
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
naive_now,
|
naive_now,
|
||||||
source::post::{Post, PostForm},
|
source::post::{Post, PostForm},
|
||||||
|
@ -22,6 +25,8 @@ use lemmy_utils::{
|
||||||
};
|
};
|
||||||
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
||||||
|
|
||||||
|
use crate::PerformCrud;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl PerformCrud for EditPost {
|
impl PerformCrud for EditPost {
|
||||||
type Response = PostResponse;
|
type Response = PostResponse;
|
||||||
|
|
|
@ -7,11 +7,11 @@ use lemmy_api_common::{
|
||||||
person::{CreatePrivateMessage, PrivateMessageResponse},
|
person::{CreatePrivateMessage, PrivateMessageResponse},
|
||||||
};
|
};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
activities::{
|
generate_local_apub_endpoint,
|
||||||
|
protocol::activities::{
|
||||||
private_message::create_or_update::CreateOrUpdatePrivateMessage,
|
private_message::create_or_update::CreateOrUpdatePrivateMessage,
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
generate_apub_endpoint,
|
|
||||||
EndpointType,
|
EndpointType,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -67,7 +67,7 @@ impl PerformCrud for CreatePrivateMessage {
|
||||||
let updated_private_message = blocking(
|
let updated_private_message = blocking(
|
||||||
context.pool(),
|
context.pool(),
|
||||||
move |conn| -> Result<PrivateMessage, LemmyError> {
|
move |conn| -> Result<PrivateMessage, LemmyError> {
|
||||||
let apub_id = generate_apub_endpoint(
|
let apub_id = generate_local_apub_endpoint(
|
||||||
EndpointType::PrivateMessage,
|
EndpointType::PrivateMessage,
|
||||||
&inserted_private_message_id.to_string(),
|
&inserted_private_message_id.to_string(),
|
||||||
&protocol_and_hostname,
|
&protocol_and_hostname,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
person::{DeletePrivateMessage, PrivateMessageResponse},
|
person::{DeletePrivateMessage, PrivateMessageResponse},
|
||||||
};
|
};
|
||||||
use lemmy_apub::activities::private_message::{
|
use lemmy_apub::protocol::activities::private_message::{
|
||||||
delete::DeletePrivateMessage as DeletePrivateMessageApub,
|
delete::DeletePrivateMessage as DeletePrivateMessageApub,
|
||||||
undo_delete::UndoDeletePrivateMessage,
|
undo_delete::UndoDeletePrivateMessage,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
person::{EditPrivateMessage, PrivateMessageResponse},
|
person::{EditPrivateMessage, PrivateMessageResponse},
|
||||||
};
|
};
|
||||||
use lemmy_apub::activities::{
|
use lemmy_apub::protocol::activities::{
|
||||||
private_message::create_or_update::CreateOrUpdatePrivateMessage,
|
private_message::create_or_update::CreateOrUpdatePrivateMessage,
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,9 +2,9 @@ use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, honeypot_check, password_length_check, person::*};
|
use lemmy_api_common::{blocking, honeypot_check, password_length_check, person::*};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
generate_apub_endpoint,
|
|
||||||
generate_followers_url,
|
generate_followers_url,
|
||||||
generate_inbox_url,
|
generate_inbox_url,
|
||||||
|
generate_local_apub_endpoint,
|
||||||
generate_shared_inbox_url,
|
generate_shared_inbox_url,
|
||||||
EndpointType,
|
EndpointType,
|
||||||
};
|
};
|
||||||
|
@ -96,7 +96,7 @@ impl PerformCrud for Register {
|
||||||
if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
|
if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
|
||||||
return Err(ApiError::err_plain("invalid_username").into());
|
return Err(ApiError::err_plain("invalid_username").into());
|
||||||
}
|
}
|
||||||
let actor_id = generate_apub_endpoint(
|
let actor_id = generate_local_apub_endpoint(
|
||||||
EndpointType::Person,
|
EndpointType::Person,
|
||||||
&data.username,
|
&data.username,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
@ -179,7 +179,7 @@ impl PerformCrud for Register {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
let default_community_name = "main";
|
let default_community_name = "main";
|
||||||
let actor_id = generate_apub_endpoint(
|
let actor_id = generate_local_apub_endpoint(
|
||||||
EndpointType::Community,
|
EndpointType::Community,
|
||||||
default_community_name,
|
default_community_name,
|
||||||
&protocol_and_hostname,
|
&protocol_and_hostname,
|
||||||
|
|
|
@ -2,11 +2,11 @@ use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
|
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
|
||||||
use lemmy_apub::{
|
use lemmy_apub::{
|
||||||
build_actor_id_from_shortname,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
|
get_actor_id_from_name,
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
EndpointType,
|
|
||||||
};
|
};
|
||||||
|
use lemmy_apub_lib::webfinger::WebfingerType;
|
||||||
use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
|
use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
|
||||||
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
|
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{
|
||||||
|
@ -46,8 +46,7 @@ impl PerformCrud for GetPersonDetails {
|
||||||
.username
|
.username
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap_or_else(|| "admin".to_string());
|
.unwrap_or_else(|| "admin".to_string());
|
||||||
let actor_id =
|
let actor_id = get_actor_id_from_name(WebfingerType::Person, &name, context).await?;
|
||||||
build_actor_id_from_shortname(EndpointType::Person, &name, &context.settings())?;
|
|
||||||
|
|
||||||
let person = ObjectId::<ApubPerson>::new(actor_id)
|
let person = ObjectId::<ApubPerson>::new(actor_id)
|
||||||
.dereference(context, &mut 0)
|
.dereference(context, &mut 0)
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
{
|
|
||||||
"comments_enabled": {
|
|
||||||
"id": "pt:commentsEnabled",
|
|
||||||
"type": "sc:Boolean"
|
|
||||||
},
|
|
||||||
"matrixUserId": {
|
|
||||||
"id": "as:alsoKnownAs",
|
|
||||||
"type": "sc:Text"
|
|
||||||
},
|
|
||||||
"moderators": "as:moderators",
|
|
||||||
"pt": "https://join-lemmy.org#",
|
|
||||||
"sc": "http://schema.org#",
|
|
||||||
"sensitive": "as:sensitive",
|
|
||||||
"stickied": "as:stickied"
|
|
||||||
},
|
|
||||||
"https://w3id.org/security/v1"
|
|
||||||
],
|
|
||||||
"id": "https://enterprise.lemmy.ml/comment/38741",
|
|
||||||
"type": "Note",
|
|
||||||
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
|
|
||||||
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
||||||
"inReplyTo": "https://enterprise.lemmy.ml/post/55143",
|
|
||||||
"content": "first comment!",
|
|
||||||
"mediaType": "text/html",
|
|
||||||
"source": {
|
|
||||||
"content": "first comment!",
|
|
||||||
"mediaType": "text/markdown"
|
|
||||||
},
|
|
||||||
"published": "2021-03-01T13:42:43.966208+00:00",
|
|
||||||
"updated": "2021-03-01T13:43:03.955787+00:00"
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
{
|
|
||||||
"comments_enabled": {
|
|
||||||
"id": "pt:commentsEnabled",
|
|
||||||
"type": "sc:Boolean"
|
|
||||||
},
|
|
||||||
"matrixUserId": {
|
|
||||||
"id": "as:alsoKnownAs",
|
|
||||||
"type": "sc:Text"
|
|
||||||
},
|
|
||||||
"moderators": "as:moderators",
|
|
||||||
"pt": "https://join-lemmy.org#",
|
|
||||||
"sc": "http://schema.org#",
|
|
||||||
"sensitive": "as:sensitive",
|
|
||||||
"stickied": "as:stickied"
|
|
||||||
},
|
|
||||||
"https://w3id.org/security/v1"
|
|
||||||
],
|
|
||||||
"id": "https://enterprise.lemmy.ml/private_message/1621",
|
|
||||||
"type": "ChatMessage",
|
|
||||||
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
|
|
||||||
"to": [
|
|
||||||
"https://queer.hacktivis.me/users/lanodan"
|
|
||||||
],
|
|
||||||
"content": "Hello hello, testing",
|
|
||||||
"mediaType": "text/html",
|
|
||||||
"source": {
|
|
||||||
"content": "Hello hello, testing",
|
|
||||||
"mediaType": "text/markdown"
|
|
||||||
},
|
|
||||||
"published": "2021-10-21T10:13:14.597721+00:00"
|
|
||||||
}
|
|
13
crates/apub/assets/lemmy/activities/community/add_mod.json
Normal file
13
crates/apub/assets/lemmy/activities/community/add_mod.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"target": "http://enterprise.lemmy.ml/c/main/moderators",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Add",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/add/ec069147-77c3-447f-88c8-0ef1df10403f"
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "http://enterprise.lemmy.ml/post/7",
|
||||||
|
"attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "post 4",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-11-01T12:11:22.871846+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/create/2807c9ec-3ad8-4859-a9e0-28b59b6e499f"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main/followers"
|
||||||
|
],
|
||||||
|
"type": "Announce",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/announce/8030b171-803a-4108-94b1-342688f375cf"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"target": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"type": "Block",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Remove",
|
||||||
|
"target": "http://enterprise.lemmy.ml/c/main/moderators",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/remove/aab114f8-cfbd-4935-a5b7-e1a64603650d"
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"object": "http://enterprise.lemmy.ml/post/7",
|
||||||
|
"summary": "report this post",
|
||||||
|
"type": "Flag",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/flag/98b0933f-5e45-4a95-a15f-e0dc86361ba4"
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"target": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"type": "Block",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/block/726f43ab-bd0e-4ab3-89c8-627e976f553c"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Undo",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/undo/06a20ffb-3e32-42fb-8f4c-674b36d7c557"
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Group",
|
||||||
|
"id": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"preferredUsername": "main",
|
||||||
|
"name": "The Updated Community",
|
||||||
|
"summary": "<p>updated 2</p>\n",
|
||||||
|
"source": {
|
||||||
|
"content": "updated 2",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"sensitive": false,
|
||||||
|
"moderators": "http://enterprise.lemmy.ml/c/main/moderators",
|
||||||
|
"inbox": "http://enterprise.lemmy.ml/c/main/inbox",
|
||||||
|
"outbox": "http://enterprise.lemmy.ml/c/main/outbox",
|
||||||
|
"followers": "http://enterprise.lemmy.ml/c/main/followers",
|
||||||
|
"endpoints": {
|
||||||
|
"sharedInbox": "http://enterprise.lemmy.ml/inbox"
|
||||||
|
},
|
||||||
|
"publicKey": {
|
||||||
|
"id": "http://enterprise.lemmy.ml/c/main#main-key",
|
||||||
|
"owner": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA16Xh06V1l2yy0WAIMUTV\nnvZIuAuKDxzDQUNT+n8gmcVuvBu7tkpbPTQ3DjGB3bQfGC2ekew/yldwOXyZ7ry1\npbJSYSrCBJrAlPLs/ao3OPTqmcl3vnSWti/hqopEV+Um2t7fwpkCjVrnzVKRSlys\nihnrth64ZiwAqq2llpaXzWc1SR2URZYSdnry/4d9UNrZVkumIeg1gk9KbCAo4j/O\njsv/aBjpZcTeLmtMZf6fcrvGre9duJdx6e2Tg/YNcnSnARosqev/UwVTzzGNVWXg\n9rItaa0a0aea4se4Bn6QXvOBbcq3+OYZMR6a34hh5BTeNG8WbpwmVahS0WFUsv9G\nswIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"published": "2021-10-29T15:05:51.476984+00:00",
|
||||||
|
"updated": "2021-11-01T12:23:50.151874+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Update",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/update/d3717cf5-096d-473f-9530-5d52f9d51f5f"
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "http://ds9.lemmy.ml/comment/1",
|
||||||
|
"attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"content": "hello",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"source": {
|
||||||
|
"content": "hello",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"inReplyTo": "http://ds9.lemmy.ml/post/1",
|
||||||
|
"published": "2021-11-01T11:45:49.794920+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main",
|
||||||
|
"http://ds9.lemmy.ml/u/lemmy_alpha"
|
||||||
|
],
|
||||||
|
"tag": [],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/create/1e77d67c-44ac-45ed-bf2a-460e21f60236"
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "http://ds9.lemmy.ml/post/1",
|
||||||
|
"attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "test post",
|
||||||
|
"content": "<p>test body</p>\n",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"source": {
|
||||||
|
"content": "test body",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-10-29T15:10:51.557399+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf"
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "http://ds9.lemmy.ml/post/1",
|
||||||
|
"attributedTo": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "test post 1",
|
||||||
|
"content": "<p>test body</p>\n",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"source": {
|
||||||
|
"content": "test body",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-10-29T15:10:51.557399+00:00",
|
||||||
|
"updated": "2021-10-29T15:11:35.976374+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Update",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/update/ab360117-e165-4de4-b7fc-906b62c98631"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/post/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Delete",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/delete/f2abee48-c7bb-41d5-9e27-8775ff32db12"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/comment/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Delete",
|
||||||
|
"summary": "bad comment",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/delete/42ca1a79-f99e-4518-a2ca-ba2df221eb5e"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/post/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Delete",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/delete/b13cca96-7737-41e1-9769-8fbf972b3509"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Undo",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/undo/5e939cfb-b8a1-4de8-950f-9d684e9162b9"
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/comment/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Delete",
|
||||||
|
"summary": "bad comment",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/delete/2598435c-87a3-49cd-81f3-a44b03b7af9d"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Undo",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/undo/a850cf21-3866-4b3a-b80b-56aa00997fee"
|
||||||
|
}
|
17
crates/apub/assets/lemmy/activities/following/accept.json
Normal file
17
crates/apub/assets/lemmy/activities/following/accept.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"to": [
|
||||||
|
"http://ds9.lemmy.ml/u/lemmy_alpha"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"object": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"type": "Follow",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/follow/6abcd50b-b8ca-4952-86b0-a6dd8cc12866"
|
||||||
|
},
|
||||||
|
"type": "Accept",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/accept/75f080cc-3d45-4654-8186-8f3bb853fa27"
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"object": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"type": "Follow",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/follow/6abcd50b-b8ca-4952-86b0-a6dd8cc12866"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"object": "http://enterprise.lemmy.ml/c/main",
|
||||||
|
"type": "Follow",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/follow/dc2f1bc5-f3a0-4daa-a46b-428cbfbd023c"
|
||||||
|
},
|
||||||
|
"type": "Undo",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/undo/dd83c482-8ebd-4b6c-9008-c8373bd1a86a"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/create/987d05fa-f637-46d7-85be-13d112bc269f",
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"http://ds9.lemmy.ml/u/lemmy_alpha"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "ChatMessage",
|
||||||
|
"id": "http://enterprise.lemmy.ml/private_message/1",
|
||||||
|
"attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"http://ds9.lemmy.ml/u/lemmy_alpha"
|
||||||
|
],
|
||||||
|
"content": "hello",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"source": {
|
||||||
|
"content": "hello",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"published": "2021-10-29T15:31:56.058289+00:00"
|
||||||
|
},
|
||||||
|
"type": "Create"
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/u/lemmy_beta"
|
||||||
|
],
|
||||||
|
"object": "http://enterprise.lemmy.ml/private_message/1",
|
||||||
|
"type": "Delete",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/delete/041d9858-5eef-4ad9-84ae-7455b4d87ed9"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"http://ds9.lemmy.ml/u/lemmy_alpha"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"http://enterprise.lemmy.ml/u/lemmy_beta"
|
||||||
|
],
|
||||||
|
"object": "http://enterprise.lemmy.ml/private_message/1",
|
||||||
|
"type": "Delete",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/delete/616c41be-04ed-4bd4-b865-30712186b122"
|
||||||
|
},
|
||||||
|
"type": "Undo",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/undo/35e5b337-014c-4bbe-8d63-6fac96f51409"
|
||||||
|
}
|
12
crates/apub/assets/lemmy/activities/voting/dislike_page.json
Normal file
12
crates/apub/assets/lemmy/activities/voting/dislike_page.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/post/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Dislike",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/dislike/64d40d40-a829-43a5-8247-1fb595b3ca1c"
|
||||||
|
}
|
12
crates/apub/assets/lemmy/activities/voting/like_note.json
Normal file
12
crates/apub/assets/lemmy/activities/voting/like_note.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/comment/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Like",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/like/fd61d070-7382-46a9-b2b7-6bb253732877"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/post/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Like",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/like/2227ab2c-79e2-4fca-a1d2-1d67dacf2457"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Undo",
|
||||||
|
"id": "http://enterprise.lemmy.ml/activities/undo/6cc6fb71-39fe-49ea-9506-f0423b101e98"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": "http://ds9.lemmy.ml/comment/1",
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Like",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/like/efcf7ae2-dfcc-4ff4-9ce4-6adf251ff004"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"http://enterprise.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Undo",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/undo/3518565c-24a7-4d9e-8e0a-f7a2f45ac618"
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"id": "http://enterprise.lemmy.ml/c/main/followers",
|
||||||
|
"type": "Collection",
|
||||||
|
"totalItems": 3,
|
||||||
|
"items": []
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"id": "https://enterprise.lemmy.ml/c/tenforward/moderators",
|
||||||
|
"orderedItems": [
|
||||||
|
"https://enterprise.lemmy.ml/u/picard"
|
||||||
|
]
|
||||||
|
}
|
209
crates/apub/assets/lemmy/collections/group_outbox.json
Normal file
209
crates/apub/assets/lemmy/collections/group_outbox.json
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
{
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"id": "https://ds9.lemmy.ml/c/main/outbox",
|
||||||
|
"totalItems": 7,
|
||||||
|
"orderedItems": [
|
||||||
|
{
|
||||||
|
"actor": "https://ds9.lemmy.ml/u/dess_ds9",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "https://ds9.lemmy.ml/post/1685",
|
||||||
|
"attributedTo": "https://ds9.lemmy.ml/u/dess_ds9",
|
||||||
|
"to": [
|
||||||
|
"https://ds9.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "Test post",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-09-30T16:37:58.425718+00:00",
|
||||||
|
"updated": "2021-09-30T16:39:50.934055+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"https://ds9.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://ds9.lemmy.ml/activities/create/157bc329-05cb-4dc3-ad9e-5110fde3f3aa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actor": "https://ds9.lemmy.ml/u/nutomic",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "https://ds9.lemmy.ml/post/1665",
|
||||||
|
"attributedTo": "https://ds9.lemmy.ml/u/nutomic",
|
||||||
|
"to": [
|
||||||
|
"https://ds9.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "another webmention test",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"url": "https://webmention.rocks/test/1",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-09-17T13:22:15.026912+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"https://ds9.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://ds9.lemmy.ml/activities/create/c54e4509-16ac-42bf-b3b4-0bf8516f8152"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actor": "https://ds9.lemmy.ml/u/nutomic",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "https://ds9.lemmy.ml/post/1664",
|
||||||
|
"attributedTo": "https://ds9.lemmy.ml/u/nutomic",
|
||||||
|
"to": [
|
||||||
|
"https://ds9.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "another test",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"url": "https://webmention.rocks/test/1",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-09-17T13:13:21.675891+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"https://ds9.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://ds9.lemmy.ml/activities/create/25f7d2cb-11d5-4c9c-aa3c-85fbff9f9e0c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actor": "https://ds9.lemmy.ml/u/nutomic",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "https://ds9.lemmy.ml/post/1663",
|
||||||
|
"attributedTo": "https://ds9.lemmy.ml/u/nutomic",
|
||||||
|
"to": [
|
||||||
|
"https://ds9.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "Webmention test from Lemmy",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"url": "https://webmention.rocks/test/1",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-09-17T13:00:15.392844+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"https://ds9.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://ds9.lemmy.ml/activities/create/cfbd12b8-2e11-42b6-a609-b482decbaf11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actor": "https://ds9.lemmy.ml/u/dess_tester_3",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "https://ds9.lemmy.ml/post/1644",
|
||||||
|
"attributedTo": "https://ds9.lemmy.ml/u/dess_tester_3",
|
||||||
|
"to": [
|
||||||
|
"https://ds9.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "The best wireless earbuds you can buy right now | Engadget",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"url": "https://www.engadget.com/best-wireless-earbuds-120058222.html",
|
||||||
|
"image": {
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://ds9.lemmy.ml/pictrs/image/0WWsYOuwAE.jpg"
|
||||||
|
},
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-08-26T01:22:06.428368+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"https://ds9.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://ds9.lemmy.ml/activities/create/76c94408-944a-4a2f-a88b-d10f12b472b0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actor": "https://ds9.lemmy.ml/u/dess_ds9",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "https://ds9.lemmy.ml/post/1643",
|
||||||
|
"attributedTo": "https://ds9.lemmy.ml/u/dess_ds9",
|
||||||
|
"to": [
|
||||||
|
"https://ds9.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "First Look: Cadillac’s luxury EV debut seems like a winner | Engadges",
|
||||||
|
"content": "<p>test</p>\n",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"source": {
|
||||||
|
"content": "test",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"url": "https://www.engadget.com/cadillac-lyriq-luxury-ev-first-look-video-171543752.html",
|
||||||
|
"image": {
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://ds9.lemmy.ml/pictrs/image/gnmtvgXP31.jpg"
|
||||||
|
},
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-08-23T23:43:06.560543+00:00",
|
||||||
|
"updated": "2021-08-23T23:52:51.832606+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"https://ds9.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://ds9.lemmy.ml/activities/create/b1f95918-f593-4951-91cf-2c3340cd9509"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actor": "https://ds9.lemmy.ml/u/dess_ds9_2",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"object": {
|
||||||
|
"type": "Page",
|
||||||
|
"id": "https://ds9.lemmy.ml/post/1642",
|
||||||
|
"attributedTo": "https://ds9.lemmy.ml/u/dess_ds9_2",
|
||||||
|
"to": [
|
||||||
|
"https://ds9.lemmy.ml/c/main",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"name": "A test post from DS9",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"commentsEnabled": true,
|
||||||
|
"sensitive": false,
|
||||||
|
"stickied": false,
|
||||||
|
"published": "2021-08-06T14:10:47.493075+00:00"
|
||||||
|
},
|
||||||
|
"cc": [
|
||||||
|
"https://ds9.lemmy.ml/c/main"
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://ds9.lemmy.ml/activities/create/6359b2e7-badb-4241-b5ee-b093078361bd"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
6
crates/apub/assets/lemmy/collections/person_outbox.json
Normal file
6
crates/apub/assets/lemmy/collections/person_outbox.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"id": "http://ds9.lemmy.ml/u/lemmy_alpha/outbox",
|
||||||
|
"orderedItems": [],
|
||||||
|
"totalItems": 0
|
||||||
|
}
|
15
crates/apub/assets/lemmy/objects/chat_message.json
Normal file
15
crates/apub/assets/lemmy/objects/chat_message.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"id": "https://enterprise.lemmy.ml/private_message/1621",
|
||||||
|
"type": "ChatMessage",
|
||||||
|
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
|
||||||
|
"to": [
|
||||||
|
"https://queer.hacktivis.me/users/lanodan"
|
||||||
|
],
|
||||||
|
"content": "<p>Hello hello, testing</p>\n",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"source": {
|
||||||
|
"content": "Hello hello, testing",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"published": "2021-10-21T10:13:14.597721+00:00"
|
||||||
|
}
|
|
@ -1,23 +1,4 @@
|
||||||
{
|
{
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
{
|
|
||||||
"comments_enabled": {
|
|
||||||
"id": "pt:commentsEnabled",
|
|
||||||
"type": "sc:Boolean"
|
|
||||||
},
|
|
||||||
"matrixUserId": {
|
|
||||||
"id": "as:alsoKnownAs",
|
|
||||||
"type": "sc:Text"
|
|
||||||
},
|
|
||||||
"moderators": "as:moderators",
|
|
||||||
"pt": "https://join-lemmy.org#",
|
|
||||||
"sc": "http://schema.org#",
|
|
||||||
"sensitive": "as:sensitive",
|
|
||||||
"stickied": "as:stickied"
|
|
||||||
},
|
|
||||||
"https://w3id.org/security/v1"
|
|
||||||
],
|
|
||||||
"id": "https://enterprise.lemmy.ml/c/tenforward",
|
"id": "https://enterprise.lemmy.ml/c/tenforward",
|
||||||
"type": "Group",
|
"type": "Group",
|
||||||
"preferredUsername": "main",
|
"preferredUsername": "main",
|
15
crates/apub/assets/lemmy/objects/note.json
Normal file
15
crates/apub/assets/lemmy/objects/note.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"id": "https://enterprise.lemmy.ml/comment/38741",
|
||||||
|
"type": "Note",
|
||||||
|
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
|
||||||
|
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"inReplyTo": "https://enterprise.lemmy.ml/post/55143",
|
||||||
|
"content": "<p>first comment!</p>\n",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"source": {
|
||||||
|
"content": "first comment!",
|
||||||
|
"mediaType": "text/markdown"
|
||||||
|
},
|
||||||
|
"published": "2021-03-01T13:42:43.966208+00:00",
|
||||||
|
"updated": "2021-03-01T13:43:03.955787+00:00"
|
||||||
|
}
|
|
@ -1,23 +1,4 @@
|
||||||
{
|
{
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
{
|
|
||||||
"comments_enabled": {
|
|
||||||
"id": "pt:commentsEnabled",
|
|
||||||
"type": "sc:Boolean"
|
|
||||||
},
|
|
||||||
"matrixUserId": {
|
|
||||||
"id": "as:alsoKnownAs",
|
|
||||||
"type": "sc:Text"
|
|
||||||
},
|
|
||||||
"moderators": "as:moderators",
|
|
||||||
"pt": "https://join-lemmy.org#",
|
|
||||||
"sc": "http://schema.org#",
|
|
||||||
"sensitive": "as:sensitive",
|
|
||||||
"stickied": "as:stickied"
|
|
||||||
},
|
|
||||||
"https://w3id.org/security/v1"
|
|
||||||
],
|
|
||||||
"id": "https://enterprise.lemmy.ml/post/55143",
|
"id": "https://enterprise.lemmy.ml/post/55143",
|
||||||
"type": "Page",
|
"type": "Page",
|
||||||
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
|
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
|
||||||
|
@ -32,6 +13,11 @@
|
||||||
"content": "This is a post in the /c/tenforward community",
|
"content": "This is a post in the /c/tenforward community",
|
||||||
"mediaType": "text/markdown"
|
"mediaType": "text/markdown"
|
||||||
},
|
},
|
||||||
|
"url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png",
|
||||||
|
"image": {
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png"
|
||||||
|
},
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
"commentsEnabled": true,
|
"commentsEnabled": true,
|
||||||
"stickied": true,
|
"stickied": true,
|
|
@ -1,23 +1,4 @@
|
||||||
{
|
{
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
{
|
|
||||||
"comments_enabled": {
|
|
||||||
"id": "pt:commentsEnabled",
|
|
||||||
"type": "sc:Boolean"
|
|
||||||
},
|
|
||||||
"matrixUserId": {
|
|
||||||
"id": "as:alsoKnownAs",
|
|
||||||
"type": "sc:Text"
|
|
||||||
},
|
|
||||||
"moderators": "as:moderators",
|
|
||||||
"pt": "https://join-lemmy.org#",
|
|
||||||
"sc": "http://schema.org#",
|
|
||||||
"sensitive": "as:sensitive",
|
|
||||||
"stickied": "as:stickied"
|
|
||||||
},
|
|
||||||
"https://w3id.org/security/v1"
|
|
||||||
],
|
|
||||||
"id": "https://enterprise.lemmy.ml/u/picard",
|
"id": "https://enterprise.lemmy.ml/u/picard",
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
"preferredUsername": "picard",
|
"preferredUsername": "picard",
|
||||||
|
@ -35,6 +16,7 @@
|
||||||
"type": "Image",
|
"type": "Image",
|
||||||
"url": "https://enterprise.lemmy.ml/pictrs/image/XenaYI5hTn.png"
|
"url": "https://enterprise.lemmy.ml/pictrs/image/XenaYI5hTn.png"
|
||||||
},
|
},
|
||||||
|
"matrix_user_id": "@picard:matrix.org",
|
||||||
"inbox": "https://enterprise.lemmy.ml/u/picard/inbox",
|
"inbox": "https://enterprise.lemmy.ml/u/picard/inbox",
|
||||||
"outbox": "https://enterprise.lemmy.ml/u/picard/outbox",
|
"outbox": "https://enterprise.lemmy.ml/u/picard/outbox",
|
||||||
"endpoints": {
|
"endpoints": {
|
52
crates/apub/assets/pleroma/activities/create_note.json
Normal file
52
crates/apub/assets/pleroma/activities/create_note.json
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://greenish.red/schemas/litepub-0.1.jsonld",
|
||||||
|
{
|
||||||
|
"@language": "und"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actor": "https://greenish.red/users/nutomic",
|
||||||
|
"cc": [
|
||||||
|
"https://greenish.red/users/nutomic/followers"
|
||||||
|
],
|
||||||
|
"context": "https://greenish.red/contexts/f6244742-0526-4b84-ac4f-ceadf1fb4e56",
|
||||||
|
"context_id": 6336544,
|
||||||
|
"directMessage": false,
|
||||||
|
"id": "https://greenish.red/activities/db61d52b-9c35-486a-bf27-bbd4edc6c6a1",
|
||||||
|
"object": {
|
||||||
|
"actor": "https://greenish.red/users/nutomic",
|
||||||
|
"attachment": [],
|
||||||
|
"attributedTo": "https://greenish.red/users/nutomic",
|
||||||
|
"cc": [
|
||||||
|
"https://greenish.red/users/nutomic/followers"
|
||||||
|
],
|
||||||
|
"content": "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"ACimPLEXPDd7enu3cm\" href=\"https://enterprise.lemmy.ml/u/picard\" rel=\"ugc\">@<span>lanodan</span></a></span> test",
|
||||||
|
"context": "https://greenish.red/contexts/f6244742-0526-4b84-ac4f-ceadf1fb4e56",
|
||||||
|
"conversation": "https://greenish.red/contexts/f6244742-0526-4b84-ac4f-ceadf1fb4e56",
|
||||||
|
"id": "https://greenish.red/objects/1a522f2e-d5ab-454b-93d7-e58bc0650c2a",
|
||||||
|
"inReplyTo": "https://enterprise.lemmy.ml/post/55143",
|
||||||
|
"published": "2021-10-26T10:28:35.602455Z",
|
||||||
|
"sensitive": false,
|
||||||
|
"source": "@lanodan@ds9.lemmy.ml test",
|
||||||
|
"summary": "",
|
||||||
|
"tag": [
|
||||||
|
{
|
||||||
|
"href": "https://enterprise.lemmy.ml/u/picard",
|
||||||
|
"name": "@lanodan@ds9.lemmy.ml",
|
||||||
|
"type": "Mention"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
"https://enterprise.lemmy.ml/u/picard",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"type": "Note"
|
||||||
|
},
|
||||||
|
"published": "2021-10-26T10:28:35.595650Z",
|
||||||
|
"to": [
|
||||||
|
"https://enterprise.lemmy.ml/u/picard",
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"type": "Create"
|
||||||
|
}
|
|
@ -1,28 +1,9 @@
|
||||||
use crate::{
|
use activitystreams::public;
|
||||||
activities::{
|
|
||||||
check_community_deleted_or_removed,
|
|
||||||
comment::{collect_non_local_mentions, get_notif_recipients},
|
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
|
||||||
extract_community,
|
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
verify_person_in_community,
|
|
||||||
CreateOrUpdateType,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{
|
|
||||||
comment::{ApubComment, Note},
|
|
||||||
community::ApubCommunity,
|
|
||||||
person::ApubPerson,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed};
|
|
||||||
use lemmy_api_common::{blocking, check_post_deleted_or_removed};
|
use lemmy_api_common::{blocking, check_post_deleted_or_removed};
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
|
traits::{ActivityHandler, ActorType, ApubObject},
|
||||||
values::PublicUrl,
|
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -31,25 +12,22 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
use crate::{
|
||||||
#[serde(rename_all = "camelCase")]
|
activities::{
|
||||||
pub struct CreateOrUpdateComment {
|
check_community_deleted_or_removed,
|
||||||
actor: ObjectId<ApubPerson>,
|
comment::{collect_non_local_mentions, get_notif_recipients},
|
||||||
to: [PublicUrl; 1],
|
community::{announce::GetCommunity, send_to_community},
|
||||||
object: Note,
|
generate_activity_id,
|
||||||
cc: Vec<Url>,
|
verify_activity,
|
||||||
tag: Vec<Mention>,
|
verify_is_public,
|
||||||
#[serde(rename = "type")]
|
verify_person_in_community,
|
||||||
kind: CreateOrUpdateType,
|
},
|
||||||
id: Url,
|
activity_lists::AnnouncableActivities,
|
||||||
#[serde(rename = "@context")]
|
fetcher::object_id::ObjectId,
|
||||||
context: OneOrMany<AnyBase>,
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
||||||
#[serde(flatten)]
|
protocol::activities::{create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType},
|
||||||
unparsed: Unparsed,
|
};
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateOrUpdateComment {
|
impl CreateOrUpdateComment {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -76,13 +54,12 @@ impl CreateOrUpdateComment {
|
||||||
|
|
||||||
let create_or_update = CreateOrUpdateComment {
|
let create_or_update = CreateOrUpdateComment {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: comment.to_apub(context).await?,
|
object: comment.to_apub(context).await?,
|
||||||
cc: maa.ccs,
|
cc: maa.ccs,
|
||||||
tag: maa.tags,
|
tag: maa.tags,
|
||||||
kind,
|
kind,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,12 +77,12 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = extract_community(&self.cc, context, request_counter).await?;
|
verify_is_public(&self.to)?;
|
||||||
let community_id = ObjectId::new(community.actor_id());
|
|
||||||
let post = self.object.get_parents(context, request_counter).await?.0;
|
let post = self.object.get_parents(context, request_counter).await?.0;
|
||||||
|
let community = self.get_community(context, request_counter).await?;
|
||||||
|
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &community_id, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
||||||
check_community_deleted_or_removed(&community)?;
|
check_community_deleted_or_removed(&community)?;
|
||||||
check_post_deleted_or_removed(&post)?;
|
check_post_deleted_or_removed(&post)?;
|
||||||
|
@ -135,3 +112,19 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for CreateOrUpdateComment {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
let post = self.object.get_parents(context, request_counter).await?.0;
|
||||||
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
Community::read(conn, post.community_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
Ok(community.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,28 +1,24 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
community::{announce::GetCommunity, get_community_from_moderators_url, send_to_community},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_add_remove_moderator_target,
|
verify_add_remove_moderator_target,
|
||||||
|
verify_is_public,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
context::lemmy_context,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::community::add_mod::AddMod,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{activity::kind::AddType, public};
|
||||||
activity::kind::AddType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::community::{CommunityModerator, CommunityModeratorForm},
|
source::community::{CommunityModerator, CommunityModeratorForm},
|
||||||
|
@ -30,25 +26,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct AddMod {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: [PublicUrl; 1],
|
|
||||||
object: ObjectId<ApubPerson>,
|
|
||||||
target: Url,
|
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: AddType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddMod {
|
impl AddMod {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -63,13 +40,12 @@ impl AddMod {
|
||||||
)?;
|
)?;
|
||||||
let add = AddMod {
|
let add = AddMod {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: ObjectId::new(added_mod.actor_id()),
|
object: ObjectId::new(added_mod.actor_id()),
|
||||||
target: generate_moderators_url(&community.actor_id)?.into(),
|
target: generate_moderators_url(&community.actor_id)?.into(),
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind: AddType::Add,
|
kind: AddType::Add,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,10 +64,12 @@ impl ActivityHandler for AddMod {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_add_remove_moderator_target(&self.target, &self.cc[0])?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
|
verify_add_remove_moderator_target(&self.target, &community)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +78,7 @@ impl ActivityHandler for AddMod {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = self.cc[0].dereference(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
let new_mod = self.object.dereference(context, request_counter).await?;
|
let new_mod = self.object.dereference(context, request_counter).await?;
|
||||||
|
|
||||||
// If we had to refetch the community while parsing the activity, then the new mod has already
|
// If we had to refetch the community while parsing the activity, then the new mod has already
|
||||||
|
@ -124,3 +102,14 @@ impl ActivityHandler for AddMod {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for AddMod {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
get_community_from_moderators_url(&self.target, context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,76 +1,28 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public},
|
||||||
comment::create_or_update::CreateOrUpdateComment,
|
activity_lists::AnnouncableActivities,
|
||||||
community::{
|
|
||||||
add_mod::AddMod,
|
|
||||||
block_user::BlockUserFromCommunity,
|
|
||||||
list_community_follower_inboxes,
|
|
||||||
remove_mod::RemoveMod,
|
|
||||||
undo_block_user::UndoBlockUserFromCommunity,
|
|
||||||
update::UpdateCommunity,
|
|
||||||
},
|
|
||||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
|
||||||
generate_activity_id,
|
|
||||||
post::create_or_update::CreateOrUpdatePost,
|
|
||||||
verify_activity,
|
|
||||||
verify_community,
|
|
||||||
voting::{undo_vote::UndoVote, vote::Vote},
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
http::is_activity_already_known,
|
http::is_activity_already_known,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
send_lemmy_activity,
|
protocol::activities::community::announce::AnnounceActivity,
|
||||||
CommunityType,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::AnnounceType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::{activity::kind::AnnounceType, public};
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityFields, ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
#[async_trait::async_trait(?Send)]
|
||||||
#[serde(untagged)]
|
pub(crate) trait GetCommunity {
|
||||||
#[activity_handler(LemmyContext)]
|
async fn get_community(
|
||||||
pub enum AnnouncableActivities {
|
&self,
|
||||||
CreateOrUpdateComment(CreateOrUpdateComment),
|
context: &LemmyContext,
|
||||||
CreateOrUpdatePost(Box<CreateOrUpdatePost>),
|
request_counter: &mut i32,
|
||||||
Vote(Vote),
|
) -> Result<ApubCommunity, LemmyError>;
|
||||||
UndoVote(UndoVote),
|
|
||||||
Delete(Delete),
|
|
||||||
UndoDelete(UndoDelete),
|
|
||||||
UpdateCommunity(Box<UpdateCommunity>),
|
|
||||||
BlockUserFromCommunity(BlockUserFromCommunity),
|
|
||||||
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
|
||||||
AddMod(AddMod),
|
|
||||||
RemoveMod(RemoveMod),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct AnnounceActivity {
|
|
||||||
actor: ObjectId<ApubCommunity>,
|
|
||||||
to: [PublicUrl; 1],
|
|
||||||
object: AnnouncableActivities,
|
|
||||||
cc: Vec<Url>,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: AnnounceType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnnounceActivity {
|
impl AnnounceActivity {
|
||||||
|
@ -82,18 +34,19 @@ impl AnnounceActivity {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let announce = AnnounceActivity {
|
let announce = AnnounceActivity {
|
||||||
actor: ObjectId::new(community.actor_id()),
|
actor: ObjectId::new(community.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object,
|
object,
|
||||||
cc: vec![community.followers_url()],
|
cc: vec![community.followers_url.clone().into_inner()],
|
||||||
kind: AnnounceType::Announce,
|
kind: AnnounceType::Announce,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
&AnnounceType::Announce,
|
&AnnounceType::Announce,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?,
|
)?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
|
let inboxes = community
|
||||||
|
.get_follower_inboxes(additional_inboxes, context)
|
||||||
|
.await?;
|
||||||
send_lemmy_activity(context, &announce, &announce.id, community, inboxes, false).await
|
send_lemmy_activity(context, &announce, &announce.id, community, inboxes, false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +59,8 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_community(&self.actor, context, request_counter).await?;
|
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,22 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
community::{announce::GetCommunity, send_to_community},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
|
verify_is_public,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
context::lemmy_context,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::community::block_user::BlockUserFromCommunity,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{activity::kind::BlockType, public};
|
||||||
activity::kind::BlockType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::community::{
|
source::community::{
|
||||||
|
@ -33,24 +29,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BlockUserFromCommunity {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: [PublicUrl; 1],
|
|
||||||
pub(in crate::activities::community) object: ObjectId<ApubPerson>,
|
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: BlockType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockUserFromCommunity {
|
impl BlockUserFromCommunity {
|
||||||
pub(in crate::activities::community) fn new(
|
pub(in crate::activities::community) fn new(
|
||||||
|
@ -61,15 +39,15 @@ impl BlockUserFromCommunity {
|
||||||
) -> Result<BlockUserFromCommunity, LemmyError> {
|
) -> Result<BlockUserFromCommunity, LemmyError> {
|
||||||
Ok(BlockUserFromCommunity {
|
Ok(BlockUserFromCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: ObjectId::new(target.actor_id()),
|
object: ObjectId::new(target.actor_id()),
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
|
target: ObjectId::new(community.actor_id()),
|
||||||
kind: BlockType::Block,
|
kind: BlockType::Block,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
BlockType::Block,
|
BlockType::Block,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?,
|
)?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -97,9 +75,11 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +88,7 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = self.cc[0].dereference(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
let blocked_user = self.object.dereference(context, request_counter).await?;
|
let blocked_user = self.object.dereference(context, request_counter).await?;
|
||||||
|
|
||||||
let community_user_ban_form = CommunityPersonBanForm {
|
let community_user_ban_form = CommunityPersonBanForm {
|
||||||
|
@ -136,3 +116,14 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for BlockUserFromCommunity {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
self.target.dereference(context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::community::announce::{AnnouncableActivities, AnnounceActivity},
|
activities::send_lemmy_activity,
|
||||||
check_is_apub_id_valid,
|
activity_lists::AnnouncableActivities,
|
||||||
|
fetcher::object_id::ObjectId,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
send_lemmy_activity,
|
protocol::activities::community::announce::AnnounceActivity,
|
||||||
CommunityType,
|
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
use lemmy_apub_lib::traits::ActorType;
|
use lemmy_apub_lib::traits::ActorType;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
@ -16,31 +15,10 @@ pub mod add_mod;
|
||||||
pub mod announce;
|
pub mod announce;
|
||||||
pub mod block_user;
|
pub mod block_user;
|
||||||
pub mod remove_mod;
|
pub mod remove_mod;
|
||||||
|
pub mod report;
|
||||||
pub mod undo_block_user;
|
pub mod undo_block_user;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
async fn list_community_follower_inboxes(
|
|
||||||
community: &ApubCommunity,
|
|
||||||
additional_inboxes: Vec<Url>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<Vec<Url>, LemmyError> {
|
|
||||||
Ok(
|
|
||||||
vec![
|
|
||||||
community
|
|
||||||
.get_follower_inboxes(context.pool(), &context.settings())
|
|
||||||
.await?,
|
|
||||||
additional_inboxes,
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.flatten()
|
|
||||||
.unique()
|
|
||||||
.filter(|inbox| inbox.host_str() != Some(&context.settings().hostname))
|
|
||||||
.filter(|inbox| check_is_apub_id_valid(inbox, false, &context.settings()).is_ok())
|
|
||||||
.map(|inbox| inbox.to_owned())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn send_to_community<T: ActorType>(
|
pub(crate) async fn send_to_community<T: ActorType>(
|
||||||
activity: AnnouncableActivities,
|
activity: AnnouncableActivities,
|
||||||
activity_id: &Url,
|
activity_id: &Url,
|
||||||
|
@ -61,3 +39,14 @@ pub(crate) async fn send_to_community<T: ActorType>(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_community_from_moderators_url(
|
||||||
|
moderators: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
let community_id = Url::parse(&moderators.to_string().replace("/moderators", ""))?;
|
||||||
|
ObjectId::new(community_id)
|
||||||
|
.dereference(context, request_counter)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
|
@ -1,28 +1,24 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
community::{announce::GetCommunity, get_community_from_moderators_url, send_to_community},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_add_remove_moderator_target,
|
verify_add_remove_moderator_target,
|
||||||
|
verify_is_public,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
context::lemmy_context,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::community::remove_mod::RemoveMod,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{activity::kind::RemoveType, public};
|
||||||
activity::kind::RemoveType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::community::{CommunityModerator, CommunityModeratorForm},
|
source::community::{CommunityModerator, CommunityModeratorForm},
|
||||||
|
@ -30,26 +26,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct RemoveMod {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: [PublicUrl; 1],
|
|
||||||
pub(in crate::activities) object: ObjectId<ApubPerson>,
|
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: RemoveType,
|
|
||||||
// if target is set, this is means remove mod from community
|
|
||||||
pub(in crate::activities) target: Url,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RemoveMod {
|
impl RemoveMod {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -64,12 +40,11 @@ impl RemoveMod {
|
||||||
)?;
|
)?;
|
||||||
let remove = RemoveMod {
|
let remove = RemoveMod {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: ObjectId::new(removed_mod.actor_id()),
|
object: ObjectId::new(removed_mod.actor_id()),
|
||||||
target: generate_moderators_url(&community.actor_id)?.into(),
|
target: generate_moderators_url(&community.actor_id)?.into(),
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
cc: vec![community.actor_id()],
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
|
||||||
kind: RemoveType::Remove,
|
kind: RemoveType::Remove,
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
@ -88,10 +63,12 @@ impl ActivityHandler for RemoveMod {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_add_remove_moderator_target(&self.target, &self.cc[0])?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
|
verify_add_remove_moderator_target(&self.target, &community)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +77,7 @@ impl ActivityHandler for RemoveMod {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = self.cc[0].dereference(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
let remove_mod = self.object.dereference(context, request_counter).await?;
|
let remove_mod = self.object.dereference(context, request_counter).await?;
|
||||||
|
|
||||||
let form = CommunityModeratorForm {
|
let form = CommunityModeratorForm {
|
||||||
|
@ -115,3 +92,14 @@ impl ActivityHandler for RemoveMod {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for RemoveMod {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
get_community_from_moderators_url(&self.target, context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,9 @@
|
||||||
use crate::{
|
use activitystreams::activity::kind::FlagType;
|
||||||
activities::{generate_activity_id, verify_activity, verify_person_in_community},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
send_lemmy_activity,
|
|
||||||
PostOrComment,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::FlagType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse};
|
use lemmy_api_common::{blocking, comment::CommentReportResponse, post::PostReportResponse};
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -27,24 +15,19 @@ use lemmy_db_schema::{
|
||||||
use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
|
use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
use crate::{
|
||||||
#[serde(rename_all = "camelCase")]
|
activities::{
|
||||||
pub struct Report {
|
generate_activity_id,
|
||||||
actor: ObjectId<ApubPerson>,
|
send_lemmy_activity,
|
||||||
to: [ObjectId<ApubCommunity>; 1],
|
verify_activity,
|
||||||
object: ObjectId<PostOrComment>,
|
verify_person_in_community,
|
||||||
summary: String,
|
},
|
||||||
#[serde(rename = "type")]
|
fetcher::object_id::ObjectId,
|
||||||
kind: FlagType,
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
id: Url,
|
protocol::activities::community::report::Report,
|
||||||
#[serde(rename = "@context")]
|
PostOrComment,
|
||||||
context: OneOrMany<AnyBase>,
|
};
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -67,7 +50,6 @@ impl Report {
|
||||||
summary: reason,
|
summary: reason,
|
||||||
kind,
|
kind,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
send_lemmy_activity(
|
send_lemmy_activity(
|
||||||
|
@ -91,7 +73,8 @@ impl ActivityHandler for Report {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.to[0], context, request_counter).await?;
|
let community = self.to[0].dereference(context, request_counter).await?;
|
||||||
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,25 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{
|
community::{announce::GetCommunity, send_to_community},
|
||||||
announce::AnnouncableActivities,
|
|
||||||
block_user::BlockUserFromCommunity,
|
|
||||||
send_to_community,
|
|
||||||
},
|
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
|
verify_is_public,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
context::lemmy_context,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::community::{
|
||||||
|
block_user::BlockUserFromCommunity,
|
||||||
|
undo_block_user::UndoBlockUserFromCommunity,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{activity::kind::UndoType, public};
|
||||||
activity::kind::UndoType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::community::{CommunityPersonBan, CommunityPersonBanForm},
|
source::community::{CommunityPersonBan, CommunityPersonBanForm},
|
||||||
|
@ -32,24 +27,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UndoBlockUserFromCommunity {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: [PublicUrl; 1],
|
|
||||||
object: BlockUserFromCommunity,
|
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: UndoType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UndoBlockUserFromCommunity {
|
impl UndoBlockUserFromCommunity {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -66,12 +43,11 @@ impl UndoBlockUserFromCommunity {
|
||||||
)?;
|
)?;
|
||||||
let undo = UndoBlockUserFromCommunity {
|
let undo = UndoBlockUserFromCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: block,
|
object: block,
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,9 +65,11 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -101,7 +79,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = self.cc[0].dereference(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
let blocked_user = self
|
let blocked_user = self
|
||||||
.object
|
.object
|
||||||
.object
|
.object
|
||||||
|
@ -121,3 +99,14 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for UndoBlockUserFromCommunity {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
self.object.get_community(context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,22 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
community::{announce::GetCommunity, send_to_community},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
|
verify_is_public,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
context::lemmy_context,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
community::{ApubCommunity, Group},
|
protocol::{activities::community::update::UpdateCommunity, objects::group::Group},
|
||||||
person::ApubPerson,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::UpdateType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::{activity::kind::UpdateType, public};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
|
traits::{ActivityHandler, ActorType, ApubObject},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::community::{Community, CommunityForm},
|
source::community::{Community, CommunityForm},
|
||||||
|
@ -31,27 +24,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
/// This activity is received from a remote community mod, and updates the description or other
|
|
||||||
/// fields of a local community.
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UpdateCommunity {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: [PublicUrl; 1],
|
|
||||||
// TODO: would be nice to use a separate struct here, which only contains the fields updated here
|
|
||||||
object: Group,
|
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: UpdateType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UpdateCommunity {
|
impl UpdateCommunity {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -65,12 +37,11 @@ impl UpdateCommunity {
|
||||||
)?;
|
)?;
|
||||||
let update = UpdateCommunity {
|
let update = UpdateCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: community.to_apub(context).await?,
|
object: community.to_apub(context).await?,
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind: UpdateType::Update,
|
kind: UpdateType::Update,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,9 +58,11 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +71,7 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let cc = self.cc[0].clone();
|
let community = self.get_community(context, request_counter).await?;
|
||||||
let community = cc.dereference(context, request_counter).await?;
|
|
||||||
|
|
||||||
let updated_community = Group::from_apub_to_form(
|
let updated_community = Group::from_apub_to_form(
|
||||||
&self.object,
|
&self.object,
|
||||||
|
@ -133,3 +105,15 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for UpdateCommunity {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
let cid = ObjectId::new(self.object.id.clone());
|
||||||
|
cid.dereference(context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,31 +1,11 @@
|
||||||
use crate::{
|
use activitystreams::{activity::kind::DeleteType, public};
|
||||||
activities::{
|
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
|
||||||
deletion::{
|
|
||||||
receive_delete_action,
|
|
||||||
verify_delete_activity,
|
|
||||||
DeletableObjects,
|
|
||||||
WebsocketMessages,
|
|
||||||
},
|
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::DeleteType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -49,37 +29,25 @@ use lemmy_websocket::{
|
||||||
LemmyContext,
|
LemmyContext,
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
/// This is very confusing, because there are four distinct cases to handle:
|
use crate::{
|
||||||
/// - user deletes their post
|
activities::{
|
||||||
/// - user deletes their comment
|
community::{announce::GetCommunity, send_to_community},
|
||||||
/// - remote community mod deletes local community
|
deletion::{
|
||||||
/// - remote community deletes itself (triggered by a mod)
|
receive_delete_action,
|
||||||
///
|
verify_delete_activity,
|
||||||
/// TODO: we should probably change how community deletions work to simplify this. Probably by
|
DeletableObjects,
|
||||||
/// wrapping it in an announce just like other activities, instead of having the community send it.
|
WebsocketMessages,
|
||||||
#[skip_serializing_none]
|
},
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
generate_activity_id,
|
||||||
#[serde(rename_all = "camelCase")]
|
verify_activity,
|
||||||
pub struct Delete {
|
verify_is_public,
|
||||||
actor: ObjectId<ApubPerson>,
|
},
|
||||||
to: [PublicUrl; 1],
|
activity_lists::AnnouncableActivities,
|
||||||
pub(in crate::activities::deletion) object: Url,
|
fetcher::object_id::ObjectId,
|
||||||
pub(in crate::activities::deletion) cc: [ObjectId<ApubCommunity>; 1],
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
#[serde(rename = "type")]
|
protocol::activities::deletion::delete::Delete,
|
||||||
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<String>,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ActivityHandler for Delete {
|
impl ActivityHandler for Delete {
|
||||||
|
@ -89,11 +57,13 @@ impl ActivityHandler for Delete {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object,
|
&self.object,
|
||||||
self,
|
self,
|
||||||
&self.cc[0],
|
&community,
|
||||||
self.summary.is_some(),
|
self.summary.is_some(),
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -144,16 +114,15 @@ impl Delete {
|
||||||
) -> Result<Delete, LemmyError> {
|
) -> Result<Delete, LemmyError> {
|
||||||
Ok(Delete {
|
Ok(Delete {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: object_id,
|
object: object_id,
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind: DeleteType::Delete,
|
kind: DeleteType::Delete,
|
||||||
summary,
|
summary,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
DeleteType::Delete,
|
DeleteType::Delete,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?,
|
)?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -243,3 +212,26 @@ pub(in crate::activities) async fn receive_remove_action(
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for Delete {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
_request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
let community_id = match DeletableObjects::read_from_db(&self.object, context).await? {
|
||||||
|
DeletableObjects::Community(c) => c.id,
|
||||||
|
DeletableObjects::Comment(c) => {
|
||||||
|
let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
|
||||||
|
post.community_id
|
||||||
|
}
|
||||||
|
DeletableObjects::Post(p) => p.community_id,
|
||||||
|
};
|
||||||
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
Community::read(conn, community_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
Ok(community.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
use crate::{
|
use url::Url;
|
||||||
activities::{
|
|
||||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
|
||||||
verify_mod_action,
|
|
||||||
verify_person_in_community,
|
|
||||||
},
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
traits::{ActivityFields, ActorType, ApubObject},
|
traits::{ActivityFields, ActorType, ApubObject},
|
||||||
|
@ -19,7 +12,13 @@ use lemmy_websocket::{
|
||||||
LemmyContext,
|
LemmyContext,
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
use url::Url;
|
|
||||||
|
use crate::{
|
||||||
|
activities::{verify_mod_action, verify_person_in_community},
|
||||||
|
fetcher::object_id::ObjectId,
|
||||||
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
|
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod undo_delete;
|
pub mod undo_delete;
|
||||||
|
@ -82,7 +81,7 @@ impl DeletableObjects {
|
||||||
pub(in crate::activities) async fn verify_delete_activity(
|
pub(in crate::activities) async fn verify_delete_activity(
|
||||||
object: &Url,
|
object: &Url,
|
||||||
activity: &dyn ActivityFields,
|
activity: &dyn ActivityFields,
|
||||||
community_id: &ObjectId<ApubCommunity>,
|
community: &ApubCommunity,
|
||||||
is_mod_action: bool,
|
is_mod_action: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
|
@ -90,26 +89,20 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
let object = DeletableObjects::read_from_db(object, context).await?;
|
let object = DeletableObjects::read_from_db(object, context).await?;
|
||||||
let actor = ObjectId::new(activity.actor().clone());
|
let actor = ObjectId::new(activity.actor().clone());
|
||||||
match object {
|
match object {
|
||||||
DeletableObjects::Community(c) => {
|
DeletableObjects::Community(community) => {
|
||||||
if c.local {
|
if community.local {
|
||||||
// can only do this check for local community, in remote case it would try to fetch the
|
// can only do this check for local community, in remote case it would try to fetch the
|
||||||
// deleted community (which fails)
|
// deleted community (which fails)
|
||||||
verify_person_in_community(&actor, community_id, context, request_counter).await?;
|
verify_person_in_community(&actor, &community, context, request_counter).await?;
|
||||||
}
|
}
|
||||||
// community deletion is always a mod (or admin) action
|
// community deletion is always a mod (or admin) action
|
||||||
verify_mod_action(
|
verify_mod_action(&actor, &community, context, request_counter).await?;
|
||||||
&actor,
|
|
||||||
&ObjectId::new(c.actor_id()),
|
|
||||||
context,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
DeletableObjects::Post(p) => {
|
DeletableObjects::Post(p) => {
|
||||||
verify_delete_activity_post_or_comment(
|
verify_delete_activity_post_or_comment(
|
||||||
activity,
|
activity,
|
||||||
&p.ap_id.clone().into(),
|
&p.ap_id.clone().into(),
|
||||||
community_id,
|
community,
|
||||||
is_mod_action,
|
is_mod_action,
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -120,7 +113,7 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
verify_delete_activity_post_or_comment(
|
verify_delete_activity_post_or_comment(
|
||||||
activity,
|
activity,
|
||||||
&c.ap_id.clone().into(),
|
&c.ap_id.clone().into(),
|
||||||
community_id,
|
community,
|
||||||
is_mod_action,
|
is_mod_action,
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -134,15 +127,15 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
async fn verify_delete_activity_post_or_comment(
|
async fn verify_delete_activity_post_or_comment(
|
||||||
activity: &dyn ActivityFields,
|
activity: &dyn ActivityFields,
|
||||||
object_id: &Url,
|
object_id: &Url,
|
||||||
community_id: &ObjectId<ApubCommunity>,
|
community: &ApubCommunity,
|
||||||
is_mod_action: bool,
|
is_mod_action: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor = ObjectId::new(activity.actor().clone());
|
let actor = ObjectId::new(activity.actor().clone());
|
||||||
verify_person_in_community(&actor, community_id, context, request_counter).await?;
|
verify_person_in_community(&actor, community, context, request_counter).await?;
|
||||||
if is_mod_action {
|
if is_mod_action {
|
||||||
verify_mod_action(&actor, community_id, context, request_counter).await?;
|
verify_mod_action(&actor, community, context, request_counter).await?;
|
||||||
} else {
|
} else {
|
||||||
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
|
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
|
||||||
verify_domains_match(activity.actor(), object_id)?;
|
verify_domains_match(activity.actor(), object_id)?;
|
||||||
|
|
|
@ -1,32 +1,11 @@
|
||||||
use crate::{
|
use activitystreams::{activity::kind::UndoType, public};
|
||||||
activities::{
|
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
|
||||||
deletion::{
|
|
||||||
delete::Delete,
|
|
||||||
receive_delete_action,
|
|
||||||
verify_delete_activity,
|
|
||||||
DeletableObjects,
|
|
||||||
WebsocketMessages,
|
|
||||||
},
|
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::UndoType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -35,24 +14,25 @@ use lemmy_websocket::{
|
||||||
LemmyContext,
|
LemmyContext,
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
use crate::{
|
||||||
#[serde(rename_all = "camelCase")]
|
activities::{
|
||||||
pub struct UndoDelete {
|
community::{announce::GetCommunity, send_to_community},
|
||||||
actor: ObjectId<ApubPerson>,
|
deletion::{
|
||||||
to: [PublicUrl; 1],
|
receive_delete_action,
|
||||||
object: Delete,
|
verify_delete_activity,
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
DeletableObjects,
|
||||||
#[serde(rename = "type")]
|
WebsocketMessages,
|
||||||
kind: UndoType,
|
},
|
||||||
id: Url,
|
generate_activity_id,
|
||||||
#[serde(rename = "@context")]
|
verify_activity,
|
||||||
context: OneOrMany<AnyBase>,
|
verify_is_public,
|
||||||
#[serde(flatten)]
|
},
|
||||||
unparsed: Unparsed,
|
activity_lists::AnnouncableActivities,
|
||||||
}
|
fetcher::object_id::ObjectId,
|
||||||
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ActivityHandler for UndoDelete {
|
impl ActivityHandler for UndoDelete {
|
||||||
|
@ -62,12 +42,14 @@ impl ActivityHandler for UndoDelete {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object.object,
|
&self.object.object,
|
||||||
self,
|
self,
|
||||||
&self.cc[0],
|
&community,
|
||||||
self.object.summary.is_some(),
|
self.object.summary.is_some(),
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -117,12 +99,11 @@ impl UndoDelete {
|
||||||
)?;
|
)?;
|
||||||
let undo = UndoDelete {
|
let undo = UndoDelete {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object,
|
object,
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,3 +145,14 @@ impl UndoDelete {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for UndoDelete {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
self.object.get_community(context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity},
|
||||||
following::follow::FollowCommunity,
|
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
verify_community,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
||||||
send_lemmy_activity,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::AcceptType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::activity::kind::AcceptType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
@ -25,23 +13,6 @@ use lemmy_apub_lib::{
|
||||||
use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
|
use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct AcceptFollowCommunity {
|
|
||||||
actor: ObjectId<ApubCommunity>,
|
|
||||||
to: ObjectId<ApubPerson>,
|
|
||||||
object: FollowCommunity,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: AcceptType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AcceptFollowCommunity {
|
impl AcceptFollowCommunity {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -57,14 +28,13 @@ impl AcceptFollowCommunity {
|
||||||
.await?;
|
.await?;
|
||||||
let accept = AcceptFollowCommunity {
|
let accept = AcceptFollowCommunity {
|
||||||
actor: ObjectId::new(community.actor_id()),
|
actor: ObjectId::new(community.actor_id()),
|
||||||
to: ObjectId::new(person.actor_id()),
|
to: [ObjectId::new(person.actor_id())],
|
||||||
object: follow,
|
object: follow,
|
||||||
kind: AcceptType::Accept,
|
kind: AcceptType::Accept,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
AcceptType::Accept,
|
AcceptType::Accept,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?,
|
)?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
let inbox = vec![person.inbox_url()];
|
let inbox = vec![person.inbox_url()];
|
||||||
|
@ -82,9 +52,8 @@ impl ActivityHandler for AcceptFollowCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_urls_match(self.to.inner(), self.object.actor())?;
|
verify_urls_match(self.to[0].inner(), self.object.actor())?;
|
||||||
verify_urls_match(self.actor(), self.object.to.inner())?;
|
verify_urls_match(self.actor(), self.object.to[0].inner())?;
|
||||||
verify_community(&self.actor, context, request_counter).await?;
|
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -95,7 +64,7 @@ impl ActivityHandler for AcceptFollowCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor = self.actor.dereference(context, request_counter).await?;
|
let actor = self.actor.dereference(context, request_counter).await?;
|
||||||
let to = self.to.dereference(context, request_counter).await?;
|
let to = self.to[0].dereference(context, request_counter).await?;
|
||||||
// This will throw an error if no follow was requested
|
// This will throw an error if no follow was requested
|
||||||
blocking(context.pool(), move |conn| {
|
blocking(context.pool(), move |conn| {
|
||||||
CommunityFollower::follow_accepted(conn, actor.id, to.id)
|
CommunityFollower::follow_accepted(conn, actor.id, to.id)
|
||||||
|
|
|
@ -1,25 +1,20 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
following::accept::AcceptFollowCommunity,
|
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
|
send_lemmy_activity,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_person,
|
verify_person,
|
||||||
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
send_lemmy_activity,
|
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::FollowType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::activity::kind::FollowType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
verify::verify_urls_match,
|
verify::verify_urls_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -28,24 +23,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct FollowCommunity {
|
|
||||||
pub(in crate::activities::following) actor: ObjectId<ApubPerson>,
|
|
||||||
// TODO: is there any reason to put the same community id twice, in to and object?
|
|
||||||
pub(in crate::activities::following) to: ObjectId<ApubCommunity>,
|
|
||||||
pub(in crate::activities::following) object: ObjectId<ApubCommunity>,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: FollowType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FollowCommunity {
|
impl FollowCommunity {
|
||||||
pub(in crate::activities::following) fn new(
|
pub(in crate::activities::following) fn new(
|
||||||
|
@ -55,14 +32,13 @@ impl FollowCommunity {
|
||||||
) -> Result<FollowCommunity, LemmyError> {
|
) -> Result<FollowCommunity, LemmyError> {
|
||||||
Ok(FollowCommunity {
|
Ok(FollowCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(community.actor_id()),
|
to: [ObjectId::new(community.actor_id())],
|
||||||
object: ObjectId::new(community.actor_id()),
|
object: ObjectId::new(community.actor_id()),
|
||||||
kind: FollowType::Follow,
|
kind: FollowType::Follow,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
FollowType::Follow,
|
FollowType::Follow,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?,
|
)?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -96,8 +72,10 @@ impl ActivityHandler for FollowCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_urls_match(self.to.inner(), self.object.inner())?;
|
verify_urls_match(self.to[0].inner(), self.object.inner())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
|
let community = self.to[0].dereference(context, request_counter).await?;
|
||||||
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +85,7 @@ impl ActivityHandler for FollowCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor = self.actor.dereference(context, request_counter).await?;
|
let actor = self.actor.dereference(context, request_counter).await?;
|
||||||
let community = self.object.dereference(context, request_counter).await?;
|
let community = self.to[0].dereference(context, request_counter).await?;
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: community.id,
|
community_id: community.id,
|
||||||
person_id: actor.id,
|
person_id: actor.id,
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
pub mod accept;
|
pub mod accept;
|
||||||
pub mod follow;
|
pub mod follow;
|
||||||
pub mod undo;
|
pub mod undo_follow;
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
following::follow::FollowCommunity,
|
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
verify_person,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
send_lemmy_activity,
|
protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::UndoType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
@ -28,23 +17,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UndoFollowCommunity {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: ObjectId<ApubCommunity>,
|
|
||||||
object: FollowCommunity,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: UndoType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UndoFollowCommunity {
|
impl UndoFollowCommunity {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -55,14 +27,13 @@ impl UndoFollowCommunity {
|
||||||
let object = FollowCommunity::new(actor, community, context)?;
|
let object = FollowCommunity::new(actor, community, context)?;
|
||||||
let undo = UndoFollowCommunity {
|
let undo = UndoFollowCommunity {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(community.actor_id()),
|
to: [ObjectId::new(community.actor_id())],
|
||||||
object,
|
object,
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
UndoType::Undo,
|
UndoType::Undo,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?,
|
)?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
let inbox = vec![community.shared_inbox_or_inbox_url()];
|
let inbox = vec![community.shared_inbox_or_inbox_url()];
|
||||||
|
@ -79,7 +50,7 @@ impl ActivityHandler for UndoFollowCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_urls_match(self.to.inner(), self.object.object.inner())?;
|
verify_urls_match(self.to[0].inner(), self.object.object.inner())?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor(), self.object.actor())?;
|
||||||
verify_person(&self.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
@ -92,7 +63,7 @@ impl ActivityHandler for UndoFollowCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor = self.actor.dereference(context, request_counter).await?;
|
let actor = self.actor.dereference(context, request_counter).await?;
|
||||||
let community = self.to.dereference(context, request_counter).await?;
|
let community = self.to[0].dereference(context, request_counter).await?;
|
||||||
|
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: community.id,
|
community_id: community.id,
|
|
@ -1,21 +1,28 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
check_community_or_site_ban,
|
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
|
context::WithContext,
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
|
insert_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
};
|
};
|
||||||
use activitystreams::public;
|
use activitystreams::public;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match};
|
use lemmy_apub_lib::{
|
||||||
|
activity_queue::send_activity,
|
||||||
|
traits::{ActivityFields, ActorType},
|
||||||
|
verify::verify_domains_match,
|
||||||
|
};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_views_actor::community_view::CommunityView;
|
use lemmy_db_views_actor::{
|
||||||
|
community_person_ban_view::CommunityPersonBanView,
|
||||||
|
community_view::CommunityView,
|
||||||
|
};
|
||||||
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use log::info;
|
||||||
use std::ops::Deref;
|
use serde::Serialize;
|
||||||
use strum_macros::ToString;
|
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -25,15 +32,8 @@ pub mod deletion;
|
||||||
pub mod following;
|
pub mod following;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
pub mod report;
|
|
||||||
pub mod voting;
|
pub mod voting;
|
||||||
|
|
||||||
#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
|
|
||||||
pub enum CreateOrUpdateType {
|
|
||||||
Create,
|
|
||||||
Update,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
|
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
|
||||||
/// doesn't have a site ban.
|
/// doesn't have a site ban.
|
||||||
async fn verify_person(
|
async fn verify_person(
|
||||||
|
@ -48,44 +48,26 @@ async fn verify_person(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn extract_community(
|
|
||||||
cc: &[Url],
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<ApubCommunity, LemmyError> {
|
|
||||||
let mut cc_iter = cc.iter();
|
|
||||||
loop {
|
|
||||||
if let Some(cid) = cc_iter.next() {
|
|
||||||
let cid = ObjectId::new(cid.clone());
|
|
||||||
if let Ok(c) = cid.dereference(context, request_counter).await {
|
|
||||||
break Ok(c);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(anyhow!("No community found in cc").into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches the person and community to verify their type, then checks if person is banned from site
|
/// Fetches the person and community to verify their type, then checks if person is banned from site
|
||||||
/// or community.
|
/// or community.
|
||||||
pub(crate) async fn verify_person_in_community(
|
pub(crate) async fn verify_person_in_community(
|
||||||
person_id: &ObjectId<ApubPerson>,
|
person_id: &ObjectId<ApubPerson>,
|
||||||
community_id: &ObjectId<ApubCommunity>,
|
community: &ApubCommunity,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = community_id.dereference(context, request_counter).await?;
|
|
||||||
let person = person_id.dereference(context, request_counter).await?;
|
let person = person_id.dereference(context, request_counter).await?;
|
||||||
check_community_or_site_ban(person.deref(), community.id, context.pool()).await
|
if person.banned {
|
||||||
}
|
return Err(anyhow!("Person is banned from site").into());
|
||||||
|
}
|
||||||
|
let person_id = person.id;
|
||||||
|
let community_id = community.id;
|
||||||
|
let is_banned =
|
||||||
|
move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
|
||||||
|
if blocking(context.pool(), is_banned).await? {
|
||||||
|
return Err(anyhow!("Person is banned from community").into());
|
||||||
|
}
|
||||||
|
|
||||||
/// Simply check that the url actually refers to a valid group.
|
|
||||||
async fn verify_community(
|
|
||||||
community_id: &ObjectId<ApubCommunity>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
community_id.dereference(context, request_counter).await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,12 +82,10 @@ fn verify_activity(activity: &dyn ActivityFields, settings: &Settings) -> Result
|
||||||
/// is not federated, we cant verify their actions remotely.
|
/// is not federated, we cant verify their actions remotely.
|
||||||
pub(crate) async fn verify_mod_action(
|
pub(crate) async fn verify_mod_action(
|
||||||
actor_id: &ObjectId<ApubPerson>,
|
actor_id: &ObjectId<ApubPerson>,
|
||||||
community_id: &ObjectId<ApubCommunity>,
|
community: &ApubCommunity,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = community_id.dereference_local(context).await?;
|
|
||||||
|
|
||||||
if community.local {
|
if community.local {
|
||||||
let actor = actor_id.dereference(context, request_counter).await?;
|
let actor = actor_id.dereference(context, request_counter).await?;
|
||||||
|
|
||||||
|
@ -128,9 +108,9 @@ pub(crate) async fn verify_mod_action(
|
||||||
/// /c/community/moderators. Any different values are unsupported.
|
/// /c/community/moderators. Any different values are unsupported.
|
||||||
fn verify_add_remove_moderator_target(
|
fn verify_add_remove_moderator_target(
|
||||||
target: &Url,
|
target: &Url,
|
||||||
community: &ObjectId<ApubCommunity>,
|
community: &ApubCommunity,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
if target != &generate_moderators_url(&community.clone().into())?.into_inner() {
|
if target != &generate_moderators_url(&community.actor_id)?.into_inner() {
|
||||||
return Err(anyhow!("Unkown target url").into());
|
return Err(anyhow!("Unkown target url").into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -165,3 +145,47 @@ where
|
||||||
);
|
);
|
||||||
Url::parse(&id)
|
Url::parse(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_lemmy_activity<T: Serialize>(
|
||||||
|
context: &LemmyContext,
|
||||||
|
activity: &T,
|
||||||
|
activity_id: &Url,
|
||||||
|
actor: &dyn ActorType,
|
||||||
|
inboxes: Vec<Url>,
|
||||||
|
sensitive: bool,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
if !context.settings().federation.enabled || inboxes.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let activity = WithContext::new(activity);
|
||||||
|
|
||||||
|
info!("Sending activity {}", activity_id.to_string());
|
||||||
|
|
||||||
|
// Don't send anything to ourselves
|
||||||
|
// TODO: this should be a debug assert
|
||||||
|
let hostname = context.settings().get_hostname_without_port()?;
|
||||||
|
let inboxes: Vec<&Url> = inboxes
|
||||||
|
.iter()
|
||||||
|
.filter(|i| i.domain().expect("valid inbox url") != hostname)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let serialised_activity = serde_json::to_string(&activity)?;
|
||||||
|
|
||||||
|
insert_activity(
|
||||||
|
activity_id,
|
||||||
|
serialised_activity.clone(),
|
||||||
|
true,
|
||||||
|
sensitive,
|
||||||
|
context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
send_activity(
|
||||||
|
serialised_activity,
|
||||||
|
actor,
|
||||||
|
inboxes,
|
||||||
|
context.client(),
|
||||||
|
context.activity_queue(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
|
@ -1,51 +1,31 @@
|
||||||
use crate::{
|
use activitystreams::public;
|
||||||
activities::{
|
|
||||||
check_community_deleted_or_removed,
|
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
verify_mod_action,
|
|
||||||
verify_person_in_community,
|
|
||||||
CreateOrUpdateType,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{
|
|
||||||
community::ApubCommunity,
|
|
||||||
person::ApubPerson,
|
|
||||||
post::{ApubPost, Page},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
|
traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
|
||||||
values::PublicUrl,
|
|
||||||
verify::{verify_domains_match, verify_urls_match},
|
verify::{verify_domains_match, verify_urls_match},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
use crate::{
|
||||||
#[serde(rename_all = "camelCase")]
|
activities::{
|
||||||
pub struct CreateOrUpdatePost {
|
check_community_deleted_or_removed,
|
||||||
actor: ObjectId<ApubPerson>,
|
community::{announce::GetCommunity, send_to_community},
|
||||||
to: [PublicUrl; 1],
|
generate_activity_id,
|
||||||
object: Page,
|
verify_activity,
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
verify_is_public,
|
||||||
#[serde(rename = "type")]
|
verify_mod_action,
|
||||||
kind: CreateOrUpdateType,
|
verify_person_in_community,
|
||||||
id: Url,
|
},
|
||||||
#[serde(rename = "@context")]
|
activity_lists::AnnouncableActivities,
|
||||||
context: OneOrMany<AnyBase>,
|
fetcher::object_id::ObjectId,
|
||||||
#[serde(flatten)]
|
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
unparsed: Unparsed,
|
protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
|
||||||
}
|
};
|
||||||
|
|
||||||
impl CreateOrUpdatePost {
|
impl CreateOrUpdatePost {
|
||||||
pub(crate) async fn new(
|
pub(crate) async fn new(
|
||||||
|
@ -61,12 +41,11 @@ impl CreateOrUpdatePost {
|
||||||
)?;
|
)?;
|
||||||
Ok(CreateOrUpdatePost {
|
Ok(CreateOrUpdatePost {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: post.to_apub(context).await?,
|
object: post.to_apub(context).await?,
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind,
|
kind,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -97,9 +76,10 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
let community = self.cc[0].dereference(context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
check_community_deleted_or_removed(&community)?;
|
check_community_deleted_or_removed(&community)?;
|
||||||
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
|
@ -119,7 +99,7 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
CreateOrUpdateType::Update => {
|
CreateOrUpdateType::Update => {
|
||||||
let is_mod_action = self.object.is_mod_action(context).await?;
|
let is_mod_action = self.object.is_mod_action(context).await?;
|
||||||
if is_mod_action {
|
if is_mod_action {
|
||||||
verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
|
verify_mod_action(&self.actor, &community, context, request_counter).await?;
|
||||||
} else {
|
} else {
|
||||||
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
|
||||||
verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
|
verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
|
||||||
|
@ -147,3 +127,17 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for CreateOrUpdatePost {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
self
|
||||||
|
.object
|
||||||
|
.extract_community(context, request_counter)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,40 +1,21 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, verify_activity, verify_person, CreateOrUpdateType},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
person::ApubPerson,
|
protocol::activities::{
|
||||||
private_message::{ApubPrivateMessage, Note},
|
private_message::create_or_update::CreateOrUpdatePrivateMessage,
|
||||||
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
send_lemmy_activity,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType, ApubObject},
|
traits::{ActivityHandler, ActorType, ApubObject},
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{source::person::Person, traits::Crud};
|
use lemmy_db_schema::{source::person::Person, traits::Crud};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct CreateOrUpdatePrivateMessage {
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
pub context: OneOrMany<AnyBase>,
|
|
||||||
id: Url,
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: ObjectId<ApubPerson>,
|
|
||||||
object: Note,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: CreateOrUpdateType,
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateOrUpdatePrivateMessage {
|
impl CreateOrUpdatePrivateMessage {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -54,10 +35,9 @@ impl CreateOrUpdatePrivateMessage {
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?;
|
)?;
|
||||||
let create_or_update = CreateOrUpdatePrivateMessage {
|
let create_or_update = CreateOrUpdatePrivateMessage {
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(recipient.actor_id()),
|
to: [ObjectId::new(recipient.actor_id())],
|
||||||
object: private_message.to_apub(context).await?,
|
object: private_message.to_apub(context).await?,
|
||||||
kind,
|
kind,
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, verify_activity, verify_person},
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
send_lemmy_activity,
|
protocol::activities::private_message::delete::DeletePrivateMessage,
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::DeleteType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::activity::kind::DeleteType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
verify::verify_domains_match,
|
verify::verify_domains_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -23,23 +17,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct DeletePrivateMessage {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: ObjectId<ApubPerson>,
|
|
||||||
pub(in crate::activities::private_message) object: ObjectId<ApubPrivateMessage>,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: DeleteType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeletePrivateMessage {
|
impl DeletePrivateMessage {
|
||||||
pub(in crate::activities::private_message) fn new(
|
pub(in crate::activities::private_message) fn new(
|
||||||
|
@ -49,14 +26,13 @@ impl DeletePrivateMessage {
|
||||||
) -> Result<DeletePrivateMessage, LemmyError> {
|
) -> Result<DeletePrivateMessage, LemmyError> {
|
||||||
Ok(DeletePrivateMessage {
|
Ok(DeletePrivateMessage {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(actor.actor_id()),
|
to: [ObjectId::new(actor.actor_id())],
|
||||||
object: ObjectId::new(pm.ap_id.clone()),
|
object: ObjectId::new(pm.ap_id.clone()),
|
||||||
kind: DeleteType::Delete,
|
kind: DeleteType::Delete,
|
||||||
id: generate_activity_id(
|
id: generate_activity_id(
|
||||||
DeleteType::Delete,
|
DeleteType::Delete,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?,
|
)?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
|
||||||
generate_activity_id,
|
|
||||||
private_message::delete::DeletePrivateMessage,
|
|
||||||
verify_activity,
|
|
||||||
verify_person,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||||
send_lemmy_activity,
|
protocol::activities::private_message::{
|
||||||
};
|
delete::DeletePrivateMessage,
|
||||||
use activitystreams::{
|
undo_delete::UndoDeletePrivateMessage,
|
||||||
activity::kind::UndoType,
|
},
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
|
use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
@ -28,23 +20,6 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UndoDeletePrivateMessage {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: ObjectId<ApubPerson>,
|
|
||||||
object: DeletePrivateMessage,
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: UndoType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UndoDeletePrivateMessage {
|
impl UndoDeletePrivateMessage {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -65,11 +40,10 @@ impl UndoDeletePrivateMessage {
|
||||||
)?;
|
)?;
|
||||||
let undo = UndoDeletePrivateMessage {
|
let undo = UndoDeletePrivateMessage {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: ObjectId::new(recipient.actor_id()),
|
to: [ObjectId::new(recipient.actor_id())],
|
||||||
object,
|
object,
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
let inbox = vec![recipient.shared_inbox_or_inbox_url()];
|
let inbox = vec![recipient.shared_inbox_or_inbox_url()];
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
use crate::{
|
|
||||||
activities::voting::vote::VoteType,
|
|
||||||
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -17,6 +13,11 @@ use lemmy_websocket::{
|
||||||
UserOperation,
|
UserOperation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
|
||||||
|
protocol::activities::voting::vote::VoteType,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod undo_vote;
|
pub mod undo_vote;
|
||||||
pub mod vote;
|
pub mod vote;
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,35 @@
|
||||||
use crate::{
|
use std::ops::Deref;
|
||||||
activities::{
|
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
use activitystreams::{activity::kind::UndoType, public};
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
verify_person_in_community,
|
|
||||||
voting::{
|
|
||||||
undo_vote_comment,
|
|
||||||
undo_vote_post,
|
|
||||||
vote::{Vote, VoteType},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
PostOrComment,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::kind::UndoType,
|
|
||||||
base::AnyBase,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityFields, ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
|
||||||
verify::verify_urls_match,
|
verify::verify_urls_match,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
|
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::ops::Deref;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
use crate::{
|
||||||
#[serde(rename_all = "camelCase")]
|
activities::{
|
||||||
pub struct UndoVote {
|
community::{announce::GetCommunity, send_to_community},
|
||||||
actor: ObjectId<ApubPerson>,
|
generate_activity_id,
|
||||||
to: [PublicUrl; 1],
|
verify_activity,
|
||||||
object: Vote,
|
verify_is_public,
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
verify_person_in_community,
|
||||||
#[serde(rename = "type")]
|
voting::{undo_vote_comment, undo_vote_post},
|
||||||
kind: UndoType,
|
},
|
||||||
id: Url,
|
activity_lists::AnnouncableActivities,
|
||||||
#[serde(rename = "@context")]
|
fetcher::object_id::ObjectId,
|
||||||
context: OneOrMany<AnyBase>,
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
#[serde(flatten)]
|
protocol::activities::voting::{
|
||||||
unparsed: Unparsed,
|
undo_vote::UndoVote,
|
||||||
}
|
vote::{Vote, VoteType},
|
||||||
|
},
|
||||||
|
PostOrComment,
|
||||||
|
};
|
||||||
|
|
||||||
impl UndoVote {
|
impl UndoVote {
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
|
@ -72,12 +52,11 @@ impl UndoVote {
|
||||||
)?;
|
)?;
|
||||||
let undo_vote = UndoVote {
|
let undo_vote = UndoVote {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object,
|
object,
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
||||||
|
@ -93,8 +72,10 @@ impl ActivityHandler for UndoVote {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
verify_urls_match(self.actor(), self.object.actor())?;
|
verify_urls_match(self.actor(), self.object.actor())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -117,3 +98,14 @@ impl ActivityHandler for UndoVote {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for UndoVote {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
self.object.get_community(context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,74 +1,35 @@
|
||||||
use crate::{
|
use std::ops::Deref;
|
||||||
activities::{
|
|
||||||
community::{announce::AnnouncableActivities, send_to_community},
|
use activitystreams::public;
|
||||||
generate_activity_id,
|
|
||||||
verify_activity,
|
|
||||||
verify_person_in_community,
|
|
||||||
voting::{vote_comment, vote_post},
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
|
||||||
PostOrComment,
|
|
||||||
};
|
|
||||||
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
traits::{ActivityFields, ActivityHandler, ActorType},
|
traits::{ActivityHandler, ActorType},
|
||||||
values::PublicUrl,
|
};
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
newtypes::CommunityId,
|
||||||
|
source::{community::Community, post::Post},
|
||||||
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
|
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::{convert::TryFrom, ops::Deref};
|
|
||||||
use strum_macros::ToString;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
|
use crate::{
|
||||||
pub enum VoteType {
|
activities::{
|
||||||
Like,
|
community::{announce::GetCommunity, send_to_community},
|
||||||
Dislike,
|
generate_activity_id,
|
||||||
}
|
verify_activity,
|
||||||
|
verify_is_public,
|
||||||
impl TryFrom<i16> for VoteType {
|
verify_person_in_community,
|
||||||
type Error = LemmyError;
|
voting::{vote_comment, vote_post},
|
||||||
|
},
|
||||||
fn try_from(value: i16) -> Result<Self, Self::Error> {
|
activity_lists::AnnouncableActivities,
|
||||||
match value {
|
fetcher::object_id::ObjectId,
|
||||||
1 => Ok(VoteType::Like),
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
-1 => Ok(VoteType::Dislike),
|
protocol::activities::voting::vote::{Vote, VoteType},
|
||||||
_ => Err(anyhow!("invalid vote value").into()),
|
PostOrComment,
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&VoteType> for i16 {
|
|
||||||
fn from(value: &VoteType) -> i16 {
|
|
||||||
match value {
|
|
||||||
VoteType::Like => 1,
|
|
||||||
VoteType::Dislike => -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Vote {
|
|
||||||
actor: ObjectId<ApubPerson>,
|
|
||||||
to: [PublicUrl; 1],
|
|
||||||
pub(in crate::activities::voting) object: ObjectId<PostOrComment>,
|
|
||||||
cc: [ObjectId<ApubCommunity>; 1],
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub(in crate::activities::voting) kind: VoteType,
|
|
||||||
id: Url,
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vote {
|
impl Vote {
|
||||||
pub(in crate::activities::voting) fn new(
|
pub(in crate::activities::voting) fn new(
|
||||||
|
@ -80,12 +41,11 @@ impl Vote {
|
||||||
) -> Result<Vote, LemmyError> {
|
) -> Result<Vote, LemmyError> {
|
||||||
Ok(Vote {
|
Ok(Vote {
|
||||||
actor: ObjectId::new(actor.actor_id()),
|
actor: ObjectId::new(actor.actor_id()),
|
||||||
to: [PublicUrl::Public],
|
to: vec![public()],
|
||||||
object: ObjectId::new(object.ap_id()),
|
object: ObjectId::new(object.ap_id()),
|
||||||
cc: [ObjectId::new(community.actor_id())],
|
cc: vec![community.actor_id()],
|
||||||
kind: kind.clone(),
|
kind: kind.clone(),
|
||||||
id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
|
id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
|
||||||
context: lemmy_context(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -118,8 +78,10 @@ impl ActivityHandler for Vote {
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_is_public(&self.to)?;
|
||||||
verify_activity(self, &context.settings())?;
|
verify_activity(self, &context.settings())?;
|
||||||
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
let community = self.get_community(context, request_counter).await?;
|
||||||
|
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,3 +98,24 @@ impl ActivityHandler for Vote {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for Vote {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
let object = self.object.dereference(context, request_counter).await?;
|
||||||
|
let cid = match object {
|
||||||
|
PostOrComment::Post(p) => p.community_id,
|
||||||
|
PostOrComment::Comment(c) => {
|
||||||
|
blocking(context.pool(), move |conn| Post::read(conn, c.post_id))
|
||||||
|
.await??
|
||||||
|
.community_id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let community = blocking(context.pool(), move |conn| Community::read(conn, cid)).await??;
|
||||||
|
Ok(community.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
107
crates/apub/src/activity_lists.rs
Normal file
107
crates/apub/src/activity_lists.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use crate::{
|
||||||
|
activities::community::announce::GetCommunity,
|
||||||
|
objects::community::ApubCommunity,
|
||||||
|
protocol::activities::{
|
||||||
|
community::{
|
||||||
|
add_mod::AddMod,
|
||||||
|
announce::AnnounceActivity,
|
||||||
|
block_user::BlockUserFromCommunity,
|
||||||
|
remove_mod::RemoveMod,
|
||||||
|
report::Report,
|
||||||
|
undo_block_user::UndoBlockUserFromCommunity,
|
||||||
|
update::UpdateCommunity,
|
||||||
|
},
|
||||||
|
create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost},
|
||||||
|
deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
|
following::{
|
||||||
|
accept::AcceptFollowCommunity,
|
||||||
|
follow::FollowCommunity,
|
||||||
|
undo_follow::UndoFollowCommunity,
|
||||||
|
},
|
||||||
|
private_message::{
|
||||||
|
create_or_update::CreateOrUpdatePrivateMessage,
|
||||||
|
delete::DeletePrivateMessage,
|
||||||
|
undo_delete::UndoDeletePrivateMessage,
|
||||||
|
},
|
||||||
|
voting::{undo_vote::UndoVote, vote::Vote},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[activity_handler(LemmyContext)]
|
||||||
|
pub enum SharedInboxActivities {
|
||||||
|
GroupInboxActivities(GroupInboxActivities),
|
||||||
|
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
|
||||||
|
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
|
||||||
|
PersonInboxActivities(PersonInboxActivities),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[activity_handler(LemmyContext)]
|
||||||
|
pub enum GroupInboxActivities {
|
||||||
|
FollowCommunity(FollowCommunity),
|
||||||
|
UndoFollowCommunity(UndoFollowCommunity),
|
||||||
|
AnnouncableActivities(AnnouncableActivities),
|
||||||
|
Report(Report),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[activity_handler(LemmyContext)]
|
||||||
|
pub enum PersonInboxActivities {
|
||||||
|
AcceptFollowCommunity(AcceptFollowCommunity),
|
||||||
|
/// Some activities can also be sent from user to user, eg a comment with mentions
|
||||||
|
AnnouncableActivities(AnnouncableActivities),
|
||||||
|
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
|
||||||
|
DeletePrivateMessage(DeletePrivateMessage),
|
||||||
|
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
|
||||||
|
AnnounceActivity(Box<AnnounceActivity>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[activity_handler(LemmyContext)]
|
||||||
|
pub enum AnnouncableActivities {
|
||||||
|
CreateOrUpdateComment(CreateOrUpdateComment),
|
||||||
|
CreateOrUpdatePost(Box<CreateOrUpdatePost>),
|
||||||
|
Vote(Vote),
|
||||||
|
UndoVote(UndoVote),
|
||||||
|
Delete(Delete),
|
||||||
|
UndoDelete(UndoDelete),
|
||||||
|
UpdateCommunity(Box<UpdateCommunity>),
|
||||||
|
BlockUserFromCommunity(BlockUserFromCommunity),
|
||||||
|
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
||||||
|
AddMod(AddMod),
|
||||||
|
RemoveMod(RemoveMod),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl GetCommunity for AnnouncableActivities {
|
||||||
|
async fn get_community(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<ApubCommunity, LemmyError> {
|
||||||
|
use AnnouncableActivities::*;
|
||||||
|
let community = match self {
|
||||||
|
CreateOrUpdateComment(a) => a.get_community(context, request_counter).await?,
|
||||||
|
CreateOrUpdatePost(a) => a.get_community(context, request_counter).await?,
|
||||||
|
Vote(a) => a.get_community(context, request_counter).await?,
|
||||||
|
UndoVote(a) => a.get_community(context, request_counter).await?,
|
||||||
|
Delete(a) => a.get_community(context, request_counter).await?,
|
||||||
|
UndoDelete(a) => a.get_community(context, request_counter).await?,
|
||||||
|
UpdateCommunity(a) => a.get_community(context, request_counter).await?,
|
||||||
|
BlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
|
||||||
|
UndoBlockUserFromCommunity(a) => a.get_community(context, request_counter).await?,
|
||||||
|
AddMod(a) => a.get_community(context, request_counter).await?,
|
||||||
|
RemoveMod(a) => a.get_community(context, request_counter).await?,
|
||||||
|
};
|
||||||
|
Ok(community)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
collections::CommunityContext,
|
collections::CommunityContext,
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
|
protocol::collections::group_moderators::GroupModerators,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{chrono::NaiveDateTime, collection::kind::OrderedCollectionType};
|
||||||
base::AnyBase,
|
|
||||||
chrono::NaiveDateTime,
|
|
||||||
collection::kind::OrderedCollectionType,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
url::Url,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{traits::ApubObject, verify::verify_domains_match};
|
use lemmy_apub_lib::{traits::ApubObject, verify::verify_domains_match};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -20,19 +14,7 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
|
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use serde::{Deserialize, Serialize};
|
use url::Url;
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct GroupModerators {
|
|
||||||
#[serde(rename = "@context")]
|
|
||||||
context: OneOrMany<AnyBase>,
|
|
||||||
r#type: OrderedCollectionType,
|
|
||||||
id: Url,
|
|
||||||
ordered_items: Vec<ObjectId<ApubPerson>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct ApubCommunityModerators(pub(crate) Vec<CommunityModeratorView>);
|
pub(crate) struct ApubCommunityModerators(pub(crate) Vec<CommunityModeratorView>);
|
||||||
|
@ -75,7 +57,6 @@ impl ApubObject for ApubCommunityModerators {
|
||||||
.map(|m| ObjectId::<ApubPerson>::new(m.moderator.actor_id.clone().into_inner()))
|
.map(|m| ObjectId::<ApubPerson>::new(m.moderator.actor_id.clone().into_inner()))
|
||||||
.collect();
|
.collect();
|
||||||
Ok(GroupModerators {
|
Ok(GroupModerators {
|
||||||
context: lemmy_context(),
|
|
||||||
r#type: OrderedCollectionType::OrderedCollection,
|
r#type: OrderedCollectionType::OrderedCollection,
|
||||||
id: generate_moderators_url(&data.0.actor_id)?.into(),
|
id: generate_moderators_url(&data.0.actor_id)?.into(),
|
||||||
ordered_items,
|
ordered_items,
|
||||||
|
@ -139,3 +120,74 @@ impl ApubObject for ApubCommunityModerators {
|
||||||
Ok(ApubCommunityModerators { 0: vec![] })
|
Ok(ApubCommunityModerators { 0: vec![] })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::objects::{
|
||||||
|
community::tests::parse_lemmy_community,
|
||||||
|
person::tests::parse_lemmy_person,
|
||||||
|
tests::{file_to_json_object, init_context},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
source::{
|
||||||
|
community::Community,
|
||||||
|
person::{Person, PersonForm},
|
||||||
|
},
|
||||||
|
traits::Crud,
|
||||||
|
};
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_parse_lemmy_community_moderators() {
|
||||||
|
let context = init_context();
|
||||||
|
let community = parse_lemmy_community(&context).await;
|
||||||
|
let community_id = community.id;
|
||||||
|
|
||||||
|
let old_mod = PersonForm {
|
||||||
|
name: "holly".into(),
|
||||||
|
..PersonForm::default()
|
||||||
|
};
|
||||||
|
let old_mod = Person::create(&context.pool().get().unwrap(), &old_mod).unwrap();
|
||||||
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
|
community_id: community.id,
|
||||||
|
person_id: old_mod.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommunityModerator::join(&context.pool().get().unwrap(), &community_moderator_form).unwrap();
|
||||||
|
|
||||||
|
let new_mod = parse_lemmy_person(&context).await;
|
||||||
|
|
||||||
|
let json: GroupModerators =
|
||||||
|
file_to_json_object("assets/lemmy/collections/group_moderators.json");
|
||||||
|
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
||||||
|
let mut request_counter = 0;
|
||||||
|
let community_context = CommunityContext {
|
||||||
|
0: community,
|
||||||
|
1: context,
|
||||||
|
};
|
||||||
|
ApubCommunityModerators::from_apub(&json, &community_context, &url, &mut request_counter)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(request_counter, 0);
|
||||||
|
|
||||||
|
let current_moderators = blocking(community_context.1.pool(), move |conn| {
|
||||||
|
CommunityModeratorView::for_community(conn, community_id)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(current_moderators.len(), 1);
|
||||||
|
assert_eq!(current_moderators[0].moderator.id, new_mod.id);
|
||||||
|
|
||||||
|
Person::delete(&*community_context.1.pool().get().unwrap(), old_mod.id).unwrap();
|
||||||
|
Person::delete(&*community_context.1.pool().get().unwrap(), new_mod.id).unwrap();
|
||||||
|
Community::delete(
|
||||||
|
&*community_context.1.pool().get().unwrap(),
|
||||||
|
community_context.0.id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,7 @@
|
||||||
use crate::{
|
use activitystreams::collection::kind::OrderedCollectionType;
|
||||||
activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType},
|
use chrono::NaiveDateTime;
|
||||||
collections::CommunityContext,
|
use url::Url;
|
||||||
context::lemmy_context,
|
|
||||||
generate_outbox_url,
|
|
||||||
objects::{person::ApubPerson, post::ApubPost},
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
base::AnyBase,
|
|
||||||
chrono::NaiveDateTime,
|
|
||||||
collection::kind::OrderedCollectionType,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
url::Url,
|
|
||||||
};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
data::Data,
|
data::Data,
|
||||||
|
@ -23,19 +13,16 @@ use lemmy_db_schema::{
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
use crate::{
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
collections::CommunityContext,
|
||||||
#[serde(rename_all = "camelCase")]
|
generate_outbox_url,
|
||||||
pub struct GroupOutbox {
|
objects::{person::ApubPerson, post::ApubPost},
|
||||||
#[serde(rename = "@context")]
|
protocol::{
|
||||||
context: OneOrMany<AnyBase>,
|
activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
|
||||||
r#type: OrderedCollectionType,
|
collections::group_outbox::GroupOutbox,
|
||||||
id: Url,
|
},
|
||||||
ordered_items: Vec<CreateOrUpdatePost>,
|
};
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
|
pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
|
||||||
|
@ -88,9 +75,9 @@ impl ApubObject for ApubCommunityOutbox {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(GroupOutbox {
|
Ok(GroupOutbox {
|
||||||
context: lemmy_context(),
|
|
||||||
r#type: OrderedCollectionType::OrderedCollection,
|
r#type: OrderedCollectionType::OrderedCollection,
|
||||||
id: generate_outbox_url(&data.0.actor_id)?.into(),
|
id: generate_outbox_url(&data.0.actor_id)?.into(),
|
||||||
|
total_items: ordered_items.len() as i32,
|
||||||
ordered_items,
|
ordered_items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::objects::community::ApubCommunity;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
|
||||||
|
use crate::objects::community::ApubCommunity;
|
||||||
|
|
||||||
pub(crate) mod community_moderators;
|
pub(crate) mod community_moderators;
|
||||||
pub(crate) mod community_outbox;
|
pub(crate) mod community_outbox;
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,51 @@
|
||||||
use activitystreams::{base::AnyBase, context, primitives::OneOrMany};
|
use activitystreams::{base::AnyBase, context, primitives::OneOrMany};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) fn lemmy_context() -> OneOrMany<AnyBase> {
|
lazy_static! {
|
||||||
let context_ext = AnyBase::from_arbitrary_json(json!(
|
static ref CONTEXT: OneOrMany<AnyBase> = {
|
||||||
{
|
let context_ext = AnyBase::from_arbitrary_json(json!(
|
||||||
"sc": "http://schema.org#",
|
{
|
||||||
"sensitive": "as:sensitive",
|
"sc": "http://schema.org#",
|
||||||
"stickied": "as:stickied",
|
"sensitive": "as:sensitive",
|
||||||
"pt": "https://join-lemmy.org#",
|
"stickied": "as:stickied",
|
||||||
"comments_enabled": {
|
"pt": "https://join-lemmy.org#",
|
||||||
"type": "sc:Boolean",
|
"comments_enabled": {
|
||||||
"id": "pt:commentsEnabled"
|
"type": "sc:Boolean",
|
||||||
},
|
"id": "pt:commentsEnabled"
|
||||||
"moderators": "as:moderators",
|
},
|
||||||
"matrixUserId": {
|
"moderators": "as:moderators",
|
||||||
"type": "sc:Text",
|
"matrixUserId": {
|
||||||
"id": "as:alsoKnownAs"
|
"type": "sc:Text",
|
||||||
},
|
"id": "as:alsoKnownAs"
|
||||||
}))
|
},
|
||||||
.expect("parse context");
|
}))
|
||||||
OneOrMany::from(vec![
|
.expect("parse context");
|
||||||
AnyBase::from(context()),
|
OneOrMany::from(vec![
|
||||||
context_ext,
|
AnyBase::from(context()),
|
||||||
AnyBase::from(Url::parse("https://w3id.org/security/v1").expect("parse context")),
|
context_ext,
|
||||||
])
|
AnyBase::from(Url::parse("https://w3id.org/security/v1").expect("parse context")),
|
||||||
|
])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub(crate) struct WithContext<T> {
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
inner: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> WithContext<T> {
|
||||||
|
pub(crate) fn new(inner: T) -> WithContext<T> {
|
||||||
|
WithContext {
|
||||||
|
context: CONTEXT.clone(),
|
||||||
|
inner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn inner(self) -> T {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ use url::Url;
|
||||||
/// fetch through the search). This should be configurable.
|
/// fetch through the search). This should be configurable.
|
||||||
static REQUEST_LIMIT: i32 = 25;
|
static REQUEST_LIMIT: i32 = 25;
|
||||||
|
|
||||||
// TODO: after moving this file to library, remove lazy_static dependency from apub crate
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CLIENT: Client = Client::builder()
|
static ref CLIENT: Client = Client::builder()
|
||||||
.user_agent(build_user_agent(&Settings::get()))
|
.user_agent(build_user_agent(&Settings::get()))
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
use crate::objects::{
|
use chrono::NaiveDateTime;
|
||||||
comment::{ApubComment, Note},
|
use serde::Deserialize;
|
||||||
post::{ApubPost, Page},
|
use url::Url;
|
||||||
};
|
|
||||||
use activitystreams::chrono::NaiveDateTime;
|
|
||||||
use lemmy_apub_lib::traits::ApubObject;
|
use lemmy_apub_lib::traits::ApubObject;
|
||||||
use lemmy_db_schema::source::{comment::CommentForm, post::PostForm};
|
use lemmy_db_schema::source::{comment::CommentForm, post::PostForm};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
|
||||||
use url::Url;
|
use crate::{
|
||||||
|
objects::{comment::ApubComment, post::ApubPost},
|
||||||
|
protocol::objects::{note::Note, page::Page},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PostOrComment {
|
pub enum PostOrComment {
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
use crate::{
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{
|
|
||||||
comment::{ApubComment, Note},
|
|
||||||
community::{ApubCommunity, Group},
|
|
||||||
person::{ApubPerson, Person},
|
|
||||||
post::{ApubPost, Page},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use activitystreams::chrono::NaiveDateTime;
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
traits::ApubObject,
|
traits::ApubObject,
|
||||||
|
@ -21,8 +15,12 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
|
||||||
use url::Url;
|
use crate::{
|
||||||
|
fetcher::object_id::ObjectId,
|
||||||
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
|
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
|
||||||
|
};
|
||||||
|
|
||||||
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{community::announce::GetCommunity, verify_person_in_community},
|
||||||
community::announce::{AnnouncableActivities, AnnounceActivity},
|
activity_lists::GroupInboxActivities,
|
||||||
extract_community,
|
|
||||||
following::{follow::FollowCommunity, undo::UndoFollowCommunity},
|
|
||||||
report::Report,
|
|
||||||
},
|
|
||||||
collections::{
|
collections::{
|
||||||
community_moderators::ApubCommunityModerators,
|
community_moderators::ApubCommunityModerators,
|
||||||
community_outbox::ApubCommunityOutbox,
|
community_outbox::ApubCommunityOutbox,
|
||||||
CommunityContext,
|
CommunityContext,
|
||||||
},
|
},
|
||||||
context::lemmy_context,
|
context::WithContext,
|
||||||
fetcher::object_id::ObjectId,
|
fetcher::object_id::ObjectId,
|
||||||
generate_outbox_url,
|
generate_outbox_url,
|
||||||
http::{
|
http::{
|
||||||
|
@ -20,20 +16,19 @@ use crate::{
|
||||||
receive_activity,
|
receive_activity,
|
||||||
},
|
},
|
||||||
objects::community::ApubCommunity,
|
objects::community::ApubCommunity,
|
||||||
};
|
protocol::{
|
||||||
use activitystreams::{
|
activities::community::announce::AnnounceActivity,
|
||||||
base::BaseExt,
|
collections::group_followers::GroupFollowers,
|
||||||
collection::{CollectionExt, UnorderedCollection},
|
},
|
||||||
};
|
};
|
||||||
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
|
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject};
|
use lemmy_apub_lib::traits::{ActivityFields, ApubObject};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::trace;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(crate) struct CommunityQuery {
|
pub(crate) struct CommunityQuery {
|
||||||
|
@ -60,16 +55,6 @@ pub(crate) async fn get_apub_community_http(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
#[activity_handler(LemmyContext)]
|
|
||||||
pub enum GroupInboxActivities {
|
|
||||||
FollowCommunity(FollowCommunity),
|
|
||||||
UndoFollowCommunity(UndoFollowCommunity),
|
|
||||||
AnnouncableActivities(AnnouncableActivities),
|
|
||||||
Report(Report),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for all incoming receive to community inboxes.
|
/// Handler for all incoming receive to community inboxes.
|
||||||
pub async fn community_inbox(
|
pub async fn community_inbox(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
|
@ -78,10 +63,10 @@ pub async fn community_inbox(
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let unparsed = payload_to_string(payload).await?;
|
let unparsed = payload_to_string(payload).await?;
|
||||||
trace!("Received community inbox activity {}", unparsed);
|
info!("Received community inbox activity {}", unparsed);
|
||||||
let activity = serde_json::from_str::<GroupInboxActivities>(&unparsed)?;
|
let activity = serde_json::from_str::<WithContext<GroupInboxActivities>>(&unparsed)?;
|
||||||
|
|
||||||
receive_group_inbox(activity.clone(), request, &context).await?;
|
receive_group_inbox(activity.inner(), request, &context).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
@ -92,12 +77,16 @@ pub(in crate::http) async fn receive_group_inbox(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let res = receive_activity(request, activity.clone(), context).await;
|
let res = receive_activity(request, activity.clone(), context).await;
|
||||||
if let GroupInboxActivities::AnnouncableActivities(announcable) = activity.clone() {
|
|
||||||
let community = extract_community(&announcable.cc(), context, &mut 0).await?;
|
if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
|
||||||
|
let community = announcable.get_community(context, &mut 0).await?;
|
||||||
|
let actor_id = ObjectId::new(announcable.actor().clone());
|
||||||
|
verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
|
||||||
if community.local {
|
if community.local {
|
||||||
AnnounceActivity::send(announcable, &community, vec![], context).await?;
|
AnnounceActivity::send(announcable, &community, vec![], context).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,19 +99,8 @@ pub(crate) async fn get_apub_community_followers(
|
||||||
Community::read_from_name(conn, &info.community_name)
|
Community::read_from_name(conn, &info.community_name)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
let followers = GroupFollowers::new(community, &context).await?;
|
||||||
let community_id = community.id;
|
Ok(create_apub_response(&followers))
|
||||||
let community_followers = blocking(context.pool(), move |conn| {
|
|
||||||
CommunityFollowerView::for_community(conn, community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let mut collection = UnorderedCollection::new();
|
|
||||||
collection
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(community.followers_url.into())
|
|
||||||
.set_total_items(community_followers.len() as u64);
|
|
||||||
Ok(create_apub_response(&collection))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the community outbox, which is populated by a maximum of 20 posts (but no other
|
/// Returns the community outbox, which is populated by a maximum of 20 posts (but no other
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
activity_lists::SharedInboxActivities,
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
|
context::WithContext,
|
||||||
fetcher::get_or_fetch_and_upsert_actor,
|
fetcher::get_or_fetch_and_upsert_actor,
|
||||||
http::{
|
http::{community::receive_group_inbox, person::receive_person_inbox},
|
||||||
community::{receive_group_inbox, GroupInboxActivities},
|
|
||||||
person::{receive_person_inbox, PersonInboxActivities},
|
|
||||||
},
|
|
||||||
insert_activity,
|
insert_activity,
|
||||||
};
|
};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
|
@ -27,7 +26,7 @@ use lemmy_apub_lib::{
|
||||||
use lemmy_db_schema::{source::activity::Activity, DbPool};
|
use lemmy_db_schema::{source::activity::Activity, DbPool};
|
||||||
use lemmy_utils::{location_info, LemmyError};
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::{info, trace};
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fmt::Debug, io::Read};
|
use std::{fmt::Debug, io::Read};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -38,25 +37,15 @@ mod person;
|
||||||
mod post;
|
mod post;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
#[activity_handler(LemmyContext)]
|
|
||||||
pub enum SharedInboxActivities {
|
|
||||||
GroupInboxActivities(GroupInboxActivities),
|
|
||||||
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
|
|
||||||
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
|
|
||||||
PersonInboxActivities(PersonInboxActivities),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn shared_inbox(
|
pub async fn shared_inbox(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let unparsed = payload_to_string(payload).await?;
|
let unparsed = payload_to_string(payload).await?;
|
||||||
trace!("Received shared inbox activity {}", unparsed);
|
info!("Received shared inbox activity {}", unparsed);
|
||||||
let activity = serde_json::from_str::<SharedInboxActivities>(&unparsed)?;
|
let activity = serde_json::from_str::<WithContext<SharedInboxActivities>>(&unparsed)?;
|
||||||
match activity {
|
match activity.inner() {
|
||||||
SharedInboxActivities::GroupInboxActivities(g) => {
|
SharedInboxActivities::GroupInboxActivities(g) => {
|
||||||
receive_group_inbox(g, request, &context).await
|
receive_group_inbox(g, request, &context).await
|
||||||
}
|
}
|
||||||
|
@ -134,7 +123,7 @@ where
|
||||||
{
|
{
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type(APUB_JSON_CONTENT_TYPE)
|
.content_type(APUB_JSON_CONTENT_TYPE)
|
||||||
.json(data)
|
.json(WithContext::new(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse<Body>
|
fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse<Body>
|
||||||
|
@ -144,7 +133,7 @@ where
|
||||||
HttpResponse::Gone()
|
HttpResponse::Gone()
|
||||||
.content_type(APUB_JSON_CONTENT_TYPE)
|
.content_type(APUB_JSON_CONTENT_TYPE)
|
||||||
.status(StatusCode::GONE)
|
.status(StatusCode::GONE)
|
||||||
.json(data)
|
.json(WithContext::new(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activity_lists::PersonInboxActivities,
|
||||||
community::announce::{AnnouncableActivities, AnnounceActivity},
|
context::WithContext,
|
||||||
following::accept::AcceptFollowCommunity,
|
|
||||||
private_message::{
|
|
||||||
create_or_update::CreateOrUpdatePrivateMessage,
|
|
||||||
delete::DeletePrivateMessage,
|
|
||||||
undo_delete::UndoDeletePrivateMessage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
generate_outbox_url,
|
|
||||||
http::{
|
http::{
|
||||||
create_apub_response,
|
create_apub_response,
|
||||||
create_apub_tombstone_response,
|
create_apub_tombstone_response,
|
||||||
|
@ -17,20 +8,16 @@ use crate::{
|
||||||
receive_activity,
|
receive_activity,
|
||||||
},
|
},
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
};
|
protocol::collections::person_outbox::PersonOutbox,
|
||||||
use activitystreams::{
|
|
||||||
base::BaseExt,
|
|
||||||
collection::{CollectionExt, OrderedCollection},
|
|
||||||
};
|
};
|
||||||
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
|
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ApubObject};
|
use lemmy_apub_lib::traits::ApubObject;
|
||||||
use lemmy_db_schema::source::person::Person;
|
use lemmy_db_schema::source::person::Person;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::trace;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct PersonQuery {
|
pub struct PersonQuery {
|
||||||
|
@ -59,19 +46,6 @@ pub(crate) async fn get_apub_person_http(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
#[activity_handler(LemmyContext)]
|
|
||||||
pub enum PersonInboxActivities {
|
|
||||||
AcceptFollowCommunity(AcceptFollowCommunity),
|
|
||||||
/// Some activities can also be sent from user to user, eg a comment with mentions
|
|
||||||
AnnouncableActivities(AnnouncableActivities),
|
|
||||||
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
|
|
||||||
DeletePrivateMessage(DeletePrivateMessage),
|
|
||||||
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
|
|
||||||
AnnounceActivity(Box<AnnounceActivity>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn person_inbox(
|
pub async fn person_inbox(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
|
@ -79,9 +53,9 @@ pub async fn person_inbox(
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let unparsed = payload_to_string(payload).await?;
|
let unparsed = payload_to_string(payload).await?;
|
||||||
trace!("Received person inbox activity {}", unparsed);
|
info!("Received person inbox activity {}", unparsed);
|
||||||
let activity = serde_json::from_str::<PersonInboxActivities>(&unparsed)?;
|
let activity = serde_json::from_str::<WithContext<PersonInboxActivities>>(&unparsed)?;
|
||||||
receive_person_inbox(activity, request, &context).await
|
receive_person_inbox(activity.inner(), request, &context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::http) async fn receive_person_inbox(
|
pub(in crate::http) async fn receive_person_inbox(
|
||||||
|
@ -100,12 +74,6 @@ pub(crate) async fn get_apub_person_outbox(
|
||||||
Person::find_by_name(conn, &info.user_name)
|
Person::find_by_name(conn, &info.user_name)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
// TODO: populate the person outbox
|
let outbox = PersonOutbox::new(person).await?;
|
||||||
let mut collection = OrderedCollection::new();
|
Ok(create_apub_response(&outbox))
|
||||||
collection
|
|
||||||
.set_many_items(Vec::<Url>::new())
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_outbox_url(&person.actor_id)?.into())
|
|
||||||
.set_total_items(0_u64);
|
|
||||||
Ok(create_apub_response(&collection))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
pub mod activities;
|
pub mod activities;
|
||||||
|
pub(crate) mod activity_lists;
|
||||||
pub(crate) mod collections;
|
pub(crate) mod collections;
|
||||||
mod context;
|
mod context;
|
||||||
pub mod fetcher;
|
pub mod fetcher;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
pub mod objects;
|
pub mod objects;
|
||||||
|
pub mod protocol;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
@ -12,16 +14,10 @@ extern crate lazy_static;
|
||||||
use crate::fetcher::post_or_comment::PostOrComment;
|
use crate::fetcher::post_or_comment::PostOrComment;
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{activity_queue::send_activity, traits::ActorType};
|
use lemmy_apub_lib::webfinger::{webfinger_resolve_actor, WebfingerType};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool};
|
||||||
newtypes::{CommunityId, DbUrl},
|
|
||||||
source::{activity::Activity, person::Person},
|
|
||||||
DbPool,
|
|
||||||
};
|
|
||||||
use lemmy_db_views_actor::community_person_ban_view::CommunityPersonBanView;
|
|
||||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::info;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
@ -34,6 +30,8 @@ use url::{ParseError, Url};
|
||||||
/// - URL being in the allowlist (if it is active)
|
/// - URL being in the allowlist (if it is active)
|
||||||
/// - URL not being in the blocklist (if it is active)
|
/// - URL not being in the blocklist (if it is active)
|
||||||
///
|
///
|
||||||
|
/// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
|
||||||
|
/// post/comment in a local community.
|
||||||
pub(crate) fn check_is_apub_id_valid(
|
pub(crate) fn check_is_apub_id_valid(
|
||||||
apub_id: &Url,
|
apub_id: &Url,
|
||||||
use_strict_allowlist: bool,
|
use_strict_allowlist: bool,
|
||||||
|
@ -92,16 +90,6 @@ pub(crate) fn check_is_apub_id_valid(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
pub trait CommunityType {
|
|
||||||
fn followers_url(&self) -> Url;
|
|
||||||
async fn get_follower_inboxes(
|
|
||||||
&self,
|
|
||||||
pool: &DbPool,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> Result<Vec<Url>, LemmyError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum EndpointType {
|
pub enum EndpointType {
|
||||||
Community,
|
Community,
|
||||||
Person,
|
Person,
|
||||||
|
@ -111,7 +99,7 @@ pub enum EndpointType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an apub endpoint for a given domain, IE xyz.tld
|
/// Generates an apub endpoint for a given domain, IE xyz.tld
|
||||||
fn generate_apub_endpoint_for_domain(
|
pub fn generate_local_apub_endpoint(
|
||||||
endpoint_type: EndpointType,
|
endpoint_type: EndpointType,
|
||||||
name: &str,
|
name: &str,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
|
@ -127,15 +115,6 @@ fn generate_apub_endpoint_for_domain(
|
||||||
Ok(Url::parse(&format!("{}/{}/{}", domain, point, name))?.into())
|
Ok(Url::parse(&format!("{}/{}/{}", domain, point, name))?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the ActivityPub ID for a given object type and ID.
|
|
||||||
pub fn generate_apub_endpoint(
|
|
||||||
endpoint_type: EndpointType,
|
|
||||||
name: &str,
|
|
||||||
protocol_and_hostname: &str,
|
|
||||||
) -> Result<DbUrl, ParseError> {
|
|
||||||
generate_apub_endpoint_for_domain(endpoint_type, name, protocol_and_hostname)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||||
Ok(Url::parse(&format!("{}/followers", actor_id))?.into())
|
Ok(Url::parse(&format!("{}/followers", actor_id))?.into())
|
||||||
}
|
}
|
||||||
|
@ -169,23 +148,31 @@ fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
|
||||||
|
|
||||||
/// Takes in a shortname of the type dessalines@xyz.tld or dessalines (assumed to be local), and outputs the actor id.
|
/// Takes in a shortname of the type dessalines@xyz.tld or dessalines (assumed to be local), and outputs the actor id.
|
||||||
/// Used in the API for communities and users.
|
/// Used in the API for communities and users.
|
||||||
pub fn build_actor_id_from_shortname(
|
pub async fn get_actor_id_from_name(
|
||||||
endpoint_type: EndpointType,
|
webfinger_type: WebfingerType,
|
||||||
short_name: &str,
|
short_name: &str,
|
||||||
settings: &Settings,
|
context: &LemmyContext,
|
||||||
) -> Result<DbUrl, ParseError> {
|
) -> Result<DbUrl, LemmyError> {
|
||||||
let split = short_name.split('@').collect::<Vec<&str>>();
|
let split = short_name.split('@').collect::<Vec<&str>>();
|
||||||
|
|
||||||
let name = split[0];
|
let name = split[0];
|
||||||
|
|
||||||
// If there's no @, its local
|
// If there's no @, its local
|
||||||
let domain = if split.len() == 1 {
|
if split.len() == 1 {
|
||||||
settings.get_protocol_and_hostname()
|
let domain = context.settings().get_protocol_and_hostname();
|
||||||
|
let endpoint_type = match webfinger_type {
|
||||||
|
WebfingerType::Person => EndpointType::Person,
|
||||||
|
WebfingerType::Group => EndpointType::Community,
|
||||||
|
};
|
||||||
|
Ok(generate_local_apub_endpoint(endpoint_type, name, &domain)?)
|
||||||
} else {
|
} else {
|
||||||
format!("{}://{}", settings.get_protocol_string(), split[1])
|
let protocol = context.settings().get_protocol_string();
|
||||||
};
|
Ok(
|
||||||
|
webfinger_resolve_actor(name, split[1], webfinger_type, context.client(), protocol)
|
||||||
generate_apub_endpoint_for_domain(endpoint_type, name, &domain)
|
.await?
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a sent or received activity in the database, for logging purposes. These records are not
|
/// Store a sent or received activity in the database, for logging purposes. These records are not
|
||||||
|
@ -207,64 +194,3 @@ where
|
||||||
.await??;
|
.await??;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_community_or_site_ban(
|
|
||||||
person: &Person,
|
|
||||||
community_id: CommunityId,
|
|
||||||
pool: &DbPool,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
if person.banned {
|
|
||||||
return Err(anyhow!("Person is banned from site").into());
|
|
||||||
}
|
|
||||||
let person_id = person.id;
|
|
||||||
let is_banned =
|
|
||||||
move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
|
|
||||||
if blocking(pool, is_banned).await? {
|
|
||||||
return Err(anyhow!("Person is banned from community").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn send_lemmy_activity<T: Serialize>(
|
|
||||||
context: &LemmyContext,
|
|
||||||
activity: &T,
|
|
||||||
activity_id: &Url,
|
|
||||||
actor: &dyn ActorType,
|
|
||||||
inboxes: Vec<Url>,
|
|
||||||
sensitive: bool,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
if !context.settings().federation.enabled || inboxes.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Sending activity {}", activity_id.to_string());
|
|
||||||
|
|
||||||
// Don't send anything to ourselves
|
|
||||||
// TODO: this should be a debug assert
|
|
||||||
let hostname = context.settings().get_hostname_without_port()?;
|
|
||||||
let inboxes: Vec<&Url> = inboxes
|
|
||||||
.iter()
|
|
||||||
.filter(|i| i.domain().expect("valid inbox url") != hostname)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let serialised_activity = serde_json::to_string(&activity)?;
|
|
||||||
|
|
||||||
insert_activity(
|
|
||||||
activity_id,
|
|
||||||
serialised_activity.clone(),
|
|
||||||
true,
|
|
||||||
sensitive,
|
|
||||||
context.pool(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
send_activity(
|
|
||||||
serialised_activity,
|
|
||||||
actor,
|
|
||||||
inboxes,
|
|
||||||
context.client(),
|
|
||||||
context.activity_queue(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,29 +1,17 @@
|
||||||
use crate::{
|
use std::ops::Deref;
|
||||||
activities::{verify_is_public, verify_person_in_community},
|
|
||||||
context::lemmy_context,
|
use activitystreams::{object::kind::NoteType, public};
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
objects::{person::ApubPerson, post::ApubPost, tombstone::Tombstone, Source},
|
|
||||||
PostOrComment,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
base::AnyBase,
|
|
||||||
chrono::NaiveDateTime,
|
|
||||||
object::kind::NoteType,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
public,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::NaiveDateTime;
|
||||||
use html2md::parse_html;
|
use html2md::parse_html;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{
|
||||||
traits::ApubObject,
|
traits::ApubObject,
|
||||||
values::{MediaTypeHtml, MediaTypeMarkdown},
|
values::{MediaTypeHtml, MediaTypeMarkdown},
|
||||||
verify::verify_domains_match,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::CommentId,
|
|
||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentForm},
|
comment::{Comment, CommentForm},
|
||||||
community::Community,
|
community::Community,
|
||||||
|
@ -37,107 +25,21 @@ use lemmy_utils::{
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[skip_serializing_none]
|
use crate::{
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
activities::verify_person_in_community,
|
||||||
#[serde(rename_all = "camelCase")]
|
check_is_apub_id_valid,
|
||||||
pub struct Note {
|
fetcher::object_id::ObjectId,
|
||||||
#[serde(rename = "@context")]
|
protocol::{
|
||||||
context: OneOrMany<AnyBase>,
|
objects::{
|
||||||
r#type: NoteType,
|
note::{Note, SourceCompat},
|
||||||
id: Url,
|
tombstone::Tombstone,
|
||||||
pub(crate) attributed_to: ObjectId<ApubPerson>,
|
},
|
||||||
/// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain
|
Source,
|
||||||
/// the community ID, as it would be incompatible with Pleroma (and we can get the community from
|
},
|
||||||
/// the post in [`in_reply_to`]).
|
PostOrComment,
|
||||||
to: Vec<Url>,
|
};
|
||||||
content: String,
|
use lemmy_utils::utils::markdown_to_html;
|
||||||
media_type: Option<MediaTypeHtml>,
|
|
||||||
source: SourceCompat,
|
|
||||||
in_reply_to: ObjectId<PostOrComment>,
|
|
||||||
published: Option<DateTime<FixedOffset>>,
|
|
||||||
updated: Option<DateTime<FixedOffset>>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pleroma puts a raw string in the source, so we have to handle it here for deserialization to work
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum SourceCompat {
|
|
||||||
Lemmy(Source),
|
|
||||||
Pleroma(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Note {
|
|
||||||
pub(crate) fn id_unchecked(&self) -> &Url {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
|
|
||||||
verify_domains_match(&self.id, expected_domain)?;
|
|
||||||
Ok(&self.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_parents(
|
|
||||||
&self,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(ApubPost, Option<CommentId>), LemmyError> {
|
|
||||||
// Fetch parent comment chain in a box, otherwise it can cause a stack overflow.
|
|
||||||
let parent = Box::pin(
|
|
||||||
self
|
|
||||||
.in_reply_to
|
|
||||||
.dereference(context, request_counter)
|
|
||||||
.await?,
|
|
||||||
);
|
|
||||||
match parent.deref() {
|
|
||||||
PostOrComment::Post(p) => {
|
|
||||||
// Workaround because I cant figure out how to get the post out of the box (and we dont
|
|
||||||
// want to stackoverflow in a deep comment hierarchy).
|
|
||||||
let post_id = p.id;
|
|
||||||
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
|
||||||
Ok((post.into(), None))
|
|
||||||
}
|
|
||||||
PostOrComment::Comment(c) => {
|
|
||||||
let post_id = c.post_id;
|
|
||||||
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
|
||||||
Ok((post.into(), Some(c.id)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn verify(
|
|
||||||
&self,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let (post, _parent_comment_id) = self.get_parents(context, request_counter).await?;
|
|
||||||
let community_id = post.community_id;
|
|
||||||
let community = blocking(context.pool(), move |conn| {
|
|
||||||
Community::read(conn, community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
if post.locked {
|
|
||||||
return Err(anyhow!("Post is locked").into());
|
|
||||||
}
|
|
||||||
verify_domains_match(self.attributed_to.inner(), &self.id)?;
|
|
||||||
verify_person_in_community(
|
|
||||||
&self.attributed_to,
|
|
||||||
&ObjectId::new(community.actor_id),
|
|
||||||
context,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
verify_is_public(&self.to)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ApubComment(Comment);
|
pub struct ApubComment(Comment);
|
||||||
|
@ -202,12 +104,11 @@ impl ApubObject for ApubComment {
|
||||||
};
|
};
|
||||||
|
|
||||||
let note = Note {
|
let note = Note {
|
||||||
context: lemmy_context(),
|
|
||||||
r#type: NoteType::Note,
|
r#type: NoteType::Note,
|
||||||
id: self.ap_id.to_owned().into_inner(),
|
id: self.ap_id.to_owned().into_inner(),
|
||||||
attributed_to: ObjectId::new(creator.actor_id),
|
attributed_to: ObjectId::new(creator.actor_id),
|
||||||
to: vec![public()],
|
to: vec![public()],
|
||||||
content: self.content.clone(),
|
content: markdown_to_html(&self.content),
|
||||||
media_type: Some(MediaTypeHtml::Html),
|
media_type: Some(MediaTypeHtml::Html),
|
||||||
source: SourceCompat::Lemmy(Source {
|
source: SourceCompat::Lemmy(Source {
|
||||||
content: self.content.clone(),
|
content: self.content.clone(),
|
||||||
|
@ -244,6 +145,19 @@ impl ApubObject for ApubComment {
|
||||||
.dereference(context, request_counter)
|
.dereference(context, request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
let (post, parent_comment_id) = note.get_parents(context, request_counter).await?;
|
let (post, parent_comment_id) = note.get_parents(context, request_counter).await?;
|
||||||
|
let community_id = post.community_id;
|
||||||
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
Community::read(conn, community_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
check_is_apub_id_valid(¬e.id, community.local, &context.settings())?;
|
||||||
|
verify_person_in_community(
|
||||||
|
¬e.attributed_to,
|
||||||
|
&community.into(),
|
||||||
|
context,
|
||||||
|
request_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
if post.locked {
|
if post.locked {
|
||||||
return Err(anyhow!("Post is locked").into());
|
return Err(anyhow!("Post is locked").into());
|
||||||
}
|
}
|
||||||
|
@ -274,10 +188,12 @@ impl ApubObject for ApubComment {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::objects::{
|
use crate::objects::{
|
||||||
community::ApubCommunity,
|
community::{tests::parse_lemmy_community, ApubCommunity},
|
||||||
|
person::{tests::parse_lemmy_person, ApubPerson},
|
||||||
|
post::ApubPost,
|
||||||
tests::{file_to_json_object, init_context},
|
tests::{file_to_json_object, init_context},
|
||||||
};
|
};
|
||||||
use assert_json_diff::assert_json_include;
|
use assert_json_diff::assert_json_include;
|
||||||
|
@ -287,15 +203,9 @@ mod tests {
|
||||||
url: &Url,
|
url: &Url,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> (ApubPerson, ApubCommunity, ApubPost) {
|
) -> (ApubPerson, ApubCommunity, ApubPost) {
|
||||||
let person_json = file_to_json_object("assets/lemmy-person.json");
|
let person = parse_lemmy_person(context).await;
|
||||||
let person = ApubPerson::from_apub(&person_json, context, url, &mut 0)
|
let community = parse_lemmy_community(context).await;
|
||||||
.await
|
let post_json = file_to_json_object("assets/lemmy/objects/page.json");
|
||||||
.unwrap();
|
|
||||||
let community_json = file_to_json_object("assets/lemmy-community.json");
|
|
||||||
let community = ApubCommunity::from_apub(&community_json, context, url, &mut 0)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let post_json = file_to_json_object("assets/lemmy-post.json");
|
|
||||||
let post = ApubPost::from_apub(&post_json, context, url, &mut 0)
|
let post = ApubPost::from_apub(&post_json, context, url, &mut 0)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -310,14 +220,12 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_parse_lemmy_comment() {
|
pub(crate) async fn test_parse_lemmy_comment() {
|
||||||
// TODO: changed ObjectId::dereference() so that it always fetches if
|
|
||||||
// last_refreshed_at() == None. But post doesnt store that and expects to never be refetched
|
|
||||||
let context = init_context();
|
let context = init_context();
|
||||||
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap();
|
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap();
|
||||||
let data = prepare_comment_test(&url, &context).await;
|
let data = prepare_comment_test(&url, &context).await;
|
||||||
|
|
||||||
let json = file_to_json_object("assets/lemmy-comment.json");
|
let json = file_to_json_object("assets/lemmy/objects/note.json");
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let comment = ApubComment::from_apub(&json, &context, &url, &mut request_counter)
|
let comment = ApubComment::from_apub(&json, &context, &url, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
|
@ -345,11 +253,11 @@ mod tests {
|
||||||
let pleroma_url =
|
let pleroma_url =
|
||||||
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")
|
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let person_json = file_to_json_object("assets/pleroma-person.json");
|
let person_json = file_to_json_object("assets/pleroma/objects/person.json");
|
||||||
ApubPerson::from_apub(&person_json, &context, &pleroma_url, &mut 0)
|
ApubPerson::from_apub(&person_json, &context, &pleroma_url, &mut 0)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let json = file_to_json_object("assets/pleroma-comment.json");
|
let json = file_to_json_object("assets/pleroma/objects/note.json");
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let comment = ApubComment::from_apub(&json, &context, &pleroma_url, &mut request_counter)
|
let comment = ApubComment::from_apub(&json, &context, &pleroma_url, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,128 +1,37 @@
|
||||||
use crate::{
|
|
||||||
check_is_apub_id_valid,
|
|
||||||
collections::{
|
|
||||||
community_moderators::ApubCommunityModerators,
|
|
||||||
community_outbox::ApubCommunityOutbox,
|
|
||||||
CommunityContext,
|
|
||||||
},
|
|
||||||
context::lemmy_context,
|
|
||||||
fetcher::object_id::ObjectId,
|
|
||||||
generate_moderators_url,
|
|
||||||
generate_outbox_url,
|
|
||||||
objects::{get_summary_from_string_or_source, tombstone::Tombstone, ImageObject, Source},
|
|
||||||
CommunityType,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
actor::{kind::GroupType, Endpoints},
|
actor::{kind::GroupType, Endpoints},
|
||||||
base::AnyBase,
|
|
||||||
chrono::NaiveDateTime,
|
|
||||||
object::kind::ImageType,
|
object::kind::ImageType,
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::NaiveDateTime;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lemmy_api_common::blocking;
|
|
||||||
use lemmy_apub_lib::{
|
|
||||||
signatures::PublicKey,
|
|
||||||
traits::{ActorType, ApubObject},
|
|
||||||
values::MediaTypeMarkdown,
|
|
||||||
verify::verify_domains_match,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
|
||||||
naive_now,
|
|
||||||
source::community::{Community, CommunityForm},
|
|
||||||
DbPool,
|
|
||||||
};
|
|
||||||
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
|
||||||
use lemmy_utils::{
|
|
||||||
settings::structs::Settings,
|
|
||||||
utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
|
|
||||||
LemmyError,
|
|
||||||
};
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[skip_serializing_none]
|
use crate::{
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
check_is_apub_id_valid,
|
||||||
#[serde(rename_all = "camelCase")]
|
collections::{community_moderators::ApubCommunityModerators, CommunityContext},
|
||||||
pub struct Group {
|
fetcher::object_id::ObjectId,
|
||||||
#[serde(rename = "@context")]
|
generate_moderators_url,
|
||||||
context: OneOrMany<AnyBase>,
|
generate_outbox_url,
|
||||||
#[serde(rename = "type")]
|
protocol::{
|
||||||
kind: GroupType,
|
objects::{group::Group, tombstone::Tombstone},
|
||||||
id: Url,
|
ImageObject,
|
||||||
/// username, set at account creation and can never be changed
|
Source,
|
||||||
preferred_username: String,
|
},
|
||||||
/// title (can be changed at any time)
|
};
|
||||||
name: String,
|
use lemmy_api_common::blocking;
|
||||||
summary: Option<String>,
|
use lemmy_apub_lib::{
|
||||||
source: Option<Source>,
|
traits::{ActorType, ApubObject},
|
||||||
icon: Option<ImageObject>,
|
values::MediaTypeMarkdown,
|
||||||
/// banner
|
};
|
||||||
image: Option<ImageObject>,
|
use lemmy_db_schema::source::community::Community;
|
||||||
// lemmy extension
|
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
||||||
sensitive: Option<bool>,
|
use lemmy_utils::{
|
||||||
// lemmy extension
|
utils::{convert_datetime, markdown_to_html},
|
||||||
pub(crate) moderators: Option<ObjectId<ApubCommunityModerators>>,
|
LemmyError,
|
||||||
inbox: Url,
|
};
|
||||||
pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
|
use lemmy_websocket::LemmyContext;
|
||||||
followers: Url,
|
|
||||||
endpoints: Endpoints<Url>,
|
|
||||||
public_key: PublicKey,
|
|
||||||
published: Option<DateTime<FixedOffset>>,
|
|
||||||
updated: Option<DateTime<FixedOffset>>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Group {
|
|
||||||
pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
|
|
||||||
verify_domains_match(&self.id, expected_domain)?;
|
|
||||||
Ok(&self.id)
|
|
||||||
}
|
|
||||||
pub(crate) async fn from_apub_to_form(
|
|
||||||
group: &Group,
|
|
||||||
expected_domain: &Url,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> Result<CommunityForm, LemmyError> {
|
|
||||||
let actor_id = Some(group.id(expected_domain)?.clone().into());
|
|
||||||
let name = group.preferred_username.clone();
|
|
||||||
let title = group.name.clone();
|
|
||||||
let description = get_summary_from_string_or_source(&group.summary, &group.source);
|
|
||||||
let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into());
|
|
||||||
|
|
||||||
let slur_regex = &settings.slur_regex();
|
|
||||||
check_slurs(&name, slur_regex)?;
|
|
||||||
check_slurs(&title, slur_regex)?;
|
|
||||||
check_slurs_opt(&description, slur_regex)?;
|
|
||||||
|
|
||||||
Ok(CommunityForm {
|
|
||||||
name,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
removed: None,
|
|
||||||
published: group.published.map(|u| u.naive_local()),
|
|
||||||
updated: group.updated.map(|u| u.naive_local()),
|
|
||||||
deleted: None,
|
|
||||||
nsfw: Some(group.sensitive.unwrap_or(false)),
|
|
||||||
actor_id,
|
|
||||||
local: Some(false),
|
|
||||||
private_key: None,
|
|
||||||
public_key: Some(group.public_key.public_key_pem.clone()),
|
|
||||||
last_refreshed_at: Some(naive_now()),
|
|
||||||
icon: Some(group.icon.clone().map(|i| i.url.into())),
|
|
||||||
banner: Some(group.image.clone().map(|i| i.url.into())),
|
|
||||||
followers_url: Some(group.followers.clone().into()),
|
|
||||||
inbox_url: Some(group.inbox.clone().into()),
|
|
||||||
shared_inbox_url: Some(shared_inbox),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ApubCommunity(Community);
|
pub struct ApubCommunity(Community);
|
||||||
|
@ -186,7 +95,6 @@ impl ApubObject for ApubCommunity {
|
||||||
});
|
});
|
||||||
|
|
||||||
let group = Group {
|
let group = Group {
|
||||||
context: lemmy_context(),
|
|
||||||
kind: GroupType::Group,
|
kind: GroupType::Group,
|
||||||
id: self.actor_id(),
|
id: self.actor_id(),
|
||||||
preferred_username: self.name.clone(),
|
preferred_username: self.name.clone(),
|
||||||
|
@ -283,32 +191,32 @@ impl ActorType for ApubCommunity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
impl ApubCommunity {
|
||||||
impl CommunityType for Community {
|
|
||||||
fn followers_url(&self) -> Url {
|
|
||||||
self.followers_url.clone().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For a given community, returns the inboxes of all followers.
|
/// For a given community, returns the inboxes of all followers.
|
||||||
async fn get_follower_inboxes(
|
pub(crate) async fn get_follower_inboxes(
|
||||||
&self,
|
&self,
|
||||||
pool: &DbPool,
|
additional_inboxes: Vec<Url>,
|
||||||
settings: &Settings,
|
context: &LemmyContext,
|
||||||
) -> Result<Vec<Url>, LemmyError> {
|
) -> Result<Vec<Url>, LemmyError> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
|
||||||
let follows = blocking(pool, move |conn| {
|
let follows = blocking(context.pool(), move |conn| {
|
||||||
CommunityFollowerView::for_community(conn, id)
|
CommunityFollowerView::for_community(conn, id)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let inboxes = follows
|
let follower_inboxes: Vec<Url> = follows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|f| !f.follower.local)
|
.filter(|f| !f.follower.local)
|
||||||
.map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url))
|
.map(|f| f.follower.shared_inbox_url.unwrap_or(f.follower.inbox_url))
|
||||||
.map(|i| i.into_inner())
|
.map(|i| i.into_inner())
|
||||||
|
.collect();
|
||||||
|
let inboxes = vec![follower_inboxes, additional_inboxes]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.unique()
|
.unique()
|
||||||
|
.filter(|inbox| inbox.host_str() != Some(&context.settings().hostname))
|
||||||
// Don't send to blocked instances
|
// Don't send to blocked instances
|
||||||
.filter(|inbox| check_is_apub_id_valid(inbox, false, settings).is_ok())
|
.filter(|inbox| check_is_apub_id_valid(inbox, false, &context.settings()).is_ok())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(inboxes)
|
Ok(inboxes)
|
||||||
|
@ -316,42 +224,39 @@ impl CommunityType for Community {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::objects::tests::{file_to_json_object, init_context};
|
use crate::objects::tests::{file_to_json_object, init_context};
|
||||||
use assert_json_diff::assert_json_include;
|
|
||||||
use lemmy_db_schema::traits::Crud;
|
use lemmy_db_schema::traits::Crud;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[actix_rt::test]
|
pub(crate) async fn parse_lemmy_community(context: &LemmyContext) -> ApubCommunity {
|
||||||
#[serial]
|
let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json");
|
||||||
async fn test_parse_lemmy_community() {
|
|
||||||
let context = init_context();
|
|
||||||
let mut json: Group = file_to_json_object("assets/lemmy-community.json");
|
|
||||||
let json_orig = json.clone();
|
|
||||||
// change these links so they dont fetch over the network
|
// change these links so they dont fetch over the network
|
||||||
json.moderators = Some(ObjectId::new(
|
json.moderators = None;
|
||||||
Url::parse("https://enterprise.lemmy.ml/c/tenforward/not_moderators").unwrap(),
|
|
||||||
));
|
|
||||||
json.outbox =
|
json.outbox =
|
||||||
ObjectId::new(Url::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap());
|
ObjectId::new(Url::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap());
|
||||||
|
|
||||||
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
||||||
let mut request_counter = 0;
|
let mut request_counter = 0;
|
||||||
let community = ApubCommunity::from_apub(&json, &context, &url, &mut request_counter)
|
let community = ApubCommunity::from_apub(&json, context, &url, &mut request_counter)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
// this makes two requests to the (intentionally) broken outbox/moderators collections
|
||||||
|
assert_eq!(request_counter, 1);
|
||||||
|
community
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_parse_lemmy_community() {
|
||||||
|
let context = init_context();
|
||||||
|
let community = parse_lemmy_community(&context).await;
|
||||||
|
|
||||||
assert_eq!(community.actor_id.clone().into_inner(), url);
|
|
||||||
assert_eq!(community.title, "Ten Forward");
|
assert_eq!(community.title, "Ten Forward");
|
||||||
assert!(community.public_key.is_some());
|
assert!(community.public_key.is_some());
|
||||||
assert!(!community.local);
|
assert!(!community.local);
|
||||||
assert_eq!(community.description.as_ref().unwrap().len(), 132);
|
assert_eq!(community.description.as_ref().unwrap().len(), 132);
|
||||||
// this makes two requests to the (intentionally) broken outbox/moderators collections
|
|
||||||
assert_eq!(request_counter, 2);
|
|
||||||
|
|
||||||
let to_apub = community.to_apub(&context).await.unwrap();
|
|
||||||
assert_json_include!(actual: json_orig, expected: to_apub);
|
|
||||||
|
|
||||||
Community::delete(&*context.pool().get().unwrap(), community.id).unwrap();
|
Community::delete(&*context.pool().get().unwrap(), community.id).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,13 @@
|
||||||
use activitystreams::object::kind::ImageType;
|
use crate::protocol::Source;
|
||||||
use html2md::parse_html;
|
use html2md::parse_html;
|
||||||
use lemmy_apub_lib::values::MediaTypeMarkdown;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
pub mod tombstone;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
pub(crate) fn get_summary_from_string_or_source(
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Source {
|
|
||||||
content: String,
|
|
||||||
media_type: MediaTypeMarkdown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ImageObject {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: ImageType,
|
|
||||||
url: Url,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_summary_from_string_or_source(
|
|
||||||
raw: &Option<String>,
|
raw: &Option<String>,
|
||||||
source: &Option<Source>,
|
source: &Option<Source>,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
|
@ -38,7 +19,7 @@ fn get_summary_from_string_or_source(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
pub(crate) mod tests {
|
||||||
use actix::Actor;
|
use actix::Actor;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
r2d2::{ConnectionManager, Pool},
|
r2d2::{ConnectionManager, Pool},
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue