mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-01 16:51:21 +00:00
Rewrite remaining activities (#1712)
* Limit type/method visibility in apub code * Simplify db_queries traits by removing generics * Simplify delete activity implementation * Rewrite delete activities * Implement helper functions for websocket message sending * When receiving delete reason as empty string, change to none * Rewrite remaining activities * Simplify inbox * Remove struct ActivityCommonFields, derive ActivityFields trait instead * Community should announce received activities to followers
This commit is contained in:
parent
2016afc9db
commit
f6f169b4eb
35 changed files with 963 additions and 1141 deletions
|
@ -8,12 +8,14 @@ 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::{
|
use lemmy_apub::activities::{
|
||||||
activities::following::{
|
community::{
|
||||||
follow::FollowCommunity as FollowCommunityApub,
|
add_mod::AddMod,
|
||||||
undo::UndoFollowCommunity,
|
block_user::BlockUserFromCommunity,
|
||||||
|
remove_mod::RemoveMod,
|
||||||
|
undo_block_user::UndoBlockUserFromCommunity,
|
||||||
},
|
},
|
||||||
CommunityType,
|
following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
|
||||||
};
|
};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
source::{comment::Comment_, community::CommunityModerator_, post::Post_},
|
source::{comment::Comment_, community::CommunityModerator_, post::Post_},
|
||||||
|
@ -219,16 +221,19 @@ impl Perform for BanFromCommunity {
|
||||||
.await?
|
.await?
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
community
|
BlockUserFromCommunity::send(&community, &banned_person, &local_user_view.person, context)
|
||||||
.send_block_user(&local_user_view.person, banned_person, context)
|
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
|
let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
|
||||||
if blocking(context.pool(), unban).await?.is_err() {
|
if blocking(context.pool(), unban).await?.is_err() {
|
||||||
return Err(ApiError::err("community_user_already_banned").into());
|
return Err(ApiError::err("community_user_already_banned").into());
|
||||||
}
|
}
|
||||||
community
|
UndoBlockUserFromCommunity::send(
|
||||||
.send_undo_block_user(&local_user_view.person, banned_person, context)
|
&community,
|
||||||
|
&banned_person,
|
||||||
|
&local_user_view.person,
|
||||||
|
context,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,13 +361,9 @@ impl Perform for AddModToCommunity {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
if data.added {
|
if data.added {
|
||||||
community
|
AddMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
|
||||||
.send_add_mod(&local_user_view.person, updated_mod, context)
|
|
||||||
.await?;
|
|
||||||
} else {
|
} else {
|
||||||
community
|
RemoveMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
|
||||||
.send_remove_mod(&local_user_view.person, updated_mod, context)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: in case a remote mod is added, this returns the old moderators list, it will only get
|
// Note: in case a remote mod is added, this returns the old moderators list, it will only get
|
||||||
|
|
|
@ -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::CommunityType;
|
use lemmy_apub::activities::community::update::UpdateCommunity;
|
||||||
use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud};
|
use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
naive_now,
|
naive_now,
|
||||||
|
@ -69,9 +69,7 @@ impl PerformCrud for EditCommunity {
|
||||||
.await?
|
.await?
|
||||||
.map_err(|_| ApiError::err("couldnt_update_community"))?;
|
.map_err(|_| ApiError::err("couldnt_update_community"))?;
|
||||||
|
|
||||||
updated_community
|
UpdateCommunity::send(&updated_community, &local_user_view.person, context).await?;
|
||||||
.send_update(local_user_view.person.to_owned(), context)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let op = UserOperationCrud::EditCommunity;
|
let op = UserOperationCrud::EditCommunity;
|
||||||
send_community_ws_message(data.community_id, op, websocket_id, None, context).await
|
send_community_ws_message(data.community_id, op, websocket_id, None, context).await
|
||||||
|
|
|
@ -13,14 +13,9 @@ use crate::{
|
||||||
objects::{comment::Note, FromApub, ToApub},
|
objects::{comment::Note, FromApub, ToApub},
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::link::Mention;
|
use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{
|
use lemmy_apub_lib::{values::PublicUrl, verify_domains_match, ActivityFields, ActivityHandler};
|
||||||
values::PublicUrl,
|
|
||||||
verify_domains_match,
|
|
||||||
ActivityCommonFields,
|
|
||||||
ActivityHandler,
|
|
||||||
};
|
|
||||||
use lemmy_db_queries::Crud;
|
use lemmy_db_queries::Crud;
|
||||||
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
|
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -28,17 +23,21 @@ use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateOrUpdateComment {
|
pub struct CreateOrUpdateComment {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: Note,
|
object: Note,
|
||||||
cc: Vec<Url>,
|
cc: Vec<Url>,
|
||||||
tag: Vec<Mention>,
|
tag: Vec<Mention>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateOrUpdateComment {
|
impl CreateOrUpdateComment {
|
||||||
|
@ -61,17 +60,15 @@ impl CreateOrUpdateComment {
|
||||||
let maa = collect_non_local_mentions(comment, &community, context).await?;
|
let maa = collect_non_local_mentions(comment, &community, context).await?;
|
||||||
|
|
||||||
let create_or_update = CreateOrUpdateComment {
|
let create_or_update = CreateOrUpdateComment {
|
||||||
|
actor: actor.actor_id(),
|
||||||
to: PublicUrl::Public,
|
to: PublicUrl::Public,
|
||||||
object: comment.to_apub(context.pool()).await?,
|
object: comment.to_apub(context.pool()).await?,
|
||||||
cc: maa.ccs,
|
cc: maa.ccs,
|
||||||
tag: maa.tags,
|
tag: maa.tags,
|
||||||
kind,
|
kind,
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: actor.actor_id(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
|
let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
|
||||||
|
@ -88,15 +85,10 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = extract_community(&self.cc, context, request_counter).await?;
|
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||||
|
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person_in_community(
|
verify_person_in_community(&self.actor, &community.actor_id(), context, request_counter)
|
||||||
&self.common.actor,
|
|
||||||
&community.actor_id(),
|
|
||||||
context,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
|
verify_domains_match(&self.actor, self.object.id_unchecked())?;
|
||||||
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
||||||
// comment deserialization)
|
// comment deserialization)
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
@ -108,10 +100,8 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let comment =
|
let comment = Comment::from_apub(&self.object, context, &self.actor, request_counter).await?;
|
||||||
Comment::from_apub(&self.object, context, &self.common.actor, request_counter).await?;
|
let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?;
|
||||||
let recipients =
|
|
||||||
get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
|
|
||||||
let notif_type = match self.kind {
|
let notif_type = match self.kind {
|
||||||
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
|
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
|
||||||
CreateOrUpdateType::Update => UserOperationCrud::EditComment,
|
CreateOrUpdateType::Update => UserOperationCrud::EditComment,
|
||||||
|
@ -122,8 +112,4 @@ impl ActivityHandler for CreateOrUpdateComment {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,77 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
|
community::announce::AnnouncableActivities,
|
||||||
|
generate_activity_id,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_add_remove_moderator_target,
|
verify_add_remove_moderator_target,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
|
activity_queue::send_to_community_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
||||||
CommunityType,
|
generate_moderators_url,
|
||||||
|
ActorType,
|
||||||
|
};
|
||||||
|
use activitystreams::{
|
||||||
|
activity::kind::AddType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::AddType, base::AnyBase};
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
|
use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
|
||||||
use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
|
use lemmy_db_schema::source::{
|
||||||
|
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||||
|
person::Person,
|
||||||
|
};
|
||||||
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, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddMod {
|
pub struct AddMod {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: Url,
|
object: Url,
|
||||||
target: Url,
|
target: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: AddType,
|
kind: AddType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddMod {
|
||||||
|
pub async fn send(
|
||||||
|
community: &Community,
|
||||||
|
added_mod: &Person,
|
||||||
|
actor: &Person,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let id = generate_activity_id(AddType::Add)?;
|
||||||
|
let add = AddMod {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: added_mod.actor_id(),
|
||||||
|
target: generate_moderators_url(&community.actor_id)?.into(),
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
kind: AddType::Add,
|
||||||
|
id: id.clone(),
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let activity = AnnouncableActivities::AddMod(add);
|
||||||
|
let inboxes = vec![added_mod.get_shared_inbox_or_inbox_url()];
|
||||||
|
send_to_community_new(activity, &id, actor, community, inboxes, context).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -37,9 +81,9 @@ impl ActivityHandler for AddMod {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?;
|
verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -70,17 +114,7 @@ impl ActivityHandler for AddMod {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
}
|
}
|
||||||
if community.local {
|
|
||||||
let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
|
|
||||||
community
|
|
||||||
.send_announce(anybase, Some(self.object.clone()), context)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
// TODO: send websocket notification about added mod
|
// TODO: send websocket notification about added mod
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@ use crate::{
|
||||||
add_mod::AddMod,
|
add_mod::AddMod,
|
||||||
block_user::BlockUserFromCommunity,
|
block_user::BlockUserFromCommunity,
|
||||||
list_community_follower_inboxes,
|
list_community_follower_inboxes,
|
||||||
|
remove_mod::RemoveMod,
|
||||||
undo_block_user::UndoBlockUserFromCommunity,
|
undo_block_user::UndoBlockUserFromCommunity,
|
||||||
|
update::UpdateCommunity,
|
||||||
},
|
},
|
||||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
generate_activity_id,
|
generate_activity_id,
|
||||||
post::create_or_update::CreateOrUpdatePost,
|
post::create_or_update::CreateOrUpdatePost,
|
||||||
removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity},
|
undo_remove::UndoRemovePostCommentOrCommunity,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_community,
|
verify_community,
|
||||||
voting::{undo_vote::UndoVote, vote::Vote},
|
voting::{undo_vote::UndoVote, vote::Vote},
|
||||||
|
@ -22,15 +24,20 @@ use crate::{
|
||||||
ActorType,
|
ActorType,
|
||||||
CommunityType,
|
CommunityType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::AnnounceType;
|
use activitystreams::{
|
||||||
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
activity::kind::AnnounceType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
|
};
|
||||||
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum AnnouncableActivities {
|
pub enum AnnouncableActivities {
|
||||||
CreateOrUpdateComment(CreateOrUpdateComment),
|
CreateOrUpdateComment(CreateOrUpdateComment),
|
||||||
|
@ -40,22 +47,27 @@ pub enum AnnouncableActivities {
|
||||||
Delete(Delete),
|
Delete(Delete),
|
||||||
UndoDelete(UndoDelete),
|
UndoDelete(UndoDelete),
|
||||||
UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
|
UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
|
||||||
|
UpdateCommunity(Box<UpdateCommunity>),
|
||||||
BlockUserFromCommunity(BlockUserFromCommunity),
|
BlockUserFromCommunity(BlockUserFromCommunity),
|
||||||
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
||||||
AddMod(AddMod),
|
AddMod(AddMod),
|
||||||
RemoveMod(RemoveMod),
|
RemoveMod(RemoveMod),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AnnounceActivity {
|
pub struct AnnounceActivity {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: AnnouncableActivities,
|
object: AnnouncableActivities,
|
||||||
cc: Vec<Url>,
|
cc: Vec<Url>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: AnnounceType,
|
kind: AnnounceType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnnounceActivity {
|
impl AnnounceActivity {
|
||||||
|
@ -66,27 +78,17 @@ impl AnnounceActivity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let announce = AnnounceActivity {
|
let announce = AnnounceActivity {
|
||||||
|
actor: community.actor_id(),
|
||||||
to: PublicUrl::Public,
|
to: PublicUrl::Public,
|
||||||
object,
|
object,
|
||||||
cc: vec![community.followers_url()],
|
cc: vec![community.followers_url()],
|
||||||
kind: AnnounceType::Announce,
|
kind: AnnounceType::Announce,
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: generate_activity_id(&AnnounceType::Announce)?,
|
id: generate_activity_id(&AnnounceType::Announce)?,
|
||||||
actor: community.actor_id(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
|
let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
|
||||||
send_activity_new(
|
send_activity_new(context, &announce, &announce.id, community, inboxes, false).await
|
||||||
context,
|
|
||||||
&announce,
|
|
||||||
&announce.common.id,
|
|
||||||
community,
|
|
||||||
inboxes,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +99,8 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_community(&self.common.actor, context, request_counter).await?;
|
verify_community(&self.actor, context, request_counter).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -108,11 +110,11 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
if is_activity_already_known(context.pool(), self.object.common().id_unchecked()).await? {
|
if is_activity_already_known(context.pool(), self.object.id_unchecked()).await? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
insert_activity(
|
insert_activity(
|
||||||
self.object.common().id_unchecked(),
|
self.object.id_unchecked(),
|
||||||
self.object.clone(),
|
self.object.clone(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
@ -121,8 +123,4 @@ impl ActivityHandler for AnnounceActivity {
|
||||||
.await?;
|
.await?;
|
||||||
self.object.receive(context, request_counter).await
|
self.object.receive(context, request_counter).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,87 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_activity, verify_mod_action, verify_person_in_community},
|
activities::{
|
||||||
|
community::announce::AnnouncableActivities,
|
||||||
|
generate_activity_id,
|
||||||
|
verify_activity,
|
||||||
|
verify_mod_action,
|
||||||
|
verify_person_in_community,
|
||||||
|
},
|
||||||
|
activity_queue::send_to_community_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
||||||
|
ActorType,
|
||||||
|
};
|
||||||
|
use activitystreams::{
|
||||||
|
activity::kind::BlockType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::BlockType;
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{Bannable, Followable};
|
use lemmy_db_queries::{Bannable, Followable};
|
||||||
use lemmy_db_schema::source::community::{
|
use lemmy_db_schema::source::{
|
||||||
|
community::{
|
||||||
|
Community,
|
||||||
CommunityFollower,
|
CommunityFollower,
|
||||||
CommunityFollowerForm,
|
CommunityFollowerForm,
|
||||||
CommunityPersonBan,
|
CommunityPersonBan,
|
||||||
CommunityPersonBanForm,
|
CommunityPersonBanForm,
|
||||||
|
},
|
||||||
|
person::Person,
|
||||||
};
|
};
|
||||||
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, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BlockUserFromCommunity {
|
pub struct BlockUserFromCommunity {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
pub(in crate::activities::community) object: Url,
|
pub(in crate::activities::community) object: Url,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: BlockType,
|
kind: BlockType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockUserFromCommunity {
|
||||||
|
pub(in crate::activities::community) fn new(
|
||||||
|
community: &Community,
|
||||||
|
target: &Person,
|
||||||
|
actor: &Person,
|
||||||
|
) -> Result<BlockUserFromCommunity, LemmyError> {
|
||||||
|
Ok(BlockUserFromCommunity {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: target.actor_id(),
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
kind: BlockType::Block,
|
||||||
|
id: generate_activity_id(BlockType::Block)?,
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(
|
||||||
|
community: &Community,
|
||||||
|
target: &Person,
|
||||||
|
actor: &Person,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let block = BlockUserFromCommunity::new(community, target, actor)?;
|
||||||
|
let block_id = block.id.clone();
|
||||||
|
|
||||||
|
let activity = AnnouncableActivities::BlockUserFromCommunity(block);
|
||||||
|
let inboxes = vec![target.get_shared_inbox_or_inbox_url()];
|
||||||
|
send_to_community_new(activity, &block_id, actor, community, inboxes, context).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -35,9 +91,9 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +131,4 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use url::Url;
|
||||||
pub mod add_mod;
|
pub mod add_mod;
|
||||||
pub mod announce;
|
pub mod announce;
|
||||||
pub mod block_user;
|
pub mod block_user;
|
||||||
|
pub mod remove_mod;
|
||||||
pub mod undo_block_user;
|
pub mod undo_block_user;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
|
|
130
crates/apub/src/activities/community/remove_mod.rs
Normal file
130
crates/apub/src/activities/community/remove_mod.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
use crate::{
|
||||||
|
activities::{
|
||||||
|
community::announce::AnnouncableActivities,
|
||||||
|
deletion::{delete::receive_remove_action, verify_delete_activity},
|
||||||
|
generate_activity_id,
|
||||||
|
verify_activity,
|
||||||
|
verify_add_remove_moderator_target,
|
||||||
|
verify_mod_action,
|
||||||
|
verify_person_in_community,
|
||||||
|
},
|
||||||
|
activity_queue::send_to_community_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
|
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
||||||
|
generate_moderators_url,
|
||||||
|
ActorType,
|
||||||
|
};
|
||||||
|
use activitystreams::{
|
||||||
|
activity::kind::RemoveType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
|
};
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
|
use lemmy_db_queries::Joinable;
|
||||||
|
use lemmy_db_schema::source::{
|
||||||
|
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||||
|
person::Person,
|
||||||
|
};
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
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: Url,
|
||||||
|
to: PublicUrl,
|
||||||
|
pub(in crate::activities) object: Url,
|
||||||
|
cc: [Url; 1],
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
kind: RemoveType,
|
||||||
|
// if target is set, this is means remove mod from community
|
||||||
|
pub(in crate::activities) target: Option<Url>,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
unparsed: Unparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RemoveMod {
|
||||||
|
pub async fn send(
|
||||||
|
community: &Community,
|
||||||
|
removed_mod: &Person,
|
||||||
|
actor: &Person,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let id = generate_activity_id(RemoveType::Remove)?;
|
||||||
|
let remove = RemoveMod {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: removed_mod.actor_id(),
|
||||||
|
target: Some(generate_moderators_url(&community.actor_id)?.into()),
|
||||||
|
id: id.clone(),
|
||||||
|
context: lemmy_context(),
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
kind: RemoveType::Remove,
|
||||||
|
unparsed: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let activity = AnnouncableActivities::RemoveMod(remove);
|
||||||
|
let inboxes = vec![removed_mod.get_shared_inbox_or_inbox_url()];
|
||||||
|
send_to_community_new(activity, &id, actor, community, inboxes, context).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl ActivityHandler for RemoveMod {
|
||||||
|
async fn verify(
|
||||||
|
&self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_activity(self)?;
|
||||||
|
if let Some(target) = &self.target {
|
||||||
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
|
verify_add_remove_moderator_target(target, self.cc[0].clone())?;
|
||||||
|
} else {
|
||||||
|
verify_delete_activity(
|
||||||
|
&self.object,
|
||||||
|
self,
|
||||||
|
&self.cc[0],
|
||||||
|
true,
|
||||||
|
context,
|
||||||
|
request_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn receive(
|
||||||
|
self,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
if self.target.is_some() {
|
||||||
|
let community =
|
||||||
|
get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?;
|
||||||
|
let remove_mod =
|
||||||
|
get_or_fetch_and_upsert_person(&self.object, context, request_counter).await?;
|
||||||
|
|
||||||
|
let form = CommunityModeratorForm {
|
||||||
|
community_id: community.id,
|
||||||
|
person_id: remove_mod.id,
|
||||||
|
};
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
CommunityModerator::leave(conn, &form)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
// TODO: send websocket notification about removed mod
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
receive_remove_action(&self.actor, &self.object, None, context, request_counter).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +1,75 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
community::block_user::BlockUserFromCommunity,
|
community::{announce::AnnouncableActivities, block_user::BlockUserFromCommunity},
|
||||||
|
generate_activity_id,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
|
activity_queue::send_to_community_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
||||||
|
ActorType,
|
||||||
|
};
|
||||||
|
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::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::Bannable;
|
use lemmy_db_queries::Bannable;
|
||||||
use lemmy_db_schema::source::community::{CommunityPersonBan, CommunityPersonBanForm};
|
use lemmy_db_schema::source::{
|
||||||
|
community::{Community, CommunityPersonBan, CommunityPersonBanForm},
|
||||||
|
person::Person,
|
||||||
|
};
|
||||||
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, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoBlockUserFromCommunity {
|
pub struct UndoBlockUserFromCommunity {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: BlockUserFromCommunity,
|
object: BlockUserFromCommunity,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: UndoType,
|
kind: UndoType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UndoBlockUserFromCommunity {
|
||||||
|
pub async fn send(
|
||||||
|
community: &Community,
|
||||||
|
target: &Person,
|
||||||
|
actor: &Person,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let block = BlockUserFromCommunity::new(community, target, actor)?;
|
||||||
|
|
||||||
|
let id = generate_activity_id(UndoType::Undo)?;
|
||||||
|
let undo = UndoBlockUserFromCommunity {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: block,
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
kind: UndoType::Undo,
|
||||||
|
id: id.clone(),
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo);
|
||||||
|
let inboxes = vec![target.get_shared_inbox_or_inbox_url()];
|
||||||
|
send_to_community_new(activity, &id, actor, community, inboxes, context).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -35,9 +79,9 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -64,8 +108,4 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,74 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_activity, verify_mod_action, verify_person_in_community},
|
activities::{
|
||||||
objects::community::Group,
|
community::announce::AnnouncableActivities,
|
||||||
|
generate_activity_id,
|
||||||
|
verify_activity,
|
||||||
|
verify_mod_action,
|
||||||
|
verify_person_in_community,
|
||||||
|
},
|
||||||
|
activity_queue::send_to_community_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
|
objects::{community::Group, ToApub},
|
||||||
|
ActorType,
|
||||||
|
};
|
||||||
|
use activitystreams::{
|
||||||
|
activity::kind::UpdateType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UpdateType;
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{ApubObject, Crud};
|
use lemmy_db_queries::{ApubObject, Crud};
|
||||||
use lemmy_db_schema::source::community::{Community, CommunityForm};
|
use lemmy_db_schema::source::{
|
||||||
|
community::{Community, CommunityForm},
|
||||||
|
person::Person,
|
||||||
|
};
|
||||||
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;
|
use url::Url;
|
||||||
|
|
||||||
/// This activity is received from a remote community mod, and updates the description or other
|
/// This activity is received from a remote community mod, and updates the description or other
|
||||||
/// fields of a local community.
|
/// fields of a local community.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdateCommunity {
|
pub struct UpdateCommunity {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
|
// TODO: would be nice to use a separate struct here, which only contains the fields updated here
|
||||||
object: Group,
|
object: Group,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: UpdateType,
|
kind: UpdateType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateCommunity {
|
||||||
|
pub async fn send(
|
||||||
|
community: &Community,
|
||||||
|
actor: &Person,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let id = generate_activity_id(UpdateType::Update)?;
|
||||||
|
let update = UpdateCommunity {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: community.to_apub(context.pool()).await?,
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
kind: UpdateType::Update,
|
||||||
|
id: id.clone(),
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let activity = AnnouncableActivities::UpdateCommunity(Box::new(update));
|
||||||
|
send_to_community_new(activity, &id, actor, community, vec![], context).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -32,9 +78,9 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +122,4 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,15 @@ use crate::{
|
||||||
fetcher::person::get_or_fetch_and_upsert_person,
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::DeleteType;
|
use activitystreams::{
|
||||||
|
activity::kind::DeleteType,
|
||||||
|
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::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
source::{comment::Comment_, community::Community_, post::Post_},
|
source::{comment::Comment_, community::Community_, post::Post_},
|
||||||
Crud,
|
Crud,
|
||||||
|
@ -43,6 +48,7 @@ use lemmy_websocket::{
|
||||||
LemmyContext,
|
LemmyContext,
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// This is very confusing, because there are four distinct cases to handle:
|
/// This is very confusing, because there are four distinct cases to handle:
|
||||||
|
@ -53,19 +59,23 @@ use url::Url;
|
||||||
///
|
///
|
||||||
/// TODO: we should probably change how community deletions work to simplify this. Probably by
|
/// TODO: we should probably change how community deletions work to simplify this. Probably by
|
||||||
/// wrapping it in an announce just like other activities, instead of having the community send it.
|
/// wrapping it in an announce just like other activities, instead of having the community send it.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Delete {
|
pub struct Delete {
|
||||||
pub(in crate::activities::deletion) to: PublicUrl,
|
actor: Url,
|
||||||
|
to: PublicUrl,
|
||||||
pub(in crate::activities::deletion) object: Url,
|
pub(in crate::activities::deletion) object: Url,
|
||||||
pub(in crate::activities::deletion) cc: [Url; 1],
|
pub(in crate::activities::deletion) cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub(in crate::activities::deletion) kind: DeleteType,
|
kind: DeleteType,
|
||||||
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
|
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
|
||||||
/// deleting their own content.
|
/// deleting their own content.
|
||||||
pub(in crate::activities::deletion) summary: Option<String>,
|
pub(in crate::activities::deletion) summary: Option<String>,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub(in crate::activities::deletion) common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -75,11 +85,11 @@ impl ActivityHandler for Delete {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object,
|
&self.object,
|
||||||
|
self,
|
||||||
&self.cc[0],
|
&self.cc[0],
|
||||||
&self.common,
|
|
||||||
self.summary.is_some(),
|
self.summary.is_some(),
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -101,18 +111,11 @@ impl ActivityHandler for Delete {
|
||||||
} else {
|
} else {
|
||||||
Some(reason)
|
Some(reason)
|
||||||
};
|
};
|
||||||
receive_remove_action(
|
receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await
|
||||||
&self.common.actor,
|
|
||||||
&self.object,
|
|
||||||
reason,
|
|
||||||
context,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
} else {
|
||||||
receive_delete_action(
|
receive_delete_action(
|
||||||
&self.object,
|
&self.object,
|
||||||
&self.common.actor,
|
&self.actor,
|
||||||
WebsocketMessages {
|
WebsocketMessages {
|
||||||
community: UserOperationCrud::DeleteCommunity,
|
community: UserOperationCrud::DeleteCommunity,
|
||||||
post: UserOperationCrud::DeletePost,
|
post: UserOperationCrud::DeletePost,
|
||||||
|
@ -125,13 +128,27 @@ impl ActivityHandler for Delete {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Delete {
|
impl Delete {
|
||||||
|
pub(in crate::activities::deletion) fn new(
|
||||||
|
actor: &Person,
|
||||||
|
community: &Community,
|
||||||
|
object_id: Url,
|
||||||
|
summary: Option<String>,
|
||||||
|
) -> Result<Delete, LemmyError> {
|
||||||
|
Ok(Delete {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: object_id,
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
kind: DeleteType::Delete,
|
||||||
|
summary,
|
||||||
|
id: generate_activity_id(DeleteType::Delete)?,
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
pub(in crate::activities::deletion) async fn send(
|
pub(in crate::activities::deletion) async fn send(
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
community: &Community,
|
community: &Community,
|
||||||
|
@ -139,23 +156,11 @@ impl Delete {
|
||||||
summary: Option<String>,
|
summary: Option<String>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let id = generate_activity_id(DeleteType::Delete)?;
|
let delete = Delete::new(actor, community, object_id, summary)?;
|
||||||
let delete = Delete {
|
let delete_id = delete.id.clone();
|
||||||
to: PublicUrl::Public,
|
|
||||||
object: object_id,
|
|
||||||
cc: [community.actor_id()],
|
|
||||||
kind: DeleteType::Delete,
|
|
||||||
summary,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
|
||||||
actor: actor.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let activity = AnnouncableActivities::Delete(delete);
|
let activity = AnnouncableActivities::Delete(delete);
|
||||||
send_to_community_new(activity, &id, actor, community, vec![], context).await
|
send_to_community_new(activity, &delete_id, actor, community, vec![], context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields};
|
use lemmy_apub_lib::{verify_domains_match, ActivityFields};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
source::{comment::Comment_, community::Community_, post::Post_},
|
source::{comment::Comment_, community::Community_, post::Post_},
|
||||||
ApubObject,
|
ApubObject,
|
||||||
|
@ -98,8 +98,8 @@ impl DeletableObjects {
|
||||||
|
|
||||||
pub(in crate::activities) async fn verify_delete_activity(
|
pub(in crate::activities) async fn verify_delete_activity(
|
||||||
object: &Url,
|
object: &Url,
|
||||||
cc: &Url,
|
activity: &dyn ActivityFields,
|
||||||
common: &ActivityCommonFields,
|
community_id: &Url,
|
||||||
is_mod_action: bool,
|
is_mod_action: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
|
@ -110,16 +110,17 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
if c.local {
|
if c.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(&common.actor, cc, context, request_counter).await?;
|
verify_person_in_community(activity.actor(), community_id, 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(&common.actor, c.actor_id(), context).await?;
|
verify_mod_action(activity.actor(), c.actor_id(), context).await?;
|
||||||
}
|
}
|
||||||
DeletableObjects::Post(p) => {
|
DeletableObjects::Post(p) => {
|
||||||
verify_delete_activity_post_or_comment(
|
verify_delete_activity_post_or_comment(
|
||||||
cc,
|
activity,
|
||||||
common,
|
|
||||||
&p.ap_id.into(),
|
&p.ap_id.into(),
|
||||||
|
community_id,
|
||||||
is_mod_action,
|
is_mod_action,
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -128,9 +129,9 @@ pub(in crate::activities) async fn verify_delete_activity(
|
||||||
}
|
}
|
||||||
DeletableObjects::Comment(c) => {
|
DeletableObjects::Comment(c) => {
|
||||||
verify_delete_activity_post_or_comment(
|
verify_delete_activity_post_or_comment(
|
||||||
cc,
|
activity,
|
||||||
common,
|
|
||||||
&c.ap_id.into(),
|
&c.ap_id.into(),
|
||||||
|
community_id,
|
||||||
is_mod_action,
|
is_mod_action,
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -142,19 +143,19 @@ 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(
|
||||||
cc: &Url,
|
activity: &dyn ActivityFields,
|
||||||
common: &ActivityCommonFields,
|
|
||||||
object_id: &Url,
|
object_id: &Url,
|
||||||
|
community_id: &Url,
|
||||||
is_mod_action: bool,
|
is_mod_action: bool,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_person_in_community(&common.actor, cc, context, request_counter).await?;
|
verify_person_in_community(activity.actor(), community_id, context, request_counter).await?;
|
||||||
if is_mod_action {
|
if is_mod_action {
|
||||||
verify_mod_action(&common.actor, cc.clone(), context).await?;
|
verify_mod_action(activity.actor(), community_id.clone(), context).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(&common.actor, object_id)?;
|
verify_domains_match(activity.actor(), object_id)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,15 @@ use crate::{
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::{DeleteType, UndoType};
|
use activitystreams::{
|
||||||
|
activity::kind::UndoType,
|
||||||
|
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::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
|
use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
|
||||||
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
|
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -27,18 +32,23 @@ use lemmy_websocket::{
|
||||||
LemmyContext,
|
LemmyContext,
|
||||||
UserOperationCrud,
|
UserOperationCrud,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDelete {
|
pub struct UndoDelete {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: Delete,
|
object: Delete,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: UndoType,
|
kind: UndoType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -48,12 +58,12 @@ impl ActivityHandler for UndoDelete {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object.object,
|
&self.object.object,
|
||||||
|
self,
|
||||||
&self.cc[0],
|
&self.cc[0],
|
||||||
&self.common,
|
|
||||||
self.object.summary.is_some(),
|
self.object.summary.is_some(),
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -72,7 +82,7 @@ impl ActivityHandler for UndoDelete {
|
||||||
} else {
|
} else {
|
||||||
receive_delete_action(
|
receive_delete_action(
|
||||||
&self.object.object,
|
&self.object.object,
|
||||||
&self.common.actor,
|
&self.actor,
|
||||||
WebsocketMessages {
|
WebsocketMessages {
|
||||||
community: UserOperationCrud::EditCommunity,
|
community: UserOperationCrud::EditCommunity,
|
||||||
post: UserOperationCrud::EditPost,
|
post: UserOperationCrud::EditPost,
|
||||||
|
@ -85,10 +95,6 @@ impl ActivityHandler for UndoDelete {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UndoDelete {
|
impl UndoDelete {
|
||||||
|
@ -99,32 +105,18 @@ impl UndoDelete {
|
||||||
summary: Option<String>,
|
summary: Option<String>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let delete = Delete {
|
let object = Delete::new(actor, community, object_id, summary)?;
|
||||||
to: PublicUrl::Public,
|
|
||||||
object: object_id,
|
|
||||||
cc: [community.actor_id()],
|
|
||||||
kind: DeleteType::Delete,
|
|
||||||
summary,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: generate_activity_id(DeleteType::Delete)?,
|
|
||||||
actor: actor.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
let id = generate_activity_id(UndoType::Undo)?;
|
||||||
let undo = UndoDelete {
|
let undo = UndoDelete {
|
||||||
|
actor: actor.actor_id(),
|
||||||
to: PublicUrl::Public,
|
to: PublicUrl::Public,
|
||||||
object: delete,
|
object,
|
||||||
cc: [community.actor_id()],
|
cc: [community.actor_id()],
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: actor.actor_id(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let activity = AnnouncableActivities::UndoDelete(undo);
|
let activity = AnnouncableActivities::UndoDelete(undo);
|
||||||
|
|
|
@ -10,9 +10,14 @@ use crate::{
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::AcceptType;
|
use activitystreams::{
|
||||||
|
activity::kind::AcceptType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
|
};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{ApubObject, Followable};
|
use lemmy_db_queries::{ApubObject, Followable};
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
community::{Community, CommunityFollower},
|
community::{Community, CommunityFollower},
|
||||||
|
@ -20,17 +25,22 @@ use lemmy_db_schema::source::{
|
||||||
};
|
};
|
||||||
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, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AcceptFollowCommunity {
|
pub struct AcceptFollowCommunity {
|
||||||
|
actor: Url,
|
||||||
to: Url,
|
to: Url,
|
||||||
object: FollowCommunity,
|
object: FollowCommunity,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: AcceptType,
|
kind: AcceptType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AcceptFollowCommunity {
|
impl AcceptFollowCommunity {
|
||||||
|
@ -40,26 +50,23 @@ impl AcceptFollowCommunity {
|
||||||
Community::read_from_apub_id(conn, &community_id.into())
|
Community::read_from_apub_id(conn, &community_id.into())
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let person_id = follow.common.actor.clone();
|
let person_id = follow.actor().clone();
|
||||||
let person = blocking(context.pool(), move |conn| {
|
let person = blocking(context.pool(), move |conn| {
|
||||||
Person::read_from_apub_id(conn, &person_id.into())
|
Person::read_from_apub_id(conn, &person_id.into())
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let id = generate_activity_id(AcceptType::Accept)?;
|
|
||||||
let accept = AcceptFollowCommunity {
|
let accept = AcceptFollowCommunity {
|
||||||
|
actor: community.actor_id(),
|
||||||
to: person.actor_id(),
|
to: person.actor_id(),
|
||||||
object: follow,
|
object: follow,
|
||||||
kind: AcceptType::Accept,
|
kind: AcceptType::Accept,
|
||||||
common: ActivityCommonFields {
|
id: generate_activity_id(AcceptType::Accept)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
id: id.clone(),
|
|
||||||
actor: community.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let inbox = vec![person.inbox_url.into()];
|
let inbox = vec![person.inbox_url.into()];
|
||||||
send_activity_new(context, &accept, &id, &community, inbox, true).await
|
send_activity_new(context, &accept, &accept.id, &community, inbox, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Handle accepted follows
|
/// Handle accepted follows
|
||||||
|
@ -70,10 +77,10 @@ impl ActivityHandler for AcceptFollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_urls_match(&self.to, &self.object.common.actor)?;
|
verify_urls_match(&self.to, self.object.actor())?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.to)?;
|
verify_urls_match(&self.actor, &self.object.to)?;
|
||||||
verify_community(&self.common.actor, context, request_counter).await?;
|
verify_community(&self.actor, context, request_counter).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -83,8 +90,7 @@ impl ActivityHandler for AcceptFollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor =
|
let actor = get_or_fetch_and_upsert_community(&self.actor, context, request_counter).await?;
|
||||||
get_or_fetch_and_upsert_community(&self.common.actor, context, request_counter).await?;
|
|
||||||
let to = get_or_fetch_and_upsert_person(&self.to, context, request_counter).await?;
|
let to = get_or_fetch_and_upsert_person(&self.to, 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| {
|
||||||
|
@ -94,8 +100,4 @@ impl ActivityHandler for AcceptFollowCommunity {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,14 @@ use crate::{
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::FollowType;
|
use activitystreams::{
|
||||||
|
activity::kind::FollowType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
|
};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::Followable;
|
use lemmy_db_queries::Followable;
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
community::{Community, CommunityFollower, CommunityFollowerForm},
|
community::{Community, CommunityFollower, CommunityFollowerForm},
|
||||||
|
@ -20,20 +25,39 @@ use lemmy_db_schema::source::{
|
||||||
};
|
};
|
||||||
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, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct FollowCommunity {
|
pub struct FollowCommunity {
|
||||||
|
actor: Url,
|
||||||
pub(in crate::activities::following) to: Url,
|
pub(in crate::activities::following) to: Url,
|
||||||
pub(in crate::activities::following) object: Url,
|
pub(in crate::activities::following) object: Url,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub(in crate::activities::following) kind: FollowType,
|
kind: FollowType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub(in crate::activities::following) common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FollowCommunity {
|
impl FollowCommunity {
|
||||||
|
pub(in crate::activities::following) fn new(
|
||||||
|
actor: &Person,
|
||||||
|
community: &Community,
|
||||||
|
) -> Result<FollowCommunity, LemmyError> {
|
||||||
|
Ok(FollowCommunity {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: community.actor_id(),
|
||||||
|
object: community.actor_id(),
|
||||||
|
kind: FollowType::Follow,
|
||||||
|
id: generate_activity_id(FollowType::Follow)?,
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
community: &Community,
|
community: &Community,
|
||||||
|
@ -49,20 +73,9 @@ impl FollowCommunity {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let id = generate_activity_id(FollowType::Follow)?;
|
let follow = FollowCommunity::new(actor, community)?;
|
||||||
let follow = FollowCommunity {
|
|
||||||
to: community.actor_id(),
|
|
||||||
object: community.actor_id(),
|
|
||||||
kind: FollowType::Follow,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
|
||||||
actor: actor.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let inbox = vec![community.inbox_url.clone().into()];
|
let inbox = vec![community.inbox_url.clone().into()];
|
||||||
send_activity_new(context, &follow, &id, actor, inbox, true).await
|
send_activity_new(context, &follow, &follow.id, actor, inbox, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,9 +86,9 @@ impl ActivityHandler for FollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_urls_match(&self.to, &self.object)?;
|
verify_urls_match(&self.to, &self.object)?;
|
||||||
verify_person(&self.common.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,8 +97,7 @@ impl ActivityHandler for FollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor =
|
let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
|
||||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
|
||||||
let community =
|
let community =
|
||||||
get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?;
|
get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?;
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
|
@ -102,8 +114,4 @@ impl ActivityHandler for FollowCommunity {
|
||||||
|
|
||||||
AcceptFollowCommunity::send(self, context).await
|
AcceptFollowCommunity::send(self, context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,14 @@ use crate::{
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::{FollowType, UndoType};
|
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::{verify_urls_match, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::Followable;
|
use lemmy_db_queries::Followable;
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
community::{Community, CommunityFollower, CommunityFollowerForm},
|
community::{Community, CommunityFollower, CommunityFollowerForm},
|
||||||
|
@ -20,17 +25,22 @@ use lemmy_db_schema::source::{
|
||||||
};
|
};
|
||||||
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, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoFollowCommunity {
|
pub struct UndoFollowCommunity {
|
||||||
|
actor: Url,
|
||||||
to: Url,
|
to: Url,
|
||||||
object: FollowCommunity,
|
object: FollowCommunity,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: UndoType,
|
kind: UndoType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UndoFollowCommunity {
|
impl UndoFollowCommunity {
|
||||||
|
@ -39,30 +49,18 @@ impl UndoFollowCommunity {
|
||||||
community: &Community,
|
community: &Community,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
let object = FollowCommunity::new(actor, community)?;
|
||||||
let undo = UndoFollowCommunity {
|
let undo = UndoFollowCommunity {
|
||||||
to: community.actor_id(),
|
|
||||||
object: FollowCommunity {
|
|
||||||
to: community.actor_id(),
|
|
||||||
object: community.actor_id(),
|
|
||||||
kind: FollowType::Follow,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: generate_activity_id(FollowType::Follow)?,
|
|
||||||
actor: actor.actor_id(),
|
actor: actor.actor_id(),
|
||||||
unparsed: Default::default(),
|
to: community.actor_id(),
|
||||||
},
|
object,
|
||||||
},
|
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
common: ActivityCommonFields {
|
id: generate_activity_id(UndoType::Undo)?,
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
id: id.clone(),
|
|
||||||
actor: actor.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let inbox = vec![community.get_shared_inbox_or_inbox_url()];
|
let inbox = vec![community.get_shared_inbox_or_inbox_url()];
|
||||||
send_activity_new(context, &undo, &id, actor, inbox, true).await
|
send_activity_new(context, &undo, &undo.id, actor, inbox, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +71,10 @@ impl ActivityHandler for UndoFollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_urls_match(&self.to, &self.object.object)?;
|
verify_urls_match(&self.to, &self.object.object)?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.common.actor)?;
|
verify_urls_match(&self.actor, self.object.actor())?;
|
||||||
verify_person(&self.common.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?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -86,8 +84,7 @@ impl ActivityHandler for UndoFollowCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor =
|
let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
|
||||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
|
||||||
let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?;
|
let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?;
|
||||||
|
|
||||||
let community_follower_form = CommunityFollowerForm {
|
let community_follower_form = CommunityFollowerForm {
|
||||||
|
@ -103,8 +100,4 @@ impl ActivityHandler for UndoFollowCommunity {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields};
|
use lemmy_apub_lib::{verify_domains_match, ActivityFields};
|
||||||
use lemmy_db_queries::ApubObject;
|
use lemmy_db_queries::ApubObject;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{community::Community, person::Person},
|
source::{community::Community, person::Person},
|
||||||
|
@ -26,8 +26,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 removal;
|
|
||||||
pub mod send;
|
pub mod send;
|
||||||
|
pub mod undo_remove;
|
||||||
pub mod voting;
|
pub mod voting;
|
||||||
|
|
||||||
#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
|
#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
|
||||||
|
@ -90,9 +90,9 @@ async fn verify_community(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> {
|
fn verify_activity(activity: &dyn ActivityFields) -> Result<(), LemmyError> {
|
||||||
check_is_apub_id_valid(&common.actor, false)?;
|
check_is_apub_id_valid(activity.actor(), false)?;
|
||||||
verify_domains_match(common.id_unchecked(), &common.actor)?;
|
verify_domains_match(activity.id_unchecked(), activity.actor())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,31 +14,37 @@ use crate::{
|
||||||
objects::{post::Page, FromApub, ToApub},
|
objects::{post::Page, FromApub, ToApub},
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
|
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::{
|
||||||
values::PublicUrl,
|
values::PublicUrl,
|
||||||
verify_domains_match,
|
verify_domains_match,
|
||||||
verify_urls_match,
|
verify_urls_match,
|
||||||
ActivityCommonFields,
|
ActivityFields,
|
||||||
ActivityHandler,
|
ActivityHandler,
|
||||||
};
|
};
|
||||||
use lemmy_db_queries::Crud;
|
use lemmy_db_queries::Crud;
|
||||||
use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
|
use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
|
||||||
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;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateOrUpdatePost {
|
pub struct CreateOrUpdatePost {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: Page,
|
object: Page,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateOrUpdatePost {
|
impl CreateOrUpdatePost {
|
||||||
|
@ -56,16 +62,14 @@ impl CreateOrUpdatePost {
|
||||||
|
|
||||||
let id = generate_activity_id(kind.clone())?;
|
let id = generate_activity_id(kind.clone())?;
|
||||||
let create_or_update = CreateOrUpdatePost {
|
let create_or_update = CreateOrUpdatePost {
|
||||||
|
actor: actor.actor_id(),
|
||||||
to: PublicUrl::Public,
|
to: PublicUrl::Public,
|
||||||
object: post.to_apub(context.pool()).await?,
|
object: post.to_apub(context.pool()).await?,
|
||||||
cc: [community.actor_id()],
|
cc: [community.actor_id()],
|
||||||
kind,
|
kind,
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: actor.actor_id(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
|
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
|
||||||
|
@ -80,14 +84,14 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
let community = extract_community(&self.cc, context, request_counter).await?;
|
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||||
let community_id = community.actor_id();
|
let community_id = community.actor_id();
|
||||||
verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?;
|
verify_person_in_community(&self.actor, &community_id, context, request_counter).await?;
|
||||||
match self.kind {
|
match self.kind {
|
||||||
CreateOrUpdateType::Create => {
|
CreateOrUpdateType::Create => {
|
||||||
verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
|
verify_domains_match(&self.actor, self.object.id_unchecked())?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
|
verify_urls_match(&self.actor, &self.object.attributed_to)?;
|
||||||
// Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
|
// Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
|
||||||
// However, when fetching a remote post we generate a new create activity with the current
|
// However, when fetching a remote post we generate a new create activity with the current
|
||||||
// locked/stickied value, so this check may fail. So only check if its a local community,
|
// locked/stickied value, so this check may fail. So only check if its a local community,
|
||||||
|
@ -101,10 +105,10 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
CreateOrUpdateType::Update => {
|
CreateOrUpdateType::Update => {
|
||||||
let is_mod_action = self.object.is_mod_action(context.pool()).await?;
|
let is_mod_action = self.object.is_mod_action(context.pool()).await?;
|
||||||
if is_mod_action {
|
if is_mod_action {
|
||||||
verify_mod_action(&self.common.actor, community_id, context).await?;
|
verify_mod_action(&self.actor, community_id, context).await?;
|
||||||
} else {
|
} else {
|
||||||
verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
|
verify_domains_match(&self.actor, self.object.id_unchecked())?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
|
verify_urls_match(&self.actor, &self.object.attributed_to)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,8 +121,7 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor =
|
let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
|
||||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
|
||||||
let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
|
let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
|
||||||
|
|
||||||
let notif_type = match self.kind {
|
let notif_type = match self.kind {
|
||||||
|
@ -128,8 +131,4 @@ impl ActivityHandler for CreateOrUpdatePost {
|
||||||
send_post_ws_message(post.id, notif_type, None, None, context).await?;
|
send_post_ws_message(post.id, notif_type, None, None, context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,23 +5,30 @@ use crate::{
|
||||||
objects::{private_message::Note, FromApub, ToApub},
|
objects::{private_message::Note, FromApub, ToApub},
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
|
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::Crud;
|
use lemmy_db_queries::Crud;
|
||||||
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
|
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
|
||||||
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;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreateOrUpdatePrivateMessage {
|
pub struct CreateOrUpdatePrivateMessage {
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
pub context: OneOrMany<AnyBase>,
|
||||||
|
id: Url,
|
||||||
|
actor: Url,
|
||||||
to: Url,
|
to: Url,
|
||||||
|
cc: [Url; 0],
|
||||||
object: Note,
|
object: Note,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: CreateOrUpdateType,
|
kind: CreateOrUpdateType,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
pub unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateOrUpdatePrivateMessage {
|
impl CreateOrUpdatePrivateMessage {
|
||||||
|
@ -37,15 +44,14 @@ impl CreateOrUpdatePrivateMessage {
|
||||||
|
|
||||||
let id = generate_activity_id(kind.clone())?;
|
let id = generate_activity_id(kind.clone())?;
|
||||||
let create_or_update = CreateOrUpdatePrivateMessage {
|
let create_or_update = CreateOrUpdatePrivateMessage {
|
||||||
to: recipient.actor_id(),
|
|
||||||
object: private_message.to_apub(context.pool()).await?,
|
|
||||||
kind,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
context: lemmy_context(),
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: actor.actor_id(),
|
actor: actor.actor_id(),
|
||||||
|
to: recipient.actor_id(),
|
||||||
|
cc: [],
|
||||||
|
object: private_message.to_apub(context.pool()).await?,
|
||||||
|
kind,
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
|
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
|
||||||
send_activity_new(context, &create_or_update, &id, actor, inbox, true).await
|
send_activity_new(context, &create_or_update, &id, actor, inbox, true).await
|
||||||
|
@ -58,9 +64,9 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person(&self.common.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
|
verify_domains_match(&self.actor, self.object.id_unchecked())?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -71,7 +77,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let private_message =
|
let private_message =
|
||||||
PrivateMessage::from_apub(&self.object, context, &self.common.actor, request_counter).await?;
|
PrivateMessage::from_apub(&self.object, context, &self.actor, request_counter).await?;
|
||||||
|
|
||||||
let notif_type = match self.kind {
|
let notif_type = match self.kind {
|
||||||
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
|
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
|
||||||
|
@ -81,8 +87,4 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,50 +4,64 @@ use crate::{
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::DeleteType;
|
use activitystreams::{
|
||||||
|
activity::kind::DeleteType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
|
};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
|
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
|
||||||
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
|
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
|
||||||
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;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DeletePrivateMessage {
|
pub struct DeletePrivateMessage {
|
||||||
pub(in crate::activities::private_message) to: Url,
|
actor: Url,
|
||||||
|
to: Url,
|
||||||
pub(in crate::activities::private_message) object: Url,
|
pub(in crate::activities::private_message) object: Url,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub(in crate::activities::private_message) kind: DeleteType,
|
kind: DeleteType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub(in crate::activities::private_message) common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeletePrivateMessage {
|
impl DeletePrivateMessage {
|
||||||
|
pub(in crate::activities::private_message) fn new(
|
||||||
|
actor: &Person,
|
||||||
|
pm: &PrivateMessage,
|
||||||
|
) -> Result<DeletePrivateMessage, LemmyError> {
|
||||||
|
Ok(DeletePrivateMessage {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: actor.actor_id(),
|
||||||
|
object: pm.ap_id.clone().into(),
|
||||||
|
kind: DeleteType::Delete,
|
||||||
|
id: generate_activity_id(DeleteType::Delete)?,
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
pm: &PrivateMessage,
|
pm: &PrivateMessage,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
let delete = DeletePrivateMessage::new(actor, pm)?;
|
||||||
|
let delete_id = delete.id.clone();
|
||||||
|
|
||||||
let recipient_id = pm.recipient_id;
|
let recipient_id = pm.recipient_id;
|
||||||
let recipient =
|
let recipient =
|
||||||
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
||||||
|
|
||||||
let id = generate_activity_id(DeleteType::Delete)?;
|
|
||||||
let delete = DeletePrivateMessage {
|
|
||||||
to: actor.actor_id(),
|
|
||||||
object: pm.ap_id.clone().into(),
|
|
||||||
kind: DeleteType::Delete,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
|
||||||
actor: actor.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
|
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
|
||||||
send_activity_new(context, &delete, &id, actor, inbox, true).await
|
send_activity_new(context, &delete, &delete_id, actor, inbox, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,9 +72,9 @@ impl ActivityHandler for DeletePrivateMessage {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person(&self.common.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_domains_match(&self.common.actor, &self.object)?;
|
verify_domains_match(&self.actor, &self.object)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +103,4 @@ impl ActivityHandler for DeletePrivateMessage {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,29 +9,34 @@ use crate::{
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::{DeleteType, UndoType};
|
use activitystreams::{
|
||||||
use lemmy_api_common::blocking;
|
activity::kind::UndoType,
|
||||||
use lemmy_apub_lib::{
|
base::AnyBase,
|
||||||
verify_domains_match,
|
primitives::OneOrMany,
|
||||||
verify_urls_match,
|
unparsed::Unparsed,
|
||||||
ActivityCommonFields,
|
|
||||||
ActivityHandler,
|
|
||||||
};
|
};
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::{verify_domains_match, verify_urls_match, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
|
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
|
||||||
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
|
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
|
||||||
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;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoDeletePrivateMessage {
|
pub struct UndoDeletePrivateMessage {
|
||||||
|
actor: Url,
|
||||||
to: Url,
|
to: Url,
|
||||||
object: DeletePrivateMessage,
|
object: DeletePrivateMessage,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: UndoType,
|
kind: UndoType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UndoDeletePrivateMessage {
|
impl UndoDeletePrivateMessage {
|
||||||
|
@ -44,29 +49,16 @@ impl UndoDeletePrivateMessage {
|
||||||
let recipient =
|
let recipient =
|
||||||
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
|
||||||
|
|
||||||
let object = DeletePrivateMessage {
|
let object = DeletePrivateMessage::new(actor, pm)?;
|
||||||
to: recipient.actor_id(),
|
|
||||||
object: pm.ap_id.clone().into(),
|
|
||||||
kind: DeleteType::Delete,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: generate_activity_id(DeleteType::Delete)?,
|
|
||||||
actor: actor.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
let id = generate_activity_id(UndoType::Undo)?;
|
||||||
let undo = UndoDeletePrivateMessage {
|
let undo = UndoDeletePrivateMessage {
|
||||||
|
actor: actor.actor_id(),
|
||||||
to: recipient.actor_id(),
|
to: recipient.actor_id(),
|
||||||
object,
|
object,
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: actor.actor_id(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
|
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
|
||||||
send_activity_new(context, &undo, &id, actor, inbox, true).await
|
send_activity_new(context, &undo, &id, actor, inbox, true).await
|
||||||
|
@ -80,10 +72,10 @@ impl ActivityHandler for UndoDeletePrivateMessage {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person(&self.common.actor, context, request_counter).await?;
|
verify_person(&self.actor, context, request_counter).await?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.common.actor)?;
|
verify_urls_match(&self.actor, self.object.actor())?;
|
||||||
verify_domains_match(&self.common.actor, &self.object.object)?;
|
verify_domains_match(&self.actor, &self.object.object)?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -114,8 +106,4 @@ impl ActivityHandler for UndoDeletePrivateMessage {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
pub mod remove;
|
|
||||||
pub mod undo_remove;
|
|
|
@ -1,101 +0,0 @@
|
||||||
use crate::{
|
|
||||||
activities::{
|
|
||||||
deletion::{delete::receive_remove_action, verify_delete_activity},
|
|
||||||
verify_activity,
|
|
||||||
verify_add_remove_moderator_target,
|
|
||||||
verify_mod_action,
|
|
||||||
verify_person_in_community,
|
|
||||||
},
|
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
|
||||||
CommunityType,
|
|
||||||
};
|
|
||||||
use activitystreams::{activity::kind::RemoveType, base::AnyBase};
|
|
||||||
use lemmy_api_common::blocking;
|
|
||||||
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
|
||||||
use lemmy_db_queries::Joinable;
|
|
||||||
use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct RemoveMod {
|
|
||||||
to: PublicUrl,
|
|
||||||
pub(in crate::activities::removal) object: Url,
|
|
||||||
cc: [Url; 1],
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
kind: RemoveType,
|
|
||||||
// if target is set, this is means remove mod from community
|
|
||||||
pub(in crate::activities::removal) target: Option<Url>,
|
|
||||||
#[serde(flatten)]
|
|
||||||
common: ActivityCommonFields,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
impl ActivityHandler for RemoveMod {
|
|
||||||
async fn verify(
|
|
||||||
&self,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
verify_activity(self.common())?;
|
|
||||||
if let Some(target) = &self.target {
|
|
||||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
|
||||||
verify_add_remove_moderator_target(target, self.cc[0].clone())?;
|
|
||||||
} else {
|
|
||||||
verify_delete_activity(
|
|
||||||
&self.object,
|
|
||||||
&self.cc[0],
|
|
||||||
self.common(),
|
|
||||||
true,
|
|
||||||
context,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn receive(
|
|
||||||
self,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
if self.target.is_some() {
|
|
||||||
let community =
|
|
||||||
get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?;
|
|
||||||
let remove_mod =
|
|
||||||
get_or_fetch_and_upsert_person(&self.object, context, request_counter).await?;
|
|
||||||
|
|
||||||
let form = CommunityModeratorForm {
|
|
||||||
community_id: community.id,
|
|
||||||
person_id: remove_mod.id,
|
|
||||||
};
|
|
||||||
blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModerator::leave(conn, &form)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
|
|
||||||
community
|
|
||||||
.send_announce(anybase, Some(self.object.clone()), context)
|
|
||||||
.await?;
|
|
||||||
// TODO: send websocket notification about removed mod
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
receive_remove_action(
|
|
||||||
&self.common.actor,
|
|
||||||
&self.object,
|
|
||||||
None,
|
|
||||||
context,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +1,10 @@
|
||||||
use crate::{
|
use crate::{check_is_apub_id_valid, ActorType, CommunityType};
|
||||||
activities::generate_activity_id,
|
|
||||||
activity_queue::{send_to_community, send_to_community_followers},
|
|
||||||
check_is_apub_id_valid,
|
|
||||||
extensions::context::lemmy_context,
|
|
||||||
fetcher::get_or_fetch_and_upsert_actor,
|
|
||||||
generate_moderators_url,
|
|
||||||
insert_activity,
|
|
||||||
objects::ToApub,
|
|
||||||
ActorType,
|
|
||||||
CommunityType,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
|
||||||
activity::{
|
|
||||||
kind::{AddType, AnnounceType, BlockType, RemoveType, UndoType, UpdateType},
|
|
||||||
Add,
|
|
||||||
Announce,
|
|
||||||
Block,
|
|
||||||
OptTargetRefExt,
|
|
||||||
Remove,
|
|
||||||
Undo,
|
|
||||||
Update,
|
|
||||||
},
|
|
||||||
base::{AnyBase, BaseExt, ExtendsExt},
|
|
||||||
object::ObjectExt,
|
|
||||||
public,
|
|
||||||
};
|
|
||||||
use anyhow::Context;
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_db_queries::DbPool;
|
use lemmy_db_queries::DbPool;
|
||||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
||||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
impl ActorType for Community {
|
impl ActorType for Community {
|
||||||
|
@ -67,71 +39,6 @@ impl CommunityType for Community {
|
||||||
self.followers_url.clone().into()
|
self.followers_url.clone().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If a remote community is updated by a local mod, send the updated info to the community's
|
|
||||||
/// instance.
|
|
||||||
async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
|
||||||
if self.local {
|
|
||||||
// Do nothing, other instances will automatically refetch the community
|
|
||||||
} else {
|
|
||||||
let mut update = Update::new(
|
|
||||||
mod_.actor_id(),
|
|
||||||
AnyBase::from_arbitrary_json(self.to_apub(context.pool()).await?)?,
|
|
||||||
);
|
|
||||||
update
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_activity_id(UpdateType::Update)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
|
||||||
send_to_community(update, &mod_, self, None, context).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps an activity sent to the community in an announce, and then sends the announce to all
|
|
||||||
/// community followers.
|
|
||||||
///
|
|
||||||
/// If we are announcing a local activity, it hasn't been stored in the database yet, and we need
|
|
||||||
/// to do it here, so that it can be fetched by ID. Remote activities are inserted into DB in the
|
|
||||||
/// inbox.
|
|
||||||
///
|
|
||||||
/// If the `object` of the announced activity is an actor, the actor ID needs to be passed as
|
|
||||||
/// `object_actor`, so that the announce can be delivered to that user.
|
|
||||||
async fn send_announce(
|
|
||||||
&self,
|
|
||||||
activity: AnyBase,
|
|
||||||
object_actor: Option<Url>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let inner_id = activity.id().context(location_info!())?;
|
|
||||||
if inner_id.domain() == Some(&Settings::get().get_hostname_without_port()?) {
|
|
||||||
insert_activity(inner_id, activity.clone(), true, false, context.pool()).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ccs = vec![self.followers_url()];
|
|
||||||
let mut object_actor_inbox: Option<Url> = None;
|
|
||||||
if let Some(actor_id) = object_actor {
|
|
||||||
// Ignore errors, maybe its not actually an actor
|
|
||||||
// TODO: should pass the actual request counter in, but that seems complicated
|
|
||||||
let actor = get_or_fetch_and_upsert_actor(&actor_id, context, &mut 0)
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
if let Some(actor) = actor {
|
|
||||||
ccs.push(actor_id);
|
|
||||||
object_actor_inbox = Some(actor.get_shared_inbox_or_inbox_url());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut announce = Announce::new(self.actor_id(), activity);
|
|
||||||
announce
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_activity_id(AnnounceType::Announce)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(ccs);
|
|
||||||
|
|
||||||
send_to_community_followers(announce, self, object_actor_inbox, context).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
|
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
|
@ -152,82 +59,4 @@ impl CommunityType for Community {
|
||||||
|
|
||||||
Ok(inboxes)
|
Ok(inboxes)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_add_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
added_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
|
|
||||||
add
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_activity_id(AddType::Add)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()])
|
|
||||||
.set_target(generate_moderators_url(&self.actor_id)?.into_inner());
|
|
||||||
|
|
||||||
send_to_community(add, actor, self, Some(added_mod.actor_id()), context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_remove_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
removed_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
|
|
||||||
remove
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()])
|
|
||||||
.set_target(generate_moderators_url(&self.actor_id)?.into_inner());
|
|
||||||
|
|
||||||
send_to_community(remove, actor, self, Some(removed_mod.actor_id()), context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_block_user(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
blocked_user: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
|
|
||||||
block
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_activity_id(BlockType::Block)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
|
||||||
|
|
||||||
send_to_community(block, actor, self, Some(blocked_user.actor_id()), context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_undo_block_user(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
unblocked_user: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let mut block = Block::new(actor.actor_id(), unblocked_user.actor_id());
|
|
||||||
block
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_activity_id(BlockType::Block)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
|
||||||
|
|
||||||
// Undo that fake activity
|
|
||||||
let mut undo = Undo::new(actor.actor_id(), block.into_any_base()?);
|
|
||||||
undo
|
|
||||||
.set_many_contexts(lemmy_context())
|
|
||||||
.set_id(generate_activity_id(UndoType::Undo)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
|
||||||
|
|
||||||
send_to_community(undo, actor, self, Some(unblocked_user.actor_id()), context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,35 @@
|
||||||
use crate::activities::{
|
use crate::activities::{
|
||||||
|
community::remove_mod::RemoveMod,
|
||||||
deletion::{undo_delete::UndoDelete, verify_delete_activity},
|
deletion::{undo_delete::UndoDelete, verify_delete_activity},
|
||||||
removal::remove::RemoveMod,
|
|
||||||
verify_activity,
|
verify_activity,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::{
|
||||||
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
activity::kind::UndoType,
|
||||||
|
base::AnyBase,
|
||||||
|
primitives::OneOrMany,
|
||||||
|
unparsed::Unparsed,
|
||||||
|
};
|
||||||
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
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, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoRemovePostCommentOrCommunity {
|
pub struct UndoRemovePostCommentOrCommunity {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
// Note, there is no such thing as Undo/Remove/Mod, so we ignore that
|
// Note, there is no such thing as Undo/Remove/Mod, so we ignore that
|
||||||
object: RemoveMod,
|
object: RemoveMod,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: UndoType,
|
kind: UndoType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -29,13 +39,13 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
|
||||||
verify_delete_activity(
|
verify_delete_activity(
|
||||||
&self.object.object,
|
&self.object.object,
|
||||||
|
self,
|
||||||
&self.cc[0],
|
&self.cc[0],
|
||||||
self.common(),
|
|
||||||
true,
|
true,
|
||||||
context,
|
context,
|
||||||
request_counter,
|
request_counter,
|
||||||
|
@ -51,8 +61,4 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
UndoDelete::receive_undo_remove_action(&self.object.object, context).await
|
UndoDelete::receive_undo_remove_action(&self.object.object, context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -19,9 +19,14 @@ use crate::{
|
||||||
ActorType,
|
ActorType,
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
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::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::Crud;
|
use lemmy_db_queries::Crud;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{community::Community, person::Person},
|
source::{community::Community, person::Person},
|
||||||
|
@ -29,19 +34,24 @@ 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 std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UndoVote {
|
pub struct UndoVote {
|
||||||
|
actor: Url,
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: Vote,
|
object: Vote,
|
||||||
cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: UndoType,
|
kind: UndoType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UndoVote {
|
impl UndoVote {
|
||||||
|
@ -56,30 +66,18 @@ impl UndoVote {
|
||||||
Community::read(conn, community_id)
|
Community::read(conn, community_id)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let id = generate_activity_id(UndoType::Undo)?;
|
|
||||||
|
|
||||||
|
let object = Vote::new(object, actor, &community, kind.clone())?;
|
||||||
|
let id = generate_activity_id(UndoType::Undo)?;
|
||||||
let undo_vote = UndoVote {
|
let undo_vote = UndoVote {
|
||||||
to: PublicUrl::Public,
|
|
||||||
object: Vote {
|
|
||||||
to: PublicUrl::Public,
|
|
||||||
object: object.ap_id(),
|
|
||||||
cc: [community.actor_id()],
|
|
||||||
kind: kind.clone(),
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: generate_activity_id(kind)?,
|
|
||||||
actor: actor.actor_id(),
|
actor: actor.actor_id(),
|
||||||
unparsed: Default::default(),
|
to: PublicUrl::Public,
|
||||||
},
|
object,
|
||||||
},
|
|
||||||
cc: [community.actor_id()],
|
cc: [community.actor_id()],
|
||||||
kind: UndoType::Undo,
|
kind: UndoType::Undo,
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
actor: actor.actor_id(),
|
context: lemmy_context(),
|
||||||
unparsed: Default::default(),
|
unparsed: Default::default(),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
||||||
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
||||||
|
@ -93,9 +91,9 @@ impl ActivityHandler for UndoVote {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.common().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(())
|
||||||
}
|
}
|
||||||
|
@ -105,8 +103,7 @@ impl ActivityHandler for UndoVote {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor =
|
let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
|
||||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
|
||||||
let object =
|
let object =
|
||||||
get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter)
|
get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -115,8 +112,4 @@ impl ActivityHandler for UndoVote {
|
||||||
PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await,
|
PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,10 @@ use crate::{
|
||||||
ActorType,
|
ActorType,
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
|
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::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::Crud;
|
use lemmy_db_queries::Crud;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{community::Community, person::Person},
|
source::{community::Community, person::Person},
|
||||||
|
@ -57,19 +58,41 @@ impl From<&VoteType> for i16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Vote {
|
pub struct Vote {
|
||||||
pub(in crate::activities::voting) to: PublicUrl,
|
actor: Url,
|
||||||
|
to: PublicUrl,
|
||||||
pub(in crate::activities::voting) object: Url,
|
pub(in crate::activities::voting) object: Url,
|
||||||
pub(in crate::activities::voting) cc: [Url; 1],
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub(in crate::activities::voting) kind: VoteType,
|
pub(in crate::activities::voting) kind: VoteType,
|
||||||
|
id: Url,
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub(in crate::activities::voting) common: ActivityCommonFields,
|
unparsed: Unparsed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vote {
|
impl Vote {
|
||||||
|
pub(in crate::activities::voting) fn new(
|
||||||
|
object: &PostOrComment,
|
||||||
|
actor: &Person,
|
||||||
|
community: &Community,
|
||||||
|
kind: VoteType,
|
||||||
|
) -> Result<Vote, LemmyError> {
|
||||||
|
Ok(Vote {
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: object.ap_id(),
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
kind: kind.clone(),
|
||||||
|
id: generate_activity_id(kind)?,
|
||||||
|
context: lemmy_context(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
object: &PostOrComment,
|
object: &PostOrComment,
|
||||||
actor: &Person,
|
actor: &Person,
|
||||||
|
@ -81,22 +104,11 @@ impl Vote {
|
||||||
Community::read(conn, community_id)
|
Community::read(conn, community_id)
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
let id = generate_activity_id(kind.clone())?;
|
let vote = Vote::new(object, actor, &community, kind)?;
|
||||||
|
let vote_id = vote.id.clone();
|
||||||
|
|
||||||
let vote = Vote {
|
|
||||||
to: PublicUrl::Public,
|
|
||||||
object: object.ap_id(),
|
|
||||||
cc: [community.actor_id()],
|
|
||||||
kind,
|
|
||||||
common: ActivityCommonFields {
|
|
||||||
context: lemmy_context(),
|
|
||||||
id: id.clone(),
|
|
||||||
actor: actor.actor_id(),
|
|
||||||
unparsed: Default::default(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let activity = AnnouncableActivities::Vote(vote);
|
let activity = AnnouncableActivities::Vote(vote);
|
||||||
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
send_to_community_new(activity, &vote_id, actor, &community, vec![], context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +119,8 @@ impl ActivityHandler for Vote {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self)?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +129,7 @@ impl ActivityHandler for Vote {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let actor =
|
let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
|
||||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
|
||||||
let object =
|
let object =
|
||||||
get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?;
|
get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?;
|
||||||
match object {
|
match object {
|
||||||
|
@ -126,8 +137,4 @@ impl ActivityHandler for Vote {
|
||||||
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await,
|
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
&self.common
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::community::announce::{AnnouncableActivities, AnnounceActivity},
|
activities::community::announce::{AnnouncableActivities, AnnounceActivity},
|
||||||
check_is_apub_id_valid,
|
|
||||||
extensions::signatures::sign_and_send,
|
extensions::signatures::sign_and_send,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
ActorType,
|
ActorType,
|
||||||
CommunityType,
|
|
||||||
APUB_JSON_CONTENT_TYPE,
|
APUB_JSON_CONTENT_TYPE,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
|
||||||
base::{BaseExt, Extends, ExtendsExt},
|
|
||||||
object::AsObject,
|
|
||||||
};
|
|
||||||
use anyhow::{anyhow, Context, Error};
|
use anyhow::{anyhow, Context, Error};
|
||||||
use background_jobs::{
|
use background_jobs::{
|
||||||
create_server,
|
create_server,
|
||||||
|
@ -21,95 +15,15 @@ use background_jobs::{
|
||||||
QueueHandle,
|
QueueHandle,
|
||||||
WorkerConfig,
|
WorkerConfig,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
|
||||||
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::{debug, info, warn};
|
use log::{info, warn};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin};
|
use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// From a local community, send activity to all remote followers.
|
|
||||||
///
|
|
||||||
/// * `activity` the apub activity to send
|
|
||||||
/// * `community` the sending community
|
|
||||||
/// * `extra_inbox` actor inbox which should receive the activity, in addition to followers
|
|
||||||
pub(crate) async fn send_to_community_followers<T, Kind>(
|
|
||||||
activity: T,
|
|
||||||
community: &Community,
|
|
||||||
extra_inbox: Option<Url>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: AsObject<Kind> + Extends<Kind> + Debug + BaseExt<Kind>,
|
|
||||||
Kind: Serialize,
|
|
||||||
<T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let extra_inbox: Vec<Url> = extra_inbox.into_iter().collect();
|
|
||||||
let follower_inboxes: Vec<Url> = vec![
|
|
||||||
community.get_follower_inboxes(context.pool()).await?,
|
|
||||||
extra_inbox,
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.flatten()
|
|
||||||
.unique()
|
|
||||||
.filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname))
|
|
||||||
.filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok())
|
|
||||||
.map(|inbox| inbox.to_owned())
|
|
||||||
.collect();
|
|
||||||
debug!(
|
|
||||||
"Sending activity {:?} to followers of {}",
|
|
||||||
&activity.id_unchecked().map(ToString::to_string),
|
|
||||||
&community.actor_id
|
|
||||||
);
|
|
||||||
|
|
||||||
send_activity_internal(context, activity, community, follower_inboxes, true, false).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends an activity from a local person to a remote community.
|
|
||||||
///
|
|
||||||
/// * `activity` the activity to send
|
|
||||||
/// * `creator` the creator of the activity
|
|
||||||
/// * `community` the destination community
|
|
||||||
/// * `object_actor` if the object of the activity is an actor, it should be passed here so it can
|
|
||||||
/// be sent directly to the actor
|
|
||||||
///
|
|
||||||
pub(crate) async fn send_to_community<T, Kind>(
|
|
||||||
activity: T,
|
|
||||||
creator: &Person,
|
|
||||||
community: &Community,
|
|
||||||
object_actor: Option<Url>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: AsObject<Kind> + Extends<Kind> + Debug + BaseExt<Kind>,
|
|
||||||
Kind: Serialize,
|
|
||||||
<T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
// if this is a local community, we need to do an announce from the community instead
|
|
||||||
if community.local {
|
|
||||||
community
|
|
||||||
.send_announce(activity.into_any_base()?, object_actor, context)
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
let inbox = community.get_shared_inbox_or_inbox_url();
|
|
||||||
check_is_apub_id_valid(&inbox, false)?;
|
|
||||||
debug!(
|
|
||||||
"Sending activity {:?} to community {}",
|
|
||||||
&activity.id_unchecked().map(ToString::to_string),
|
|
||||||
&community.actor_id
|
|
||||||
);
|
|
||||||
// dont send to object_actor here, as that is responsibility of the community itself
|
|
||||||
send_activity_internal(context, activity, creator, vec![inbox], true, false).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn send_to_community_new(
|
pub(crate) async fn send_to_community_new(
|
||||||
activity: AnnouncableActivities,
|
activity: AnnouncableActivities,
|
||||||
activity_id: &Url,
|
activity_id: &Url,
|
||||||
|
@ -184,62 +98,6 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new `SendActivityTasks`, which will deliver the given activity to inboxes, as well as
|
|
||||||
/// handling signing and retrying failed deliveres.
|
|
||||||
///
|
|
||||||
/// The caller of this function needs to remove any blocked domains from `to`,
|
|
||||||
/// using `check_is_apub_id_valid()`.
|
|
||||||
async fn send_activity_internal<T, Kind>(
|
|
||||||
context: &LemmyContext,
|
|
||||||
activity: T,
|
|
||||||
actor: &dyn ActorType,
|
|
||||||
inboxes: Vec<Url>,
|
|
||||||
insert_into_db: bool,
|
|
||||||
sensitive: bool,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: AsObject<Kind> + Extends<Kind> + Debug,
|
|
||||||
Kind: Serialize,
|
|
||||||
<T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
if !Settings::get().federation.enabled || inboxes.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't send anything to ourselves
|
|
||||||
let hostname = Settings::get().get_hostname_without_port()?;
|
|
||||||
let inboxes: Vec<&Url> = inboxes
|
|
||||||
.iter()
|
|
||||||
.filter(|i| i.domain().expect("valid inbox url") != hostname)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let activity = activity.into_any_base()?;
|
|
||||||
let serialised_activity = serde_json::to_string(&activity)?;
|
|
||||||
|
|
||||||
// This is necessary because send_comment and send_comment_mentions
|
|
||||||
// might send the same ap_id
|
|
||||||
if insert_into_db {
|
|
||||||
let id = activity.id().context(location_info!())?;
|
|
||||||
insert_activity(id, activity.clone(), true, sensitive, context.pool()).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in inboxes {
|
|
||||||
let message = SendActivityTask {
|
|
||||||
activity: serialised_activity.to_owned(),
|
|
||||||
inbox: i.to_owned(),
|
|
||||||
actor_id: actor.actor_id(),
|
|
||||||
private_key: actor.private_key().context(location_info!())?,
|
|
||||||
};
|
|
||||||
if env::var("LEMMY_TEST_SEND_SYNC").is_ok() {
|
|
||||||
do_send(message, &Client::default()).await?;
|
|
||||||
} else {
|
|
||||||
context.activity_queue.queue::<SendActivityTask>(message)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
struct SendActivityTask {
|
struct SendActivityTask {
|
||||||
activity: String,
|
activity: String,
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
activities::{
|
||||||
|
community::announce::{AnnouncableActivities, AnnounceActivity},
|
||||||
|
extract_community,
|
||||||
|
following::{follow::FollowCommunity, undo::UndoFollowCommunity},
|
||||||
|
},
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
http::{
|
http::{
|
||||||
create_apub_response,
|
create_apub_response,
|
||||||
create_apub_tombstone_response,
|
create_apub_tombstone_response,
|
||||||
inbox_enums::GroupInboxActivities,
|
|
||||||
payload_to_string,
|
payload_to_string,
|
||||||
receive_activity,
|
receive_activity,
|
||||||
},
|
},
|
||||||
|
@ -18,6 +22,7 @@ use activitystreams::{
|
||||||
};
|
};
|
||||||
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::{ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::source::{activity::Activity_, community::Community_};
|
use lemmy_db_queries::source::{activity::Activity_, community::Community_};
|
||||||
use lemmy_db_schema::source::{activity::Activity, community::Community};
|
use lemmy_db_schema::source::{activity::Activity, community::Community};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{
|
||||||
|
@ -26,7 +31,8 @@ use lemmy_db_views_actor::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
use log::trace;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(crate) struct CommunityQuery {
|
pub(crate) struct CommunityQuery {
|
||||||
|
@ -52,6 +58,14 @@ pub(crate) async fn get_apub_community_http(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum GroupInboxActivities {
|
||||||
|
FollowCommunity(FollowCommunity),
|
||||||
|
UndoFollowCommunity(UndoFollowCommunity),
|
||||||
|
AnnouncableActivities(AnnouncableActivities),
|
||||||
|
}
|
||||||
|
|
||||||
/// 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,
|
||||||
|
@ -60,7 +74,26 @@ 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?;
|
||||||
receive_activity::<GroupInboxActivities>(request, &unparsed, context).await
|
trace!("Received community inbox activity {}", unparsed);
|
||||||
|
let activity = serde_json::from_str::<GroupInboxActivities>(&unparsed)?;
|
||||||
|
|
||||||
|
receive_group_inbox(activity.clone(), request, &context).await?;
|
||||||
|
|
||||||
|
if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
|
||||||
|
let community = extract_community(&announcable.cc(), &context, &mut 0).await?;
|
||||||
|
if community.local {
|
||||||
|
AnnounceActivity::send(announcable, &community, vec![], &context).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::http) async fn receive_group_inbox(
|
||||||
|
activity: GroupInboxActivities,
|
||||||
|
request: HttpRequest,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
|
receive_activity(request, activity.clone(), context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an empty followers collection, only populating the size (for privacy).
|
/// Returns an empty followers collection, only populating the size (for privacy).
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
use crate::activities::{
|
|
||||||
comment::create_or_update::CreateOrUpdateComment,
|
|
||||||
community::{
|
|
||||||
add_mod::AddMod,
|
|
||||||
announce::AnnounceActivity,
|
|
||||||
block_user::BlockUserFromCommunity,
|
|
||||||
undo_block_user::UndoBlockUserFromCommunity,
|
|
||||||
update::UpdateCommunity,
|
|
||||||
},
|
|
||||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
|
||||||
following::{accept::AcceptFollowCommunity, follow::FollowCommunity, undo::UndoFollowCommunity},
|
|
||||||
post::create_or_update::CreateOrUpdatePost,
|
|
||||||
private_message::{
|
|
||||||
create_or_update::CreateOrUpdatePrivateMessage,
|
|
||||||
delete::DeletePrivateMessage,
|
|
||||||
undo_delete::UndoDeletePrivateMessage,
|
|
||||||
},
|
|
||||||
removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity},
|
|
||||||
voting::{undo_vote::UndoVote, vote::Vote},
|
|
||||||
};
|
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler};
|
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use lemmy_websocket::LemmyContext;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum PersonInboxActivities {
|
|
||||||
AcceptFollowCommunity(AcceptFollowCommunity),
|
|
||||||
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
|
|
||||||
DeletePrivateMessage(DeletePrivateMessage),
|
|
||||||
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
|
|
||||||
AnnounceActivity(Box<AnnounceActivity>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum GroupInboxActivities {
|
|
||||||
FollowCommunity(FollowCommunity),
|
|
||||||
UndoFollowCommunity(UndoFollowCommunity),
|
|
||||||
CreateOrUpdateComment(CreateOrUpdateComment),
|
|
||||||
CreateOrUpdatePost(Box<CreateOrUpdatePost>),
|
|
||||||
Vote(Vote),
|
|
||||||
UndoVote(UndoVote),
|
|
||||||
DeletePostCommentOrCommunity(Delete),
|
|
||||||
UndoDeletePostCommentOrCommunity(UndoDelete),
|
|
||||||
UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
|
|
||||||
UpdateCommunity(Box<UpdateCommunity>),
|
|
||||||
BlockUserFromCommunity(BlockUserFromCommunity),
|
|
||||||
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
|
||||||
AddMod(AddMod),
|
|
||||||
RemoveMod(RemoveMod),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum SharedInboxActivities {
|
|
||||||
// received by group
|
|
||||||
FollowCommunity(FollowCommunity),
|
|
||||||
UndoFollowCommunity(UndoFollowCommunity),
|
|
||||||
CreateOrUpdateComment(CreateOrUpdateComment),
|
|
||||||
CreateOrUpdatePost(Box<CreateOrUpdatePost>),
|
|
||||||
Vote(Vote),
|
|
||||||
UndoVote(UndoVote),
|
|
||||||
Delete(Delete),
|
|
||||||
UndoDelete(UndoDelete),
|
|
||||||
UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
|
|
||||||
UpdateCommunity(Box<UpdateCommunity>),
|
|
||||||
BlockUserFromCommunity(BlockUserFromCommunity),
|
|
||||||
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
|
|
||||||
AddMod(AddMod),
|
|
||||||
RemoveMod(RemoveMod),
|
|
||||||
// received by person
|
|
||||||
AcceptFollowCommunity(AcceptFollowCommunity),
|
|
||||||
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
|
|
||||||
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
|
|
||||||
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
|
|
||||||
DeletePrivateMessage(DeletePrivateMessage),
|
|
||||||
UndoDeletePrivateMessage(UndoDeletePrivateMessage),
|
|
||||||
AnnounceActivity(Box<AnnounceActivity>),
|
|
||||||
}
|
|
|
@ -2,7 +2,10 @@ use crate::{
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
extensions::signatures::verify_signature,
|
extensions::signatures::verify_signature,
|
||||||
fetcher::get_or_fetch_and_upsert_actor,
|
fetcher::get_or_fetch_and_upsert_actor,
|
||||||
http::inbox_enums::SharedInboxActivities,
|
http::{
|
||||||
|
community::{receive_group_inbox, GroupInboxActivities},
|
||||||
|
person::{receive_person_inbox, PersonInboxActivities},
|
||||||
|
},
|
||||||
insert_activity,
|
insert_activity,
|
||||||
APUB_JSON_CONTENT_TYPE,
|
APUB_JSON_CONTENT_TYPE,
|
||||||
};
|
};
|
||||||
|
@ -17,29 +20,47 @@ use anyhow::{anyhow, Context};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::ActivityHandler;
|
use lemmy_apub_lib::{ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{source::activity::Activity_, DbPool};
|
use lemmy_db_queries::{source::activity::Activity_, DbPool};
|
||||||
use lemmy_db_schema::source::activity::Activity;
|
use lemmy_db_schema::source::activity::Activity;
|
||||||
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, trace};
|
||||||
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;
|
||||||
|
|
||||||
mod comment;
|
mod comment;
|
||||||
mod community;
|
mod community;
|
||||||
mod inbox_enums;
|
|
||||||
mod person;
|
mod person;
|
||||||
mod post;
|
mod post;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
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?;
|
||||||
receive_activity::<SharedInboxActivities>(request, &unparsed, context).await
|
trace!("Received shared inbox activity {}", unparsed);
|
||||||
|
let activity = serde_json::from_str::<SharedInboxActivities>(&unparsed)?;
|
||||||
|
match activity {
|
||||||
|
SharedInboxActivities::GroupInboxActivities(g) => {
|
||||||
|
receive_group_inbox(g, request, &context).await
|
||||||
|
}
|
||||||
|
SharedInboxActivities::PersonInboxActivities(p) => {
|
||||||
|
receive_person_inbox(p, request, &context).await
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
|
async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
|
||||||
|
@ -55,36 +76,36 @@ async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
|
||||||
// TODO: move most of this code to library
|
// TODO: move most of this code to library
|
||||||
async fn receive_activity<'a, T>(
|
async fn receive_activity<'a, T>(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
activity: &'a str,
|
activity: T,
|
||||||
context: web::Data<LemmyContext>,
|
context: &LemmyContext,
|
||||||
) -> Result<HttpResponse, LemmyError>
|
) -> Result<HttpResponse, LemmyError>
|
||||||
where
|
where
|
||||||
T: ActivityHandler + Clone + Deserialize<'a> + Serialize + std::fmt::Debug + Send + 'static,
|
T: ActivityHandler
|
||||||
|
+ ActivityFields
|
||||||
|
+ Clone
|
||||||
|
+ Deserialize<'a>
|
||||||
|
+ Serialize
|
||||||
|
+ std::fmt::Debug
|
||||||
|
+ Send
|
||||||
|
+ 'static,
|
||||||
{
|
{
|
||||||
let activity = serde_json::from_str::<T>(activity)?;
|
|
||||||
let activity_data = activity.common();
|
|
||||||
|
|
||||||
let request_counter = &mut 0;
|
let request_counter = &mut 0;
|
||||||
let actor =
|
let actor = get_or_fetch_and_upsert_actor(activity.actor(), context, request_counter).await?;
|
||||||
get_or_fetch_and_upsert_actor(&activity_data.actor, &context, request_counter).await?;
|
|
||||||
verify_signature(&request, &actor.public_key().context(location_info!())?)?;
|
verify_signature(&request, &actor.public_key().context(location_info!())?)?;
|
||||||
|
|
||||||
// Do nothing if we received the same activity before
|
// Do nothing if we received the same activity before
|
||||||
if is_activity_already_known(context.pool(), activity_data.id_unchecked()).await? {
|
if is_activity_already_known(context.pool(), activity.id_unchecked()).await? {
|
||||||
return Ok(HttpResponse::Ok().finish());
|
return Ok(HttpResponse::Ok().finish());
|
||||||
}
|
}
|
||||||
check_is_apub_id_valid(&activity_data.actor, false)?;
|
check_is_apub_id_valid(activity.actor(), false)?;
|
||||||
println!(
|
info!("Verifying activity {}", activity.id_unchecked().to_string());
|
||||||
"Verifying activity {}",
|
activity.verify(context, request_counter).await?;
|
||||||
activity_data.id_unchecked().to_string()
|
|
||||||
);
|
|
||||||
activity.verify(&context, request_counter).await?;
|
|
||||||
assert_activity_not_local(&activity)?;
|
assert_activity_not_local(&activity)?;
|
||||||
|
|
||||||
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
|
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
|
||||||
// if we receive the same activity twice in very quick succession.
|
// if we receive the same activity twice in very quick succession.
|
||||||
insert_activity(
|
insert_activity(
|
||||||
activity_data.id_unchecked(),
|
activity.id_unchecked(),
|
||||||
activity.clone(),
|
activity.clone(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
@ -92,11 +113,8 @@ where
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
println!(
|
info!("Receiving activity {}", activity.id_unchecked().to_string());
|
||||||
"Receiving activity {}",
|
activity.receive(context, request_counter).await?;
|
||||||
activity_data.id_unchecked().to_string()
|
|
||||||
);
|
|
||||||
activity.receive(&context, request_counter).await?;
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,12 +186,8 @@ pub(crate) async fn is_activity_already_known(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_activity_not_local<T: Debug + ActivityHandler>(activity: &T) -> Result<(), LemmyError> {
|
fn assert_activity_not_local<T: Debug + ActivityFields>(activity: &T) -> Result<(), LemmyError> {
|
||||||
let activity_domain = activity
|
let activity_domain = activity.id_unchecked().domain().context(location_info!())?;
|
||||||
.common()
|
|
||||||
.id_unchecked()
|
|
||||||
.domain()
|
|
||||||
.context(location_info!())?;
|
|
||||||
|
|
||||||
if activity_domain == Settings::get().hostname {
|
if activity_domain == Settings::get().hostname {
|
||||||
return Err(
|
return Err(
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
activities::{
|
||||||
|
community::announce::{AnnouncableActivities, AnnounceActivity},
|
||||||
|
following::accept::AcceptFollowCommunity,
|
||||||
|
private_message::{
|
||||||
|
create_or_update::CreateOrUpdatePrivateMessage,
|
||||||
|
delete::DeletePrivateMessage,
|
||||||
|
undo_delete::UndoDeletePrivateMessage,
|
||||||
|
},
|
||||||
|
},
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
http::{
|
http::{
|
||||||
create_apub_response,
|
create_apub_response,
|
||||||
create_apub_tombstone_response,
|
create_apub_tombstone_response,
|
||||||
inbox_enums::PersonInboxActivities,
|
|
||||||
payload_to_string,
|
payload_to_string,
|
||||||
receive_activity,
|
receive_activity,
|
||||||
},
|
},
|
||||||
|
@ -16,11 +24,13 @@ use activitystreams::{
|
||||||
};
|
};
|
||||||
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::{ActivityFields, ActivityHandler};
|
||||||
use lemmy_db_queries::source::person::Person_;
|
use lemmy_db_queries::source::person::Person_;
|
||||||
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 serde::Deserialize;
|
use log::trace;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -49,6 +59,18 @@ pub(crate) async fn get_apub_person_http(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
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,
|
||||||
|
@ -56,7 +78,17 @@ 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?;
|
||||||
receive_activity::<PersonInboxActivities>(request, &unparsed, context).await
|
trace!("Received person inbox activity {}", unparsed);
|
||||||
|
let activity = serde_json::from_str::<PersonInboxActivities>(&unparsed)?;
|
||||||
|
receive_person_inbox(activity, request, &context).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::http) async fn receive_person_inbox(
|
||||||
|
activity: PersonInboxActivities,
|
||||||
|
request: HttpRequest,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
|
receive_activity(request, activity, context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_apub_person_outbox(
|
pub(crate) async fn get_apub_person_outbox(
|
||||||
|
|
|
@ -10,7 +10,6 @@ pub mod migrations;
|
||||||
pub mod objects;
|
pub mod objects;
|
||||||
|
|
||||||
use crate::extensions::signatures::PublicKey;
|
use crate::extensions::signatures::PublicKey;
|
||||||
use activitystreams::base::AnyBase;
|
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use diesel::NotFound;
|
use diesel::NotFound;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
|
@ -139,41 +138,6 @@ trait ActorType {
|
||||||
pub trait CommunityType {
|
pub trait CommunityType {
|
||||||
fn followers_url(&self) -> Url;
|
fn followers_url(&self) -> Url;
|
||||||
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
|
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
|
||||||
|
|
||||||
async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
|
|
||||||
|
|
||||||
async fn send_announce(
|
|
||||||
&self,
|
|
||||||
activity: AnyBase,
|
|
||||||
object: Option<Url>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
|
|
||||||
async fn send_add_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
added_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
async fn send_remove_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
removed_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
|
|
||||||
async fn send_block_user(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
blocked_user: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
async fn send_undo_block_user(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
blocked_user: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum EndpointType {
|
pub enum EndpointType {
|
||||||
|
|
|
@ -1,33 +1,15 @@
|
||||||
pub mod values;
|
pub mod values;
|
||||||
|
|
||||||
use activitystreams::{
|
use activitystreams::error::DomainError;
|
||||||
base::AnyBase,
|
|
||||||
error::DomainError,
|
|
||||||
primitives::OneOrMany,
|
|
||||||
unparsed::Unparsed,
|
|
||||||
};
|
|
||||||
pub use lemmy_apub_lib_derive::*;
|
pub use lemmy_apub_lib_derive::*;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
pub trait ActivityFields {
|
||||||
#[serde(rename_all = "camelCase")]
|
fn id_unchecked(&self) -> &Url;
|
||||||
pub struct ActivityCommonFields {
|
fn actor(&self) -> &Url;
|
||||||
#[serde(rename = "@context")]
|
fn cc(&self) -> Vec<Url>;
|
||||||
pub context: OneOrMany<AnyBase>,
|
|
||||||
pub id: Url,
|
|
||||||
pub actor: Url,
|
|
||||||
|
|
||||||
// unparsed fields
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub unparsed: Unparsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActivityCommonFields {
|
|
||||||
pub fn id_unchecked(&self) -> &Url {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
|
@ -43,7 +25,6 @@ pub trait ActivityHandler {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError>;
|
) -> Result<(), LemmyError>;
|
||||||
fn common(&self) -> &ActivityCommonFields;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
|
pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
|
||||||
|
@ -53,13 +34,6 @@ pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_domains_match_opt(a: &Url, b: Option<&Url>) -> Result<(), LemmyError> {
|
|
||||||
if let Some(b2) = b {
|
|
||||||
return verify_domains_match(a, b2);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
|
pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
|
||||||
if a != b {
|
if a != b {
|
||||||
return Err(DomainError.into());
|
return Err(DomainError.into());
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, Data, DeriveInput};
|
use syn::{parse_macro_input, Data, DeriveInput, Fields::Unnamed, Ident, Variant};
|
||||||
|
|
||||||
/// Generates implementation ActivityHandler for an enum, which looks like the following (handling
|
/// Generates implementation ActivityHandler for an enum, which looks like the following (handling
|
||||||
/// all enum variants).
|
/// all enum variants).
|
||||||
|
@ -46,104 +46,118 @@ use syn::{parse_macro_input, Data, DeriveInput};
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// TODO: consider replacing this macro with https://crates.io/crates/typetag crate, though it
|
|
||||||
/// doesnt support untagged enums which we need for apub.
|
|
||||||
#[proc_macro_derive(ActivityHandler)]
|
#[proc_macro_derive(ActivityHandler)]
|
||||||
pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
// Parse the input tokens into a syntax tree.
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
// Used in the quasi-quotation below as `#name`.
|
let enum_name = input.ident;
|
||||||
let name = input.ident;
|
|
||||||
|
|
||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
||||||
let input_enum = if let Data::Enum(d) = input.data {
|
let enum_variants = if let Data::Enum(d) = input.data {
|
||||||
d
|
d.variants
|
||||||
} else {
|
} else {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
};
|
};
|
||||||
|
|
||||||
let impl_verify = input_enum
|
let body_verify = quote! {a.verify(context, request_counter).await};
|
||||||
.variants
|
let impl_verify = enum_variants
|
||||||
.iter()
|
.iter()
|
||||||
.map(|variant| variant_impl_verify(&name, variant));
|
.map(|v| generate_match_arm(&enum_name, v, &body_verify));
|
||||||
let impl_receive = input_enum
|
let body_receive = quote! {a.receive(context, request_counter).await};
|
||||||
.variants
|
let impl_receive = enum_variants
|
||||||
.iter()
|
.iter()
|
||||||
.map(|variant| variant_impl_receive(&name, variant));
|
.map(|v| generate_match_arm(&enum_name, v, &body_receive));
|
||||||
let impl_common = input_enum
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| variant_impl_common(&name, variant));
|
|
||||||
|
|
||||||
// The generated impl.
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl #impl_generics lemmy_apub_lib::ActivityHandler for #name #ty_generics #where_clause {
|
impl #impl_generics lemmy_apub_lib::ActivityHandler for #enum_name #ty_generics #where_clause {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&self,
|
&self,
|
||||||
context: &LemmyContext,
|
context: &lemmy_websocket::LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), lemmy_utils::LemmyError> {
|
||||||
match self {
|
match self {
|
||||||
#(#impl_verify)*
|
#(#impl_verify)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn receive(
|
async fn receive(
|
||||||
self,
|
self,
|
||||||
context: &LemmyContext,
|
context: &lemmy_websocket::LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), lemmy_utils::LemmyError> {
|
||||||
match self {
|
match self {
|
||||||
#(#impl_receive)*
|
#(#impl_receive)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn common(&self) -> &ActivityCommonFields {
|
|
||||||
match self {
|
|
||||||
#(#impl_common)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
expanded.into()
|
||||||
// Hand the output tokens back to the compiler.
|
|
||||||
proc_macro::TokenStream::from(expanded)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variant_impl_common(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
|
fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream) -> TokenStream {
|
||||||
let id = &variant.ident;
|
let id = &variant.ident;
|
||||||
match &variant.fields {
|
match &variant.fields {
|
||||||
syn::Fields::Unnamed(_) => {
|
Unnamed(_) => {
|
||||||
quote! {
|
quote! {
|
||||||
#name::#id(a) => a.common(),
|
#enum_name::#id(a) => #body,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variant_impl_verify(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
|
#[proc_macro_derive(ActivityFields)]
|
||||||
let id = &variant.ident;
|
pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
match &variant.fields {
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
syn::Fields::Unnamed(_) => {
|
|
||||||
quote! {
|
|
||||||
#name::#id(a) => a.verify(context, request_counter).await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variant_impl_receive(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
|
let name = input.ident;
|
||||||
let id = &variant.ident;
|
|
||||||
match &variant.fields {
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
syn::Fields::Unnamed(_) => {
|
|
||||||
|
let expanded = match input.data {
|
||||||
|
Data::Enum(e) => {
|
||||||
|
let variants = e.variants;
|
||||||
|
let impl_id = variants
|
||||||
|
.iter()
|
||||||
|
.map(|v| generate_match_arm(&name, v, "e! {a.id_unchecked()}));
|
||||||
|
let impl_actor = variants
|
||||||
|
.iter()
|
||||||
|
.map(|v| generate_match_arm(&name, v, "e! {a.actor()}));
|
||||||
|
let impl_cc = variants
|
||||||
|
.iter()
|
||||||
|
.map(|v| generate_match_arm(&name, v, "e! {a.cc()}));
|
||||||
quote! {
|
quote! {
|
||||||
#name::#id(a) => a.receive(context, request_counter).await,
|
impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause {
|
||||||
|
fn id_unchecked(&self) -> &url::Url { match self { #(#impl_id)* } }
|
||||||
|
fn actor(&self) -> &url::Url { match self { #(#impl_actor)* } }
|
||||||
|
fn cc(&self) -> Vec<url::Url> { match self { #(#impl_cc)* } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Data::Struct(s) => {
|
||||||
|
// check if the struct has a field "cc", and generate impl for cc() function depending on that
|
||||||
|
let has_cc = if let syn::Fields::Named(n) = s.fields {
|
||||||
|
n.named
|
||||||
|
.iter()
|
||||||
|
.any(|i| format!("{}", i.ident.as_ref().unwrap()) == "cc")
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
};
|
||||||
|
let cc_impl = if has_cc {
|
||||||
|
quote! {self.cc.clone().into()}
|
||||||
|
} else {
|
||||||
|
quote! {vec![]}
|
||||||
|
};
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause {
|
||||||
|
fn id_unchecked(&self) -> &url::Url { &self.id }
|
||||||
|
fn actor(&self) -> &url::Url { &self.actor }
|
||||||
|
fn cc(&self) -> Vec<url::Url> { #cc_impl }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
};
|
||||||
|
expanded.into()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue