Major refactor, adding newtypes for apub crate

- this allows moving FromApub/ToApub traits into apub lib
This commit is contained in:
Felix Ableitner 2021-10-18 23:36:44 +02:00
parent f24999027e
commit 1aa0e1997b
69 changed files with 1156 additions and 904 deletions

1
Cargo.lock generated
View file

@ -1839,7 +1839,6 @@ dependencies = [
"diesel-derive-newtype", "diesel-derive-newtype",
"diesel_migrations", "diesel_migrations",
"lazy_static", "lazy_static",
"lemmy_apub_lib",
"lemmy_utils", "lemmy_utils",
"log", "log",
"regex", "regex",

View file

@ -191,7 +191,7 @@ impl Perform for CreateCommentLike {
// Only add the like if the score isnt 0 // Only add the like if the score isnt 0
let comment = orig_comment.comment; let comment = orig_comment.comment;
let object = PostOrComment::Comment(comment); let object = PostOrComment::Comment(comment.into());
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
if do_add { if do_add {
let like_form2 = like_form.clone(); let like_form2 = like_form.clone();
@ -202,7 +202,7 @@ impl Perform for CreateCommentLike {
Vote::send( Vote::send(
&object, &object,
&local_user_view.person, &local_user_view.person.clone().into(),
orig_comment.community.id, orig_comment.community.id,
like_form.score.try_into()?, like_form.score.try_into()?,
context, context,
@ -212,7 +212,7 @@ impl Perform for CreateCommentLike {
// API doesn't distinguish between Undo/Like and Undo/Dislike // API doesn't distinguish between Undo/Like and Undo/Dislike
UndoVote::send( UndoVote::send(
&object, &object,
&local_user_view.person, &local_user_view.person.clone().into(),
orig_comment.community.id, orig_comment.community.id,
VoteType::Like, VoteType::Like,
context, context,

View file

@ -79,8 +79,8 @@ impl Perform for CreateCommentReport {
Report::send( Report::send(
ObjectId::new(comment_view.comment.ap_id), ObjectId::new(comment_view.comment.ap_id),
&local_user_view.person, &local_user_view.person.into(),
comment_view.community.id, ObjectId::new(comment_view.community.actor_id),
reason.to_string(), reason.to_string(),
context, context,
) )

View file

@ -9,7 +9,8 @@ use lemmy_api_common::{
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_mod_or_admin, is_mod_or_admin,
}; };
use lemmy_apub::activities::{ use lemmy_apub::{
activities::{
community::{ community::{
add_mod::AddMod, add_mod::AddMod,
block_user::BlockUserFromCommunity, block_user::BlockUserFromCommunity,
@ -17,6 +18,8 @@ use lemmy_apub::activities::{
undo_block_user::UndoBlockUserFromCommunity, undo_block_user::UndoBlockUserFromCommunity,
}, },
following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity}, following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
},
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
@ -68,10 +71,11 @@ impl Perform for FollowCommunity {
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
let community_id = data.community_id; let community_id = data.community_id;
let community = blocking(context.pool(), move |conn| { let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??
.into();
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
community_id: data.community_id, community_id: data.community_id,
person_id: local_user_view.person.id, person_id: local_user_view.person.id,
@ -97,9 +101,11 @@ impl Perform for FollowCommunity {
} else if data.follow { } else if data.follow {
// Dont actually add to the community followers here, because you need // Dont actually add to the community followers here, because you need
// to wait for the accept // to wait for the accept
FollowCommunityApub::send(&local_user_view.person, &community, context).await?; FollowCommunityApub::send(&local_user_view.person.clone().into(), &community, context)
.await?;
} else { } else {
UndoFollowCommunity::send(&local_user_view.person, &community, context).await?; UndoFollowCommunity::send(&local_user_view.person.clone().into(), &community, context)
.await?;
let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form); let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
blocking(context.pool(), unfollow) blocking(context.pool(), unfollow)
.await? .await?
@ -165,7 +171,7 @@ impl Perform for BlockCommunity {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??;
UndoFollowCommunity::send(&local_user_view.person, &community, context).await?; UndoFollowCommunity::send(&local_user_view.person.into(), &community.into(), context).await?;
} else { } else {
let unblock = move |conn: &'_ _| CommunityBlock::unblock(conn, &community_block_form); let unblock = move |conn: &'_ _| CommunityBlock::unblock(conn, &community_block_form);
blocking(context.pool(), unblock) blocking(context.pool(), unblock)
@ -209,14 +215,16 @@ impl Perform for BanFromCommunity {
person_id: data.person_id, person_id: data.person_id,
}; };
let community = blocking(context.pool(), move |conn: &'_ _| { let community: ApubCommunity = blocking(context.pool(), move |conn: &'_ _| {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??
let banned_person = blocking(context.pool(), move |conn: &'_ _| { .into();
let banned_person: ApubPerson = blocking(context.pool(), move |conn: &'_ _| {
Person::read(conn, banned_person_id) Person::read(conn, banned_person_id)
}) })
.await??; .await??
.into();
if data.ban { if data.ban {
let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form); let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form);
@ -236,7 +244,12 @@ impl Perform for BanFromCommunity {
.await? .await?
.ok(); .ok();
BlockUserFromCommunity::send(&community, &banned_person, &local_user_view.person, context) BlockUserFromCommunity::send(
&community,
&banned_person,
&local_user_view.person.clone().into(),
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);
@ -246,7 +259,7 @@ impl Perform for BanFromCommunity {
UndoBlockUserFromCommunity::send( UndoBlockUserFromCommunity::send(
&community, &community,
&banned_person, &banned_person,
&local_user_view.person, &local_user_view.person.clone().into(),
context, context,
) )
.await?; .await?;
@ -368,18 +381,32 @@ impl Perform for AddModToCommunity {
// Send to federated instances // Send to federated instances
let updated_mod_id = data.person_id; let updated_mod_id = data.person_id;
let updated_mod = blocking(context.pool(), move |conn| { let updated_mod: ApubPerson = blocking(context.pool(), move |conn| {
Person::read(conn, updated_mod_id) Person::read(conn, updated_mod_id)
}) })
.await??; .await??
let community = blocking(context.pool(), move |conn| { .into();
let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??
.into();
if data.added { if data.added {
AddMod::send(&community, &updated_mod, &local_user_view.person, context).await?; AddMod::send(
&community,
&updated_mod,
&local_user_view.person.into(),
context,
)
.await?;
} else { } else {
RemoveMod::send(&community, &updated_mod, &local_user_view.person, context).await?; RemoveMod::send(
&community,
&updated_mod,
&local_user_view.person.into(),
context,
)
.await?;
} }
// Note: in case a remote mod is added, this returns the old moderators list, it will only get // Note: in case a remote mod is added, this returns the old moderators list, it will only get

View file

@ -21,6 +21,7 @@ use lemmy_apub::{
CreateOrUpdateType, CreateOrUpdateType,
}, },
fetcher::post_or_comment::PostOrComment, fetcher::post_or_comment::PostOrComment,
objects::post::ApubPost,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{moderator::*, post::*}, source::{moderator::*, post::*},
@ -49,7 +50,9 @@ impl Perform for CreatePostLike {
// Check for a community ban // Check for a community ban
let post_id = data.post_id; let post_id = data.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, post_id))
.await??
.into();
check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
check_community_deleted_or_removed(post.community_id, context.pool()).await?; check_community_deleted_or_removed(post.community_id, context.pool()).await?;
@ -83,7 +86,7 @@ impl Perform for CreatePostLike {
Vote::send( Vote::send(
&object, &object,
&local_user_view.person, &local_user_view.person.clone().into(),
community_id, community_id,
like_form.score.try_into()?, like_form.score.try_into()?,
context, context,
@ -93,7 +96,7 @@ impl Perform for CreatePostLike {
// API doesn't distinguish between Undo/Like and Undo/Dislike // API doesn't distinguish between Undo/Like and Undo/Dislike
UndoVote::send( UndoVote::send(
&object, &object,
&local_user_view.person, &local_user_view.person.clone().into(),
community_id, community_id,
VoteType::Like, VoteType::Like,
context, context,
@ -150,10 +153,11 @@ impl Perform for LockPost {
// Update the post // Update the post
let post_id = data.post_id; let post_id = data.post_id;
let locked = data.locked; let locked = data.locked;
let updated_post = blocking(context.pool(), move |conn| { let updated_post: ApubPost = blocking(context.pool(), move |conn| {
Post::update_locked(conn, post_id, locked) Post::update_locked(conn, post_id, locked)
}) })
.await??; .await??
.into();
// Mod tables // Mod tables
let form = ModLockPostForm { let form = ModLockPostForm {
@ -166,7 +170,7 @@ impl Perform for LockPost {
// apub updates // apub updates
CreateOrUpdatePost::send( CreateOrUpdatePost::send(
&updated_post, &updated_post,
&local_user_view.person, &local_user_view.person.clone().into(),
CreateOrUpdateType::Update, CreateOrUpdateType::Update,
context, context,
) )
@ -218,10 +222,11 @@ impl Perform for StickyPost {
// Update the post // Update the post
let post_id = data.post_id; let post_id = data.post_id;
let stickied = data.stickied; let stickied = data.stickied;
let updated_post = blocking(context.pool(), move |conn| { let updated_post: ApubPost = blocking(context.pool(), move |conn| {
Post::update_stickied(conn, post_id, stickied) Post::update_stickied(conn, post_id, stickied)
}) })
.await??; .await??
.into();
// Mod tables // Mod tables
let form = ModStickyPostForm { let form = ModStickyPostForm {
@ -238,7 +243,7 @@ impl Perform for StickyPost {
// TODO stickied should pry work like locked for ease of use // TODO stickied should pry work like locked for ease of use
CreateOrUpdatePost::send( CreateOrUpdatePost::send(
&updated_post, &updated_post,
&local_user_view.person, &local_user_view.person.clone().into(),
CreateOrUpdateType::Update, CreateOrUpdateType::Update,
context, context,
) )

View file

@ -88,8 +88,8 @@ impl Perform for CreatePostReport {
Report::send( Report::send(
ObjectId::new(post_view.post.ap_id), ObjectId::new(post_view.post.ap_id),
&local_user_view.person, &local_user_view.person.into(),
post_view.community.id, ObjectId::new(post_view.community.actor_id),
reason.to_string(), reason.to_string(),
context, context,
) )

View file

@ -6,15 +6,11 @@ pub mod site;
pub mod websocket; pub mod websocket;
use crate::site::FederatedInstances; use crate::site::FederatedInstances;
use diesel::PgConnection;
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{CommunityId, LocalUserId, PersonId, PostId}, newtypes::{CommunityId, LocalUserId, PersonId, PostId},
source::{ source::{
comment::Comment,
community::Community, community::Community,
person::Person,
person_block::PersonBlock, person_block::PersonBlock,
person_mention::{PersonMention, PersonMentionForm},
post::{Post, PostRead, PostReadForm}, post::{Post, PostRead, PostReadForm},
secret::Secret, secret::Secret,
site::Site, site::Site,
@ -27,15 +23,7 @@ use lemmy_db_views_actor::{
community_person_ban_view::CommunityPersonBanView, community_person_ban_view::CommunityPersonBanView,
community_view::CommunityView, community_view::CommunityView,
}; };
use lemmy_utils::{ use lemmy_utils::{claims::Claims, settings::structs::FederationConfig, ApiError, LemmyError};
claims::Claims,
email::send_email,
settings::structs::{FederationConfig, Settings},
utils::MentionData,
ApiError,
LemmyError,
};
use log::error;
use url::Url; use url::Url;
pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError> pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
@ -54,160 +42,6 @@ where
res res
} }
pub async fn send_local_notifs(
mentions: Vec<MentionData>,
comment: Comment,
person: Person,
post: Post,
pool: &DbPool,
do_send_email: bool,
settings: &Settings,
) -> Result<Vec<LocalUserId>, LemmyError> {
let settings = settings.to_owned();
let ids = blocking(pool, move |conn| {
do_send_local_notifs(
conn,
&mentions,
&comment,
&person,
&post,
do_send_email,
&settings,
)
})
.await?;
Ok(ids)
}
fn do_send_local_notifs(
conn: &PgConnection,
mentions: &[MentionData],
comment: &Comment,
person: &Person,
post: &Post,
do_send_email: bool,
settings: &Settings,
) -> Vec<LocalUserId> {
let mut recipient_ids = Vec::new();
// Send the local mentions
for mention in mentions
.iter()
.filter(|m| m.is_local(&settings.hostname) && m.name.ne(&person.name))
.collect::<Vec<&MentionData>>()
{
if let Ok(mention_user_view) = LocalUserView::read_from_name(conn, &mention.name) {
// TODO
// At some point, make it so you can't tag the parent creator either
// This can cause two notifications, one for reply and the other for mention
recipient_ids.push(mention_user_view.local_user.id);
let user_mention_form = PersonMentionForm {
recipient_id: mention_user_view.person.id,
comment_id: comment.id,
read: None,
};
// Allow this to fail softly, since comment edits might re-update or replace it
// Let the uniqueness handle this fail
PersonMention::create(conn, &user_mention_form).ok();
// Send an email to those local users that have notifications on
if do_send_email {
send_email_to_user(
&mention_user_view,
"Mentioned by",
"Person Mention",
&comment.content,
settings,
)
}
}
}
// Send notifs to the parent commenter / poster
match comment.parent_id {
Some(parent_id) => {
if let Ok(parent_comment) = Comment::read(conn, parent_id) {
// Don't send a notif to yourself
if parent_comment.creator_id != person.id {
// Get the parent commenter local_user
if let Ok(parent_user_view) = LocalUserView::read_person(conn, parent_comment.creator_id)
{
recipient_ids.push(parent_user_view.local_user.id);
if do_send_email {
send_email_to_user(
&parent_user_view,
"Reply from",
"Comment Reply",
&comment.content,
settings,
)
}
}
}
}
}
// Its a post
None => {
if post.creator_id != person.id {
if let Ok(parent_user_view) = LocalUserView::read_person(conn, post.creator_id) {
recipient_ids.push(parent_user_view.local_user.id);
if do_send_email {
send_email_to_user(
&parent_user_view,
"Reply from",
"Post Reply",
&comment.content,
settings,
)
}
}
}
}
};
recipient_ids
}
pub fn send_email_to_user(
local_user_view: &LocalUserView,
subject_text: &str,
body_text: &str,
comment_content: &str,
settings: &Settings,
) {
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
return;
}
if let Some(user_email) = &local_user_view.local_user.email {
let subject = &format!(
"{} - {} {}",
subject_text, settings.hostname, local_user_view.person.name,
);
let html = &format!(
"<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
body_text,
local_user_view.person.name,
comment_content,
settings.get_protocol_and_hostname()
);
match send_email(
subject,
user_email,
&local_user_view.person.name,
html,
settings,
) {
Ok(_o) => _o,
Err(e) => error!("{}", e),
};
}
}
pub async fn is_mod_or_admin( pub async fn is_mod_or_admin(
pool: &DbPool, pool: &DbPool,
person_id: PersonId, person_id: PersonId,

View file

@ -9,7 +9,6 @@ use lemmy_api_common::{
comment::*, comment::*,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
get_post, get_post,
send_local_notifs,
}; };
use lemmy_apub::{ use lemmy_apub::{
activities::{ activities::{
@ -35,7 +34,11 @@ use lemmy_utils::{
ConnectionId, ConnectionId,
LemmyError, LemmyError,
}; };
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
LemmyContext,
UserOperationCrud,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for CreateComment { impl PerformCrud for CreateComment {
@ -117,8 +120,8 @@ impl PerformCrud for CreateComment {
.map_err(|e| ApiError::err("couldnt_create_comment", e))?; .map_err(|e| ApiError::err("couldnt_create_comment", e))?;
CreateOrUpdateComment::send( CreateOrUpdateComment::send(
&updated_comment, &updated_comment.clone().into(),
&local_user_view.person, &local_user_view.person.clone().into(),
CreateOrUpdateType::Create, CreateOrUpdateType::Create,
context, context,
) )
@ -129,12 +132,11 @@ impl PerformCrud for CreateComment {
let mentions = scrape_text_for_mentions(&comment_form.content); let mentions = scrape_text_for_mentions(&comment_form.content);
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment.clone(), &updated_comment,
local_user_view.person.clone(), &local_user_view.person,
post, &post,
context.pool(),
true, true,
&context.settings(), context,
) )
.await?; .await?;
@ -151,10 +153,10 @@ impl PerformCrud for CreateComment {
.await? .await?
.map_err(|e| ApiError::err("couldnt_like_comment", e))?; .map_err(|e| ApiError::err("couldnt_like_comment", e))?;
let object = PostOrComment::Comment(updated_comment); let object = PostOrComment::Comment(updated_comment.into());
Vote::send( Vote::send(
&object, &object,
&local_user_view.person, &local_user_view.person.clone().into(),
community_id, community_id,
VoteType::Like, VoteType::Like,
context, context,

View file

@ -6,7 +6,6 @@ use lemmy_api_common::{
comment::*, comment::*,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_mod_or_admin, is_mod_or_admin,
send_local_notifs,
}; };
use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove}; use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove};
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -20,7 +19,11 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::comment_view::CommentView; use lemmy_db_views::comment_view::CommentView;
use lemmy_utils::{ApiError, ConnectionId, LemmyError}; use lemmy_utils::{ApiError, ConnectionId, LemmyError};
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
LemmyContext,
UserOperationCrud,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteComment { impl PerformCrud for DeleteComment {
@ -67,8 +70,8 @@ impl PerformCrud for DeleteComment {
}) })
.await??; .await??;
send_apub_delete( send_apub_delete(
&local_user_view.person, &local_user_view.person.clone().into(),
&community, &community.clone().into(),
updated_comment.ap_id.clone().into(), updated_comment.ap_id.clone().into(),
deleted, deleted,
context, context,
@ -79,12 +82,11 @@ impl PerformCrud for DeleteComment {
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
vec![], vec![],
updated_comment, &updated_comment,
local_user_view.person.clone(), &local_user_view.person,
post, &post,
context.pool(),
false, false,
&context.settings(), context,
) )
.await?; .await?;
@ -161,8 +163,8 @@ impl PerformCrud for RemoveComment {
}) })
.await??; .await??;
send_apub_remove( send_apub_remove(
&local_user_view.person, &local_user_view.person.clone().into(),
&community, &community.into(),
updated_comment.ap_id.clone().into(), updated_comment.ap_id.clone().into(),
data.reason.clone().unwrap_or_else(|| "".to_string()), data.reason.clone().unwrap_or_else(|| "".to_string()),
removed, removed,
@ -174,12 +176,11 @@ impl PerformCrud for RemoveComment {
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
vec![], vec![],
updated_comment, &updated_comment,
local_user_view.person.clone(), &local_user_view.person.clone(),
post, &post,
context.pool(),
false, false,
&context.settings(), context,
) )
.await?; .await?;

View file

@ -7,7 +7,6 @@ use lemmy_api_common::{
check_post_deleted_or_removed, check_post_deleted_or_removed,
comment::*, comment::*,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
send_local_notifs,
}; };
use lemmy_apub::activities::{ use lemmy_apub::activities::{
comment::create_or_update::CreateOrUpdateComment, comment::create_or_update::CreateOrUpdateComment,
@ -21,7 +20,11 @@ use lemmy_utils::{
ConnectionId, ConnectionId,
LemmyError, LemmyError,
}; };
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
LemmyContext,
UserOperationCrud,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for EditComment { impl PerformCrud for EditComment {
@ -69,8 +72,8 @@ impl PerformCrud for EditComment {
// Send the apub update // Send the apub update
CreateOrUpdateComment::send( CreateOrUpdateComment::send(
&updated_comment, &updated_comment.clone().into(),
&local_user_view.person, &local_user_view.person.clone().into(),
CreateOrUpdateType::Update, CreateOrUpdateType::Update,
context, context,
) )
@ -81,12 +84,11 @@ impl PerformCrud for EditComment {
let mentions = scrape_text_for_mentions(&updated_comment_content); let mentions = scrape_text_for_mentions(&updated_comment_content);
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
updated_comment, &updated_comment,
local_user_view.person.clone(), &local_user_view.person,
orig_comment.post, &orig_comment.post,
context.pool(),
false, false,
&context.settings(), context,
) )
.await?; .await?;

View file

@ -12,6 +12,7 @@ use lemmy_apub::{
generate_followers_url, generate_followers_url,
generate_inbox_url, generate_inbox_url,
generate_shared_inbox_url, generate_shared_inbox_url,
objects::community::ApubCommunity,
EndpointType, EndpointType,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -71,7 +72,7 @@ impl PerformCrud for CreateCommunity {
&data.name, &data.name,
&context.settings().get_protocol_and_hostname(), &context.settings().get_protocol_and_hostname(),
)?; )?;
let community_actor_id_wrapped = ObjectId::<Community>::new(community_actor_id.clone()); let community_actor_id_wrapped = ObjectId::<ApubCommunity>::new(community_actor_id.clone());
let community_dupe = community_actor_id_wrapped.dereference_local(context).await; let community_dupe = community_actor_id_wrapped.dereference_local(context).await;
if community_dupe.is_ok() { if community_dupe.is_ok() {
return Err(ApiError::err_plain("community_already_exists").into()); return Err(ApiError::err_plain("community_already_exists").into());

View file

@ -49,8 +49,8 @@ impl PerformCrud for DeleteCommunity {
// Send apub messages // Send apub messages
send_apub_delete( send_apub_delete(
&local_user_view.person, &local_user_view.person.clone().into(),
&updated_community, &updated_community.clone().into(),
updated_community.actor_id.clone().into(), updated_community.actor_id.clone().into(),
deleted, deleted,
context, context,
@ -109,8 +109,8 @@ impl PerformCrud for RemoveCommunity {
// Apub messages // Apub messages
send_apub_remove( send_apub_remove(
&local_user_view.person, &local_user_view.person.clone().into(),
&updated_community, &updated_community.clone().into(),
updated_community.actor_id.clone().into(), updated_community.actor_id.clone().into(),
data.reason.clone().unwrap_or_else(|| "".to_string()), data.reason.clone().unwrap_or_else(|| "".to_string()),
removed, removed,

View file

@ -1,10 +1,14 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt}; use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
use lemmy_apub::{build_actor_id_from_shortname, fetcher::object_id::ObjectId, EndpointType}; use lemmy_apub::{
build_actor_id_from_shortname,
fetcher::object_id::ObjectId,
objects::community::ApubCommunity,
EndpointType,
};
use lemmy_db_schema::{ use lemmy_db_schema::{
from_opt_str_to_opt_enum, from_opt_str_to_opt_enum,
source::community::Community,
traits::DeleteableOrRemoveable, traits::DeleteableOrRemoveable,
ListingType, ListingType,
SortType, SortType,
@ -37,7 +41,7 @@ impl PerformCrud for GetCommunity {
let community_actor_id = let community_actor_id =
build_actor_id_from_shortname(EndpointType::Community, &name, &context.settings())?; build_actor_id_from_shortname(EndpointType::Community, &name, &context.settings())?;
ObjectId::<Community>::new(community_actor_id) ObjectId::<ApubCommunity>::new(community_actor_id)
.dereference(context, &mut 0) .dereference(context, &mut 0)
.await .await
.map_err(|e| ApiError::err("couldnt_find_community", e))? .map_err(|e| ApiError::err("couldnt_find_community", e))?

View file

@ -71,7 +71,12 @@ impl PerformCrud for EditCommunity {
.await? .await?
.map_err(|e| ApiError::err("couldnt_update_community", e))?; .map_err(|e| ApiError::err("couldnt_update_community", e))?;
UpdateCommunity::send(&updated_community, &local_user_view.person, context).await?; UpdateCommunity::send(
&updated_community.into(),
&local_user_view.person.into(),
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

View file

@ -109,8 +109,8 @@ impl PerformCrud for CreatePost {
.map_err(|e| ApiError::err("couldnt_create_post", e))?; .map_err(|e| ApiError::err("couldnt_create_post", e))?;
CreateOrUpdatePost::send( CreateOrUpdatePost::send(
&updated_post, &updated_post.clone().into(),
&local_user_view.person, &local_user_view.person.clone().into(),
CreateOrUpdateType::Create, CreateOrUpdateType::Create,
context, context,
) )
@ -146,10 +146,10 @@ impl PerformCrud for CreatePost {
} }
} }
let object = PostOrComment::Post(Box::new(updated_post)); let object = PostOrComment::Post(Box::new(updated_post.into()));
Vote::send( Vote::send(
&object, &object,
&local_user_view.person, &local_user_view.person.clone().into(),
inserted_post.community_id, inserted_post.community_id,
VoteType::Like, VoteType::Like,
context, context,

View file

@ -63,8 +63,8 @@ impl PerformCrud for DeletePost {
}) })
.await??; .await??;
send_apub_delete( send_apub_delete(
&local_user_view.person, &local_user_view.person.clone().into(),
&community, &community.into(),
updated_post.ap_id.into(), updated_post.ap_id.into(),
deleted, deleted,
context, context,
@ -139,8 +139,8 @@ impl PerformCrud for RemovePost {
}) })
.await??; .await??;
send_apub_remove( send_apub_remove(
&local_user_view.person, &local_user_view.person.clone().into(),
&community, &community.into(),
updated_post.ap_id.into(), updated_post.ap_id.into(),
data.reason.clone().unwrap_or_else(|| "".to_string()), data.reason.clone().unwrap_or_else(|| "".to_string()),
removed, removed,

View file

@ -104,8 +104,8 @@ impl PerformCrud for EditPost {
// Send apub update // Send apub update
CreateOrUpdatePost::send( CreateOrUpdatePost::send(
&updated_post, &updated_post.into(),
&local_user_view.person, &local_user_view.person.clone().into(),
CreateOrUpdateType::Update, CreateOrUpdateType::Update,
context, context,
) )

View file

@ -5,7 +5,6 @@ use lemmy_api_common::{
check_person_block, check_person_block,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
person::{CreatePrivateMessage, PrivateMessageResponse}, person::{CreatePrivateMessage, PrivateMessageResponse},
send_email_to_user,
}; };
use lemmy_apub::{ use lemmy_apub::{
activities::{ activities::{
@ -21,7 +20,11 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::local_user_view::LocalUserView; use lemmy_db_views::local_user_view::LocalUserView;
use lemmy_utils::{utils::remove_slurs, ApiError, ConnectionId, LemmyError}; use lemmy_utils::{utils::remove_slurs, ApiError, ConnectionId, LemmyError};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{
send::{send_email_to_user, send_pm_ws_message},
LemmyContext,
UserOperationCrud,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for CreatePrivateMessage { impl PerformCrud for CreatePrivateMessage {
@ -80,8 +83,8 @@ impl PerformCrud for CreatePrivateMessage {
.map_err(|e| ApiError::err("couldnt_create_private_message", e))?; .map_err(|e| ApiError::err("couldnt_create_private_message", e))?;
CreateOrUpdatePrivateMessage::send( CreateOrUpdatePrivateMessage::send(
&updated_private_message, &updated_private_message.into(),
&local_user_view.person, &local_user_view.person.into(),
CreateOrUpdateType::Create, CreateOrUpdateType::Create,
context, context,
) )

View file

@ -51,13 +51,19 @@ impl PerformCrud for DeletePrivateMessage {
// Send the apub update // Send the apub update
if data.deleted { if data.deleted {
DeletePrivateMessageApub::send( DeletePrivateMessageApub::send(
&local_user_view.person, &local_user_view.person.into(),
&updated_private_message.blank_out_deleted_or_removed_info(), &updated_private_message
.blank_out_deleted_or_removed_info()
.into(),
context, context,
) )
.await?; .await?;
} else { } else {
UndoDeletePrivateMessage::send(&local_user_view.person, &updated_private_message, context) UndoDeletePrivateMessage::send(
&local_user_view.person.into(),
&updated_private_message.into(),
context,
)
.await?; .await?;
} }

View file

@ -47,8 +47,8 @@ impl PerformCrud for EditPrivateMessage {
// Send the apub update // Send the apub update
CreateOrUpdatePrivateMessage::send( CreateOrUpdatePrivateMessage::send(
&updated_private_message, &updated_private_message.into(),
&local_user_view.person, &local_user_view.person.into(),
CreateOrUpdateType::Update, CreateOrUpdateType::Update,
context, context,
) )

View file

@ -1,8 +1,13 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*}; use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
use lemmy_apub::{build_actor_id_from_shortname, fetcher::object_id::ObjectId, EndpointType}; use lemmy_apub::{
use lemmy_db_schema::{from_opt_str_to_opt_enum, source::person::Person, SortType}; build_actor_id_from_shortname,
fetcher::object_id::ObjectId,
objects::person::ApubPerson,
EndpointType,
};
use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder}; use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
use lemmy_db_views_actor::{ use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView, community_moderator_view::CommunityModeratorView,
@ -44,7 +49,7 @@ impl PerformCrud for GetPersonDetails {
let actor_id = let actor_id =
build_actor_id_from_shortname(EndpointType::Person, &name, &context.settings())?; build_actor_id_from_shortname(EndpointType::Person, &name, &context.settings())?;
let person = ObjectId::<Person>::new(actor_id) let person = ObjectId::<ApubPerson>::new(actor_id)
.dereference(context, &mut 0) .dereference(context, &mut 0)
.await; .await;
person person

View file

@ -11,18 +11,22 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{comment::Note, FromApub, ToApub}, objects::{
comment::{ApubComment, Note},
community::ApubCommunity,
person::ApubPerson,
},
}; };
use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed}; use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::{blocking, check_post_deleted_or_removed}; use lemmy_api_common::{blocking, check_post_deleted_or_removed};
use lemmy_apub_lib::{ use lemmy_apub_lib::{
data::Data, data::Data,
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType, FromApub, ToApub},
values::PublicUrl, values::PublicUrl,
verify::verify_domains_match, verify::verify_domains_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{comment::Comment, community::Community, person::Person, post::Post}, source::{community::Community, post::Post},
traits::Crud, traits::Crud,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -33,7 +37,7 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CreateOrUpdateComment { pub struct CreateOrUpdateComment {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
object: Note, object: Note,
cc: Vec<Url>, cc: Vec<Url>,
@ -49,8 +53,8 @@ pub struct CreateOrUpdateComment {
impl CreateOrUpdateComment { impl CreateOrUpdateComment {
pub async fn send( pub async fn send(
comment: &Comment, comment: &ApubComment,
actor: &Person, actor: &ApubPerson,
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
@ -58,10 +62,11 @@ impl CreateOrUpdateComment {
let post_id = comment.post_id; let post_id = comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
let community_id = post.community_id; let community_id = post.community_id;
let community = blocking(context.pool(), move |conn| { let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??
.into();
let id = generate_activity_id( let id = generate_activity_id(
kind.clone(), kind.clone(),
@ -117,7 +122,7 @@ impl ActivityHandler for CreateOrUpdateComment {
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let comment = let comment =
Comment::from_apub(&self.object, context, self.actor.inner(), request_counter).await?; ApubComment::from_apub(&self.object, context, self.actor.inner(), request_counter).await?;
let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?; let recipients = get_notif_recipients(&self.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,

View file

@ -1,15 +1,18 @@
use crate::fetcher::object_id::ObjectId; use crate::{
fetcher::object_id::ObjectId,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{ use activitystreams::{
base::BaseExt, base::BaseExt,
link::{LinkExt, Mention}, link::{LinkExt, Mention},
}; };
use anyhow::anyhow; use anyhow::anyhow;
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_common::{blocking, send_local_notifs}; use lemmy_api_common::blocking;
use lemmy_apub_lib::{traits::ActorType, webfinger::WebfingerResponse}; use lemmy_apub_lib::{traits::ActorType, webfinger::WebfingerResponse};
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::LocalUserId, newtypes::LocalUserId,
source::{comment::Comment, community::Community, person::Person, post::Post}, source::{comment::Comment, person::Person, post::Post},
traits::Crud, traits::Crud,
DbPool, DbPool,
}; };
@ -18,14 +21,14 @@ use lemmy_utils::{
utils::{scrape_text_for_mentions, MentionData}, utils::{scrape_text_for_mentions, MentionData},
LemmyError, LemmyError,
}; };
use lemmy_websocket::LemmyContext; use lemmy_websocket::{send::send_local_notifs, LemmyContext};
use log::debug; use log::debug;
use url::Url; use url::Url;
pub mod create_or_update; pub mod create_or_update;
async fn get_notif_recipients( async fn get_notif_recipients(
actor: &ObjectId<Person>, actor: &ObjectId<ApubPerson>,
comment: &Comment, comment: &Comment,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
@ -40,16 +43,7 @@ async fn get_notif_recipients(
// anyway. // anyway.
// TODO: for compatibility with other projects, it would be much better to read this from cc or tags // TODO: for compatibility with other projects, it would be much better to read this from cc or tags
let mentions = scrape_text_for_mentions(&comment.content); let mentions = scrape_text_for_mentions(&comment.content);
send_local_notifs( send_local_notifs(mentions, comment, &*actor, &post, true, context).await
mentions,
comment.clone(),
actor,
post,
context.pool(),
true,
&context.settings(),
)
.await
} }
pub struct MentionsAndAddresses { pub struct MentionsAndAddresses {
@ -62,12 +56,12 @@ pub struct MentionsAndAddresses {
/// and mention tags, so they know where to be sent to. /// and mention tags, so they know where to be sent to.
/// Addresses are the persons / addresses that go in the cc field. /// Addresses are the persons / addresses that go in the cc field.
pub async fn collect_non_local_mentions( pub async fn collect_non_local_mentions(
comment: &Comment, comment: &ApubComment,
community: &Community, community: &ApubCommunity,
context: &LemmyContext, context: &LemmyContext,
) -> Result<MentionsAndAddresses, LemmyError> { ) -> Result<MentionsAndAddresses, LemmyError> {
let parent_creator = get_comment_parent_creator(context.pool(), comment).await?; let parent_creator = get_comment_parent_creator(context.pool(), comment).await?;
let mut addressed_ccs = vec![community.actor_id(), parent_creator.actor_id()]; let mut addressed_ccs: Vec<Url> = vec![community.actor_id(), parent_creator.actor_id()];
// Note: dont include community inbox here, as we send to it separately with `send_to_community()` // Note: dont include community inbox here, as we send to it separately with `send_to_community()`
let mut inboxes = vec![parent_creator.shared_inbox_or_inbox_url()]; let mut inboxes = vec![parent_creator.shared_inbox_or_inbox_url()];
@ -84,9 +78,9 @@ pub async fn collect_non_local_mentions(
for mention in &mentions { for mention in &mentions {
// TODO should it be fetching it every time? // TODO should it be fetching it every time?
if let Ok(actor_id) = fetch_webfinger_url(mention, context).await { if let Ok(actor_id) = fetch_webfinger_url(mention, context).await {
let actor_id: ObjectId<Person> = ObjectId::new(actor_id); let actor_id: ObjectId<ApubPerson> = ObjectId::new(actor_id);
debug!("mention actor_id: {}", actor_id); debug!("mention actor_id: {}", actor_id);
addressed_ccs.push(actor_id.to_owned().to_string().parse()?); addressed_ccs.push(actor_id.to_string().parse()?);
let mention_person = actor_id.dereference(context, &mut 0).await?; let mention_person = actor_id.dereference(context, &mut 0).await?;
inboxes.push(mention_person.shared_inbox_or_inbox_url()); inboxes.push(mention_person.shared_inbox_or_inbox_url());
@ -113,7 +107,7 @@ pub async fn collect_non_local_mentions(
async fn get_comment_parent_creator( async fn get_comment_parent_creator(
pool: &DbPool, pool: &DbPool,
comment: &Comment, comment: &Comment,
) -> Result<Person, LemmyError> { ) -> Result<ApubPerson, LemmyError> {
let parent_creator_id = if let Some(parent_comment_id) = comment.parent_id { let parent_creator_id = if let Some(parent_comment_id) = comment.parent_id {
let parent_comment = let parent_comment =
blocking(pool, move |conn| Comment::read(conn, parent_comment_id)).await??; blocking(pool, move |conn| Comment::read(conn, parent_comment_id)).await??;
@ -123,7 +117,11 @@ async fn get_comment_parent_creator(
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??; let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
parent_post.creator_id parent_post.creator_id
}; };
Ok(blocking(pool, move |conn| Person::read(conn, parent_creator_id)).await??) Ok(
blocking(pool, move |conn| Person::read(conn, parent_creator_id))
.await??
.into(),
)
} }
/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`, /// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,

View file

@ -10,6 +10,7 @@ use crate::{
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
generate_moderators_url, generate_moderators_url,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::AddType, activity::kind::AddType,
@ -24,10 +25,7 @@ use lemmy_apub_lib::{
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{CommunityModerator, CommunityModeratorForm},
community::{Community, CommunityModerator, CommunityModeratorForm},
person::Person,
},
traits::Joinable, traits::Joinable,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -38,11 +36,11 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AddMod { pub struct AddMod {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
object: ObjectId<Person>, object: ObjectId<ApubPerson>,
target: Url, target: Url,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: AddType, kind: AddType,
id: Url, id: Url,
@ -54,9 +52,9 @@ pub struct AddMod {
impl AddMod { impl AddMod {
pub async fn send( pub async fn send(
community: &Community, community: &ApubCommunity,
added_mod: &Person, added_mod: &ApubPerson,
actor: &Person, actor: &ApubPerson,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id( let id = generate_activity_id(
@ -92,7 +90,7 @@ impl ActivityHandler for AddMod {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?; verify_activity(self, &context.settings())?;
verify_person_in_community(&self.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.actor, self.cc[0].clone(), context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
verify_add_remove_moderator_target(&self.target, &self.cc[0])?; verify_add_remove_moderator_target(&self.target, &self.cc[0])?;
Ok(()) Ok(())
} }

View file

@ -21,6 +21,7 @@ use crate::{
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
http::is_activity_already_known, http::is_activity_already_known,
insert_activity, insert_activity,
objects::community::ApubCommunity,
send_lemmy_activity, send_lemmy_activity,
CommunityType, CommunityType,
}; };
@ -35,7 +36,6 @@ use lemmy_apub_lib::{
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType},
values::PublicUrl, values::PublicUrl,
}; };
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};
@ -62,7 +62,7 @@ pub enum AnnouncableActivities {
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AnnounceActivity { pub struct AnnounceActivity {
actor: ObjectId<Community>, actor: ObjectId<ApubCommunity>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
object: AnnouncableActivities, object: AnnouncableActivities,
cc: Vec<Url>, cc: Vec<Url>,
@ -78,7 +78,7 @@ pub struct AnnounceActivity {
impl AnnounceActivity { impl AnnounceActivity {
pub async fn send( pub async fn send(
object: AnnouncableActivities, object: AnnouncableActivities,
community: &Community, community: &ApubCommunity,
additional_inboxes: Vec<Url>, additional_inboxes: Vec<Url>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {

View file

@ -8,6 +8,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::BlockType, activity::kind::BlockType,
@ -22,16 +23,12 @@ use lemmy_apub_lib::{
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{
community::{
Community,
CommunityFollower, CommunityFollower,
CommunityFollowerForm, CommunityFollowerForm,
CommunityPersonBan, CommunityPersonBan,
CommunityPersonBanForm, CommunityPersonBanForm,
}, },
person::Person,
},
traits::{Bannable, Followable}, traits::{Bannable, Followable},
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -42,10 +39,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct BlockUserFromCommunity { pub struct BlockUserFromCommunity {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
pub(in crate::activities::community) object: ObjectId<Person>, pub(in crate::activities::community) object: ObjectId<ApubPerson>,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: BlockType, kind: BlockType,
id: Url, id: Url,
@ -57,9 +54,9 @@ pub struct BlockUserFromCommunity {
impl BlockUserFromCommunity { impl BlockUserFromCommunity {
pub(in crate::activities::community) fn new( pub(in crate::activities::community) fn new(
community: &Community, community: &ApubCommunity,
target: &Person, target: &ApubPerson,
actor: &Person, actor: &ApubPerson,
context: &LemmyContext, context: &LemmyContext,
) -> Result<BlockUserFromCommunity, LemmyError> { ) -> Result<BlockUserFromCommunity, LemmyError> {
Ok(BlockUserFromCommunity { Ok(BlockUserFromCommunity {
@ -78,9 +75,9 @@ impl BlockUserFromCommunity {
} }
pub async fn send( pub async fn send(
community: &Community, community: &ApubCommunity,
target: &Person, target: &ApubPerson,
actor: &Person, actor: &ApubPerson,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let block = BlockUserFromCommunity::new(community, target, actor, context)?; let block = BlockUserFromCommunity::new(community, target, actor, context)?;
@ -102,7 +99,7 @@ impl ActivityHandler for BlockUserFromCommunity {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?; verify_activity(self, &context.settings())?;
verify_person_in_community(&self.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.actor, self.cc[0].clone(), context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
Ok(()) Ok(())
} }

View file

@ -2,12 +2,12 @@ use crate::{
activities::community::announce::{AnnouncableActivities, AnnounceActivity}, activities::community::announce::{AnnouncableActivities, AnnounceActivity},
check_is_apub_id_valid, check_is_apub_id_valid,
insert_activity, insert_activity,
objects::community::ApubCommunity,
send_lemmy_activity, send_lemmy_activity,
CommunityType, CommunityType,
}; };
use itertools::Itertools; use itertools::Itertools;
use lemmy_apub_lib::traits::ActorType; use lemmy_apub_lib::traits::ActorType;
use lemmy_db_schema::source::community::Community;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
@ -20,7 +20,7 @@ pub mod undo_block_user;
pub mod update; pub mod update;
async fn list_community_follower_inboxes( async fn list_community_follower_inboxes(
community: &Community, community: &ApubCommunity,
additional_inboxes: Vec<Url>, additional_inboxes: Vec<Url>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<Vec<Url>, LemmyError> { ) -> Result<Vec<Url>, LemmyError> {
@ -45,7 +45,7 @@ pub(crate) async fn send_to_community<T: ActorType>(
activity: AnnouncableActivities, activity: AnnouncableActivities,
activity_id: &Url, activity_id: &Url,
actor: &T, actor: &T,
community: &Community, community: &ApubCommunity,
additional_inboxes: Vec<Url>, additional_inboxes: Vec<Url>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {

View file

@ -11,6 +11,7 @@ use crate::{
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
generate_moderators_url, generate_moderators_url,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::RemoveType, activity::kind::RemoveType,
@ -25,10 +26,7 @@ use lemmy_apub_lib::{
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{CommunityModerator, CommunityModeratorForm},
community::{Community, CommunityModerator, CommunityModeratorForm},
person::Person,
},
traits::Joinable, traits::Joinable,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -39,10 +37,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RemoveMod { pub struct RemoveMod {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
pub(in crate::activities) object: ObjectId<Person>, pub(in crate::activities) object: ObjectId<ApubPerson>,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: RemoveType, kind: RemoveType,
// if target is set, this is means remove mod from community // if target is set, this is means remove mod from community
@ -56,9 +54,9 @@ pub struct RemoveMod {
impl RemoveMod { impl RemoveMod {
pub async fn send( pub async fn send(
community: &Community, community: &ApubCommunity,
removed_mod: &Person, removed_mod: &ApubPerson,
actor: &Person, actor: &ApubPerson,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id( let id = generate_activity_id(
@ -94,7 +92,7 @@ impl ActivityHandler for RemoveMod {
verify_activity(self, &context.settings())?; verify_activity(self, &context.settings())?;
if let Some(target) = &self.target { if let Some(target) = &self.target {
verify_person_in_community(&self.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.actor, self.cc[0].clone(), context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
verify_add_remove_moderator_target(target, &self.cc[0])?; verify_add_remove_moderator_target(target, &self.cc[0])?;
} else { } else {
verify_delete_activity( verify_delete_activity(

View file

@ -12,6 +12,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::UndoType, activity::kind::UndoType,
@ -26,10 +27,7 @@ use lemmy_apub_lib::{
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{CommunityPersonBan, CommunityPersonBanForm},
community::{Community, CommunityPersonBan, CommunityPersonBanForm},
person::Person,
},
traits::Bannable, traits::Bannable,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -40,10 +38,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoBlockUserFromCommunity { pub struct UndoBlockUserFromCommunity {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
object: BlockUserFromCommunity, object: BlockUserFromCommunity,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url, id: Url,
@ -55,9 +53,9 @@ pub struct UndoBlockUserFromCommunity {
impl UndoBlockUserFromCommunity { impl UndoBlockUserFromCommunity {
pub async fn send( pub async fn send(
community: &Community, community: &ApubCommunity,
target: &Person, target: &ApubPerson,
actor: &Person, actor: &ApubPerson,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let block = BlockUserFromCommunity::new(community, target, actor, context)?; let block = BlockUserFromCommunity::new(community, target, actor, context)?;
@ -93,7 +91,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?; verify_activity(self, &context.settings())?;
verify_person_in_community(&self.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.actor, self.cc[0].clone(), context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }

View file

@ -8,7 +8,10 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::Group, ToApub}, objects::{
community::{ApubCommunity, Group},
person::ApubPerson,
},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::UpdateType, activity::kind::UpdateType,
@ -19,14 +22,11 @@ use activitystreams::{
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
data::Data, data::Data,
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType, ToApub},
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{Community, CommunityForm},
community::{Community, CommunityForm},
person::Person,
},
traits::Crud, traits::Crud,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -39,11 +39,11 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UpdateCommunity { pub struct UpdateCommunity {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
// TODO: would be nice to use a separate struct here, which only contains the fields updated here // TODO: would be nice to use a separate struct here, which only contains the fields updated here
object: Group, object: Group,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UpdateType, kind: UpdateType,
id: Url, id: Url,
@ -55,8 +55,8 @@ pub struct UpdateCommunity {
impl UpdateCommunity { impl UpdateCommunity {
pub async fn send( pub async fn send(
community: &Community, community: &ApubCommunity,
actor: &Person, actor: &ApubPerson,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id( let id = generate_activity_id(
@ -89,7 +89,7 @@ impl ActivityHandler for UpdateCommunity {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?; verify_activity(self, &context.settings())?;
verify_person_in_community(&self.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.actor, self.cc[0].clone(), context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
Ok(()) Ok(())
} }

View file

@ -12,6 +12,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::DeleteType, activity::kind::DeleteType,
@ -38,7 +39,6 @@ use lemmy_db_schema::{
ModRemovePost, ModRemovePost,
ModRemovePostForm, ModRemovePostForm,
}, },
person::Person,
post::Post, post::Post,
}, },
traits::Crud, traits::Crud,
@ -65,10 +65,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Delete { pub struct Delete {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
pub(in crate::activities::deletion) object: Url, pub(in crate::activities::deletion) object: Url,
pub(in crate::activities::deletion) cc: [ObjectId<Community>; 1], pub(in crate::activities::deletion) cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
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
@ -136,8 +136,8 @@ impl ActivityHandler for Delete {
impl Delete { impl Delete {
pub(in crate::activities::deletion) fn new( pub(in crate::activities::deletion) fn new(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
object_id: Url, object_id: Url,
summary: Option<String>, summary: Option<String>,
context: &LemmyContext, context: &LemmyContext,
@ -158,8 +158,8 @@ impl Delete {
}) })
} }
pub(in crate::activities::deletion) async fn send( pub(in crate::activities::deletion) async fn send(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
object_id: Url, object_id: Url,
summary: Option<String>, summary: Option<String>,
context: &LemmyContext, context: &LemmyContext,
@ -173,7 +173,7 @@ impl Delete {
} }
pub(in crate::activities) async fn receive_remove_action( pub(in crate::activities) async fn receive_remove_action(
actor: &ObjectId<Person>, actor: &ObjectId<ApubPerson>,
object: &Url, object: &Url,
reason: Option<String>, reason: Option<String>,
context: &LemmyContext, context: &LemmyContext,

View file

@ -5,14 +5,14 @@ use crate::{
verify_person_in_community, verify_person_in_community,
}, },
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
}; };
use diesel::PgConnection;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
traits::{ActivityFields, ActorType, ApubObject}, traits::{ActivityFields, ActorType, ApubObject},
verify::verify_domains_match, verify::verify_domains_match,
}; };
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{ use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
@ -25,8 +25,8 @@ pub mod delete;
pub mod undo_delete; pub mod undo_delete;
pub async fn send_apub_delete( pub async fn send_apub_delete(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
object_id: Url, object_id: Url,
deleted: bool, deleted: bool,
context: &LemmyContext, context: &LemmyContext,
@ -41,8 +41,8 @@ pub async fn send_apub_delete(
// TODO: remove reason is actually optional in lemmy. we set an empty string in that case, but its // TODO: remove reason is actually optional in lemmy. we set an empty string in that case, but its
// ugly // ugly
pub async fn send_apub_remove( pub async fn send_apub_remove(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
object_id: Url, object_id: Url,
reason: String, reason: String,
removed: bool, removed: bool,
@ -56,9 +56,9 @@ pub async fn send_apub_remove(
} }
pub enum DeletableObjects { pub enum DeletableObjects {
Community(Box<Community>), Community(Box<ApubCommunity>),
Comment(Box<Comment>), Comment(Box<ApubComment>),
Post(Box<Post>), Post(Box<ApubPost>),
} }
impl DeletableObjects { impl DeletableObjects {
@ -66,39 +66,23 @@ impl DeletableObjects {
ap_id: &Url, ap_id: &Url,
context: &LemmyContext, context: &LemmyContext,
) -> Result<DeletableObjects, LemmyError> { ) -> Result<DeletableObjects, LemmyError> {
if let Some(c) = if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
DeletableObjects::read_type_from_db::<Community>(ap_id.clone(), context).await?
{
return Ok(DeletableObjects::Community(Box::new(c))); return Ok(DeletableObjects::Community(Box::new(c)));
} }
if let Some(p) = DeletableObjects::read_type_from_db::<Post>(ap_id.clone(), context).await? { if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Post(Box::new(p))); return Ok(DeletableObjects::Post(Box::new(p)));
} }
if let Some(c) = DeletableObjects::read_type_from_db::<Comment>(ap_id.clone(), context).await? { if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Comment(Box::new(c))); return Ok(DeletableObjects::Comment(Box::new(c)));
} }
Err(diesel::NotFound.into()) Err(diesel::NotFound.into())
} }
// TODO: a method like this should be provided by fetcher module
async fn read_type_from_db<Type>(
ap_id: Url,
context: &LemmyContext,
) -> Result<Option<Type>, LemmyError>
where
Type: ApubObject<DataType = PgConnection> + Send + 'static,
{
blocking(context.pool(), move |conn| {
Type::read_from_apub_id(conn, ap_id)
})
.await?
}
} }
pub(in crate::activities) async fn verify_delete_activity( pub(in crate::activities) async fn verify_delete_activity(
object: &Url, object: &Url,
activity: &dyn ActivityFields, activity: &dyn ActivityFields,
community_id: &ObjectId<Community>, community_id: &ObjectId<ApubCommunity>,
is_mod_action: bool, is_mod_action: bool,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
@ -115,7 +99,7 @@ pub(in crate::activities) async fn verify_delete_activity(
// community deletion is always a mod (or admin) action // community deletion is always a mod (or admin) action
verify_mod_action( verify_mod_action(
&actor, &actor,
ObjectId::new(c.actor_id()), &ObjectId::new(c.actor_id()),
context, context,
request_counter, request_counter,
) )
@ -124,7 +108,7 @@ pub(in crate::activities) async fn verify_delete_activity(
DeletableObjects::Post(p) => { DeletableObjects::Post(p) => {
verify_delete_activity_post_or_comment( verify_delete_activity_post_or_comment(
activity, activity,
&p.ap_id.into(), &p.ap_id.clone().into(),
community_id, community_id,
is_mod_action, is_mod_action,
context, context,
@ -135,7 +119,7 @@ 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(
activity, activity,
&c.ap_id.into(), &c.ap_id.clone().into(),
community_id, community_id,
is_mod_action, is_mod_action,
context, context,
@ -150,7 +134,7 @@ pub(in crate::activities) async fn verify_delete_activity(
async fn verify_delete_activity_post_or_comment( async fn verify_delete_activity_post_or_comment(
activity: &dyn ActivityFields, activity: &dyn ActivityFields,
object_id: &Url, object_id: &Url,
community_id: &ObjectId<Community>, community_id: &ObjectId<ApubCommunity>,
is_mod_action: bool, is_mod_action: bool,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
@ -158,7 +142,7 @@ async fn verify_delete_activity_post_or_comment(
let actor = ObjectId::new(activity.actor().clone()); let actor = ObjectId::new(activity.actor().clone());
verify_person_in_community(&actor, community_id, context, request_counter).await?; verify_person_in_community(&actor, community_id, context, request_counter).await?;
if is_mod_action { if is_mod_action {
verify_mod_action(&actor, community_id.clone(), context, request_counter).await?; verify_mod_action(&actor, community_id, context, request_counter).await?;
} else { } else {
// domain of post ap_id and post.creator ap_id are identical, so we just check the former // domain of post ap_id and post.creator ap_id are identical, so we just check the former
verify_domains_match(activity.actor(), object_id)?; verify_domains_match(activity.actor(), object_id)?;
@ -177,7 +161,7 @@ struct WebsocketMessages {
/// because of the mod log /// because of the mod log
async fn receive_delete_action( async fn receive_delete_action(
object: &Url, object: &Url,
actor: &ObjectId<Person>, actor: &ObjectId<ApubPerson>,
ws_messages: WebsocketMessages, ws_messages: WebsocketMessages,
deleted: bool, deleted: bool,
context: &LemmyContext, context: &LemmyContext,

View file

@ -13,6 +13,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::UndoType, activity::kind::UndoType,
@ -27,7 +28,7 @@ use lemmy_apub_lib::{
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType},
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{ use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message}, send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
@ -40,10 +41,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoDelete { pub struct UndoDelete {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
object: Delete, object: Delete,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url, id: Url,
@ -102,8 +103,8 @@ impl ActivityHandler for UndoDelete {
impl UndoDelete { impl UndoDelete {
pub(in crate::activities::deletion) async fn send( pub(in crate::activities::deletion) async fn send(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
object_id: Url, object_id: Url,
summary: Option<String>, summary: Option<String>,
context: &LemmyContext, context: &LemmyContext,

View file

@ -7,6 +7,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity, send_lemmy_activity,
}; };
use activitystreams::{ use activitystreams::{
@ -21,13 +22,7 @@ use lemmy_apub_lib::{
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType},
verify::verify_urls_match, verify::verify_urls_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
source::{
community::{Community, CommunityFollower},
person::Person,
},
traits::Followable,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -36,8 +31,8 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AcceptFollowCommunity { pub struct AcceptFollowCommunity {
actor: ObjectId<Community>, actor: ObjectId<ApubCommunity>,
to: ObjectId<Person>, to: ObjectId<ApubPerson>,
object: FollowCommunity, object: FollowCommunity,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: AcceptType, kind: AcceptType,
@ -72,7 +67,7 @@ impl AcceptFollowCommunity {
context: lemmy_context(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
}; };
let inbox = vec![person.inbox_url.into()]; let inbox = vec![person.inbox_url()];
send_lemmy_activity(context, &accept, &accept.id, &community, inbox, true).await send_lemmy_activity(context, &accept, &accept.id, &community, inbox, true).await
} }
} }

View file

@ -7,6 +7,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity, send_lemmy_activity,
}; };
use activitystreams::{ use activitystreams::{
@ -22,10 +23,7 @@ use lemmy_apub_lib::{
verify::verify_urls_match, verify::verify_urls_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{CommunityFollower, CommunityFollowerForm},
community::{Community, CommunityFollower, CommunityFollowerForm},
person::Person,
},
traits::Followable, traits::Followable,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -36,10 +34,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct FollowCommunity { pub struct FollowCommunity {
pub(in crate::activities::following) actor: ObjectId<Person>, pub(in crate::activities::following) actor: ObjectId<ApubPerson>,
// TODO: is there any reason to put the same community id twice, in to and object? // TODO: is there any reason to put the same community id twice, in to and object?
pub(in crate::activities::following) to: ObjectId<Community>, pub(in crate::activities::following) to: ObjectId<ApubCommunity>,
pub(in crate::activities::following) object: ObjectId<Community>, pub(in crate::activities::following) object: ObjectId<ApubCommunity>,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: FollowType, kind: FollowType,
id: Url, id: Url,
@ -51,8 +49,8 @@ pub struct FollowCommunity {
impl FollowCommunity { impl FollowCommunity {
pub(in crate::activities::following) fn new( pub(in crate::activities::following) fn new(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
context: &LemmyContext, context: &LemmyContext,
) -> Result<FollowCommunity, LemmyError> { ) -> Result<FollowCommunity, LemmyError> {
Ok(FollowCommunity { Ok(FollowCommunity {
@ -69,8 +67,8 @@ impl FollowCommunity {
}) })
} }
pub async fn send( pub async fn send(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {

View file

@ -7,6 +7,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity, send_lemmy_activity,
}; };
use activitystreams::{ use activitystreams::{
@ -22,10 +23,7 @@ use lemmy_apub_lib::{
verify::verify_urls_match, verify::verify_urls_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{CommunityFollower, CommunityFollowerForm},
community::{Community, CommunityFollower, CommunityFollowerForm},
person::Person,
},
traits::Followable, traits::Followable,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -36,8 +34,8 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoFollowCommunity { pub struct UndoFollowCommunity {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: ObjectId<Community>, to: ObjectId<ApubCommunity>,
object: FollowCommunity, object: FollowCommunity,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
@ -50,8 +48,8 @@ pub struct UndoFollowCommunity {
impl UndoFollowCommunity { impl UndoFollowCommunity {
pub async fn send( pub async fn send(
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let object = FollowCommunity::new(actor, community, context)?; let object = FollowCommunity::new(actor, community, context)?;

View file

@ -3,15 +3,17 @@ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
generate_moderators_url, generate_moderators_url,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match}; use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match};
use lemmy_db_schema::source::{community::Community, person::Person}; use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::community_view::CommunityView; use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_utils::{settings::structs::Settings, LemmyError}; use lemmy_utils::{settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ops::Deref;
use strum_macros::ToString; use strum_macros::ToString;
use url::{ParseError, Url}; use url::{ParseError, Url};
use uuid::Uuid; use uuid::Uuid;
@ -35,7 +37,7 @@ pub enum CreateOrUpdateType {
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person /// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
/// doesn't have a site ban. /// doesn't have a site ban.
async fn verify_person( async fn verify_person(
person_id: &ObjectId<Person>, person_id: &ObjectId<ApubPerson>,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
@ -50,7 +52,7 @@ pub(crate) async fn extract_community(
cc: &[Url], cc: &[Url],
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Community, LemmyError> { ) -> Result<ApubCommunity, LemmyError> {
let mut cc_iter = cc.iter(); let mut cc_iter = cc.iter();
loop { loop {
if let Some(cid) = cc_iter.next() { if let Some(cid) = cc_iter.next() {
@ -67,19 +69,19 @@ pub(crate) async fn extract_community(
/// Fetches the person and community to verify their type, then checks if person is banned from site /// Fetches the person and community to verify their type, then checks if person is banned from site
/// or community. /// or community.
pub(crate) async fn verify_person_in_community( pub(crate) async fn verify_person_in_community(
person_id: &ObjectId<Person>, person_id: &ObjectId<ApubPerson>,
community_id: &ObjectId<Community>, community_id: &ObjectId<ApubCommunity>,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = community_id.dereference(context, request_counter).await?; let community = community_id.dereference(context, request_counter).await?;
let person = person_id.dereference(context, request_counter).await?; let person = person_id.dereference(context, request_counter).await?;
check_community_or_site_ban(&person, community.id, context.pool()).await check_community_or_site_ban(person.deref(), community.id, context.pool()).await
} }
/// Simply check that the url actually refers to a valid group. /// Simply check that the url actually refers to a valid group.
async fn verify_community( async fn verify_community(
community_id: &ObjectId<Community>, community_id: &ObjectId<ApubCommunity>,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
@ -97,8 +99,8 @@ fn verify_activity(activity: &dyn ActivityFields, settings: &Settings) -> Result
/// because in case of remote communities, admins can also perform mod actions. As admin status /// because in case of remote communities, admins can also perform mod actions. As admin status
/// is not federated, we cant verify their actions remotely. /// is not federated, we cant verify their actions remotely.
pub(crate) async fn verify_mod_action( pub(crate) async fn verify_mod_action(
actor_id: &ObjectId<Person>, actor_id: &ObjectId<ApubPerson>,
community_id: ObjectId<Community>, community_id: &ObjectId<ApubCommunity>,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
@ -126,7 +128,7 @@ pub(crate) async fn verify_mod_action(
/// /c/community/moderators. Any different values are unsupported. /// /c/community/moderators. Any different values are unsupported.
fn verify_add_remove_moderator_target( fn verify_add_remove_moderator_target(
target: &Url, target: &Url,
community: &ObjectId<Community>, community: &ObjectId<ApubCommunity>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if target != &generate_moderators_url(&community.clone().into())?.into_inner() { if target != &generate_moderators_url(&community.clone().into())?.into_inner() {
return Err(anyhow!("Unkown target url").into()); return Err(anyhow!("Unkown target url").into());

View file

@ -10,21 +10,22 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{post::Page, FromApub, ToApub}, objects::{
community::ApubCommunity,
person::ApubPerson,
post::{ApubPost, Page},
},
}; };
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
data::Data, data::Data,
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType, FromApub, ToApub},
values::PublicUrl, values::PublicUrl,
verify::{verify_domains_match, verify_urls_match}, verify::{verify_domains_match, verify_urls_match},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{source::community::Community, traits::Crud};
source::{community::Community, person::Person, post::Post},
traits::Crud,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -33,10 +34,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CreateOrUpdatePost { pub struct CreateOrUpdatePost {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
object: Page, object: Page,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
id: Url, id: Url,
@ -48,16 +49,17 @@ pub struct CreateOrUpdatePost {
impl CreateOrUpdatePost { impl CreateOrUpdatePost {
pub async fn send( pub async fn send(
post: &Post, post: &ApubPost,
actor: &Person, actor: &ApubPerson,
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community_id = post.community_id; let community_id = post.community_id;
let community = blocking(context.pool(), move |conn| { let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??
.into();
let id = generate_activity_id( let id = generate_activity_id(
kind.clone(), kind.clone(),
@ -109,7 +111,7 @@ impl ActivityHandler for CreateOrUpdatePost {
CreateOrUpdateType::Update => { CreateOrUpdateType::Update => {
let is_mod_action = self.object.is_mod_action(context).await?; let is_mod_action = self.object.is_mod_action(context).await?;
if is_mod_action { if is_mod_action {
verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?; verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
} else { } else {
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?; verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
verify_urls_match(self.actor(), self.object.attributed_to.inner())?; verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
@ -126,7 +128,8 @@ impl ActivityHandler for CreateOrUpdatePost {
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let actor = self.actor.dereference(context, request_counter).await?; let actor = self.actor.dereference(context, request_counter).await?;
let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?; let post =
ApubPost::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
let notif_type = match self.kind { let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreatePost, CreateOrUpdateType::Create => UserOperationCrud::CreatePost,

View file

@ -2,20 +2,20 @@ use crate::{
activities::{generate_activity_id, verify_activity, verify_person, CreateOrUpdateType}, activities::{generate_activity_id, verify_activity, verify_person, CreateOrUpdateType},
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{private_message::Note, FromApub, ToApub}, objects::{
person::ApubPerson,
private_message::{ApubPrivateMessage, Note},
},
send_lemmy_activity, send_lemmy_activity,
}; };
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
data::Data, data::Data,
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType, FromApub, ToApub},
verify::verify_domains_match, verify::verify_domains_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{source::person::Person, traits::Crud};
source::{person::Person, private_message::PrivateMessage},
traits::Crud,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -27,8 +27,8 @@ pub struct CreateOrUpdatePrivateMessage {
#[serde(rename = "@context")] #[serde(rename = "@context")]
pub context: OneOrMany<AnyBase>, pub context: OneOrMany<AnyBase>,
id: Url, id: Url,
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: ObjectId<Person>, to: ObjectId<ApubPerson>,
object: Note, object: Note,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
@ -38,14 +38,16 @@ pub struct CreateOrUpdatePrivateMessage {
impl CreateOrUpdatePrivateMessage { impl CreateOrUpdatePrivateMessage {
pub async fn send( pub async fn send(
private_message: &PrivateMessage, private_message: &ApubPrivateMessage,
actor: &Person, actor: &ApubPerson,
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let recipient_id = private_message.recipient_id; let recipient_id = private_message.recipient_id;
let recipient = let recipient: ApubPerson =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
.await??
.into();
let id = generate_activity_id( let id = generate_activity_id(
kind.clone(), kind.clone(),
@ -85,7 +87,8 @@ 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.actor.inner(), request_counter).await?; ApubPrivateMessage::from_apub(&self.object, context, self.actor.inner(), request_counter)
.await?;
let notif_type = match self.kind { let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage, CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,

View file

@ -2,6 +2,7 @@ use crate::{
activities::{generate_activity_id, verify_activity, verify_person}, activities::{generate_activity_id, verify_activity, verify_person},
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
send_lemmy_activity, send_lemmy_activity,
}; };
use activitystreams::{ use activitystreams::{
@ -28,9 +29,9 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DeletePrivateMessage { pub struct DeletePrivateMessage {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: ObjectId<Person>, to: ObjectId<ApubPerson>,
pub(in crate::activities::private_message) object: ObjectId<PrivateMessage>, pub(in crate::activities::private_message) object: ObjectId<ApubPrivateMessage>,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: DeleteType, kind: DeleteType,
id: Url, id: Url,
@ -42,7 +43,7 @@ pub struct DeletePrivateMessage {
impl DeletePrivateMessage { impl DeletePrivateMessage {
pub(in crate::activities::private_message) fn new( pub(in crate::activities::private_message) fn new(
actor: &Person, actor: &ApubPerson,
pm: &PrivateMessage, pm: &PrivateMessage,
context: &LemmyContext, context: &LemmyContext,
) -> Result<DeletePrivateMessage, LemmyError> { ) -> Result<DeletePrivateMessage, LemmyError> {
@ -60,16 +61,18 @@ impl DeletePrivateMessage {
}) })
} }
pub async fn send( pub async fn send(
actor: &Person, actor: &ApubPerson,
pm: &PrivateMessage, pm: &ApubPrivateMessage,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let delete = DeletePrivateMessage::new(actor, pm, context)?; let delete = DeletePrivateMessage::new(actor, pm, context)?;
let delete_id = delete.id.clone(); let delete_id = delete.id.clone();
let recipient_id = pm.recipient_id; let recipient_id = pm.recipient_id;
let recipient = let recipient: ApubPerson =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
.await??
.into();
let inbox = vec![recipient.shared_inbox_or_inbox_url()]; let inbox = vec![recipient.shared_inbox_or_inbox_url()];
send_lemmy_activity(context, &delete, &delete_id, actor, inbox, true).await send_lemmy_activity(context, &delete, &delete_id, actor, inbox, true).await
} }

View file

@ -7,6 +7,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
send_lemmy_activity, send_lemmy_activity,
}; };
use activitystreams::{ use activitystreams::{
@ -33,8 +34,8 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoDeletePrivateMessage { pub struct UndoDeletePrivateMessage {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: ObjectId<Person>, to: ObjectId<ApubPerson>,
object: DeletePrivateMessage, object: DeletePrivateMessage,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
@ -47,13 +48,15 @@ pub struct UndoDeletePrivateMessage {
impl UndoDeletePrivateMessage { impl UndoDeletePrivateMessage {
pub async fn send( pub async fn send(
actor: &Person, actor: &ApubPerson,
pm: &PrivateMessage, pm: &ApubPrivateMessage,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let recipient_id = pm.recipient_id; let recipient_id = pm.recipient_id;
let recipient = let recipient: ApubPerson =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
.await??
.into();
let object = DeletePrivateMessage::new(actor, pm, context)?; let object = DeletePrivateMessage::new(actor, pm, context)?;
let id = generate_activity_id( let id = generate_activity_id(

View file

@ -2,6 +2,7 @@ use crate::{
activities::{generate_activity_id, verify_activity, verify_person_in_community}, activities::{generate_activity_id, verify_activity, verify_person_in_community},
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity, send_lemmy_activity,
PostOrComment, PostOrComment,
}; };
@ -17,14 +18,11 @@ use lemmy_apub_lib::{
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::CommunityId,
source::{ source::{
comment_report::{CommentReport, CommentReportForm}, comment_report::{CommentReport, CommentReportForm},
community::Community,
person::Person,
post_report::{PostReport, PostReportForm}, post_report::{PostReport, PostReportForm},
}, },
traits::{Crud, Reportable}, traits::Reportable,
}; };
use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView}; use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -35,8 +33,8 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Report { pub struct Report {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [ObjectId<Community>; 1], to: [ObjectId<ApubCommunity>; 1],
object: ObjectId<PostOrComment>, object: ObjectId<PostOrComment>,
summary: String, summary: String,
#[serde(rename = "type")] #[serde(rename = "type")]
@ -51,15 +49,12 @@ pub struct Report {
impl Report { impl Report {
pub async fn send( pub async fn send(
object_id: ObjectId<PostOrComment>, object_id: ObjectId<PostOrComment>,
actor: &Person, actor: &ApubPerson,
community_id: CommunityId, community_id: ObjectId<ApubCommunity>,
reason: String, reason: String,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = blocking(context.pool(), move |conn| { let community = community_id.dereference_local(context).await?;
Community::read(conn, community_id)
})
.await??;
let kind = FlagType::Flag; let kind = FlagType::Flag;
let id = generate_activity_id( let id = generate_activity_id(
kind.clone(), kind.clone(),
@ -111,10 +106,10 @@ impl ActivityHandler for Report {
let report_form = PostReportForm { let report_form = PostReportForm {
creator_id: actor.id, creator_id: actor.id,
post_id: post.id, post_id: post.id,
original_post_name: post.name, original_post_name: post.name.clone(),
original_post_url: post.url, original_post_url: post.url.clone(),
reason: self.summary, reason: self.summary,
original_post_body: post.body, original_post_body: post.body.clone(),
}; };
let report = blocking(context.pool(), move |conn| { let report = blocking(context.pool(), move |conn| {
@ -138,7 +133,7 @@ impl ActivityHandler for Report {
let report_form = CommentReportForm { let report_form = CommentReportForm {
creator_id: actor.id, creator_id: actor.id,
comment_id: comment.id, comment_id: comment.id,
original_comment_text: comment.content, original_comment_text: comment.content.clone(),
reason: self.summary, reason: self.summary,
}; };

View file

@ -5,6 +5,7 @@ use crate::{
verify_activity, verify_activity,
}, },
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
}; };
use activitystreams::{ use activitystreams::{
activity::kind::UndoType, activity::kind::UndoType,
@ -17,7 +18,6 @@ use lemmy_apub_lib::{
traits::{ActivityFields, ActivityHandler}, traits::{ActivityFields, ActivityHandler},
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::source::{community::Community, person::Person};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -26,11 +26,11 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoRemovePostCommentOrCommunity { pub struct UndoRemovePostCommentOrCommunity {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
// 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: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url, id: Url,

View file

@ -1,10 +1,12 @@
use crate::activities::voting::vote::VoteType; use crate::{
activities::voting::vote::VoteType,
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::{Comment, CommentLike, CommentLikeForm}, comment::{CommentLike, CommentLikeForm},
person::Person, post::{PostLike, PostLikeForm},
post::{Post, PostLike, PostLikeForm},
}, },
traits::Likeable, traits::Likeable,
}; };
@ -20,8 +22,8 @@ pub mod vote;
async fn vote_comment( async fn vote_comment(
vote_type: &VoteType, vote_type: &VoteType,
actor: Person, actor: ApubPerson,
comment: &Comment, comment: &ApubComment,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let comment_id = comment.id; let comment_id = comment.id;
@ -44,8 +46,8 @@ async fn vote_comment(
async fn vote_post( async fn vote_post(
vote_type: &VoteType, vote_type: &VoteType,
actor: Person, actor: ApubPerson,
post: &Post, post: &ApubPost,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let post_id = post.id; let post_id = post.id;
@ -66,8 +68,8 @@ async fn vote_post(
} }
async fn undo_vote_comment( async fn undo_vote_comment(
actor: Person, actor: ApubPerson,
comment: &Comment, comment: &ApubComment,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let comment_id = comment.id; let comment_id = comment.id;
@ -82,8 +84,8 @@ async fn undo_vote_comment(
} }
async fn undo_vote_post( async fn undo_vote_post(
actor: Person, actor: ApubPerson,
post: &Post, post: &ApubPost,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let post_id = post.id; let post_id = post.id;

View file

@ -12,6 +12,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
PostOrComment, PostOrComment,
}; };
use activitystreams::{ use activitystreams::{
@ -27,11 +28,7 @@ use lemmy_apub_lib::{
values::PublicUrl, values::PublicUrl,
verify::verify_urls_match, verify::verify_urls_match,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
newtypes::CommunityId,
source::{community::Community, person::Person},
traits::Crud,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -41,10 +38,10 @@ use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoVote { pub struct UndoVote {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
object: Vote, object: Vote,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url, id: Url,
@ -57,15 +54,16 @@ pub struct UndoVote {
impl UndoVote { impl UndoVote {
pub async fn send( pub async fn send(
object: &PostOrComment, object: &PostOrComment,
actor: &Person, actor: &ApubPerson,
community_id: CommunityId, community_id: CommunityId,
kind: VoteType, kind: VoteType,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = blocking(context.pool(), move |conn| { let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??
.into();
let object = Vote::new(object, actor, &community, kind.clone(), context)?; let object = Vote::new(object, actor, &community, kind.clone(), context)?;
let id = generate_activity_id( let id = generate_activity_id(

View file

@ -8,6 +8,7 @@ use crate::{
}, },
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
PostOrComment, PostOrComment,
}; };
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
@ -18,11 +19,7 @@ use lemmy_apub_lib::{
traits::{ActivityFields, ActivityHandler, ActorType}, traits::{ActivityFields, ActivityHandler, ActorType},
values::PublicUrl, values::PublicUrl,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
newtypes::CommunityId,
source::{community::Community, person::Person},
traits::Crud,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -60,10 +57,10 @@ impl From<&VoteType> for i16 {
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Vote { pub struct Vote {
actor: ObjectId<Person>, actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1], to: [PublicUrl; 1],
pub(in crate::activities::voting) object: ObjectId<PostOrComment>, pub(in crate::activities::voting) object: ObjectId<PostOrComment>,
cc: [ObjectId<Community>; 1], cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
pub(in crate::activities::voting) kind: VoteType, pub(in crate::activities::voting) kind: VoteType,
id: Url, id: Url,
@ -76,8 +73,8 @@ pub struct Vote {
impl Vote { impl Vote {
pub(in crate::activities::voting) fn new( pub(in crate::activities::voting) fn new(
object: &PostOrComment, object: &PostOrComment,
actor: &Person, actor: &ApubPerson,
community: &Community, community: &ApubCommunity,
kind: VoteType, kind: VoteType,
context: &LemmyContext, context: &LemmyContext,
) -> Result<Vote, LemmyError> { ) -> Result<Vote, LemmyError> {
@ -95,7 +92,7 @@ impl Vote {
pub async fn send( pub async fn send(
object: &PostOrComment, object: &PostOrComment,
actor: &Person, actor: &ApubPerson,
community_id: CommunityId, community_id: CommunityId,
kind: VoteType, kind: VoteType,
context: &LemmyContext, context: &LemmyContext,
@ -103,7 +100,8 @@ impl Vote {
let community = blocking(context.pool(), move |conn| { let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .await??
.into();
let vote = Vote::new(object, actor, &community, kind, context)?; let vote = Vote::new(object, actor, &community, kind, context)?;
let vote_id = vote.id.clone(); let vote_id = vote.id.clone();

View file

@ -1,17 +1,14 @@
use crate::{ use crate::{
activities::community::announce::AnnounceActivity, activities::community::announce::AnnounceActivity,
fetcher::{fetch::fetch_remote_object, object_id::ObjectId}, fetcher::{fetch::fetch_remote_object, object_id::ObjectId},
objects::community::Group, objects::{community::Group, person::ApubPerson},
}; };
use activitystreams::collection::{CollectionExt, OrderedCollection}; use activitystreams::collection::{CollectionExt, OrderedCollection};
use anyhow::Context; use anyhow::Context;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{data::Data, traits::ActivityHandler}; use lemmy_apub_lib::{data::Data, traits::ActivityHandler};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{Community, CommunityModerator, CommunityModeratorForm},
community::{Community, CommunityModerator, CommunityModeratorForm},
person::Person,
},
traits::Joinable, traits::Joinable,
}; };
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
@ -48,7 +45,7 @@ pub(crate) async fn update_community_mods(
// Add new mods to database which have been added to moderators collection // Add new mods to database which have been added to moderators collection
for mod_id in new_moderators { for mod_id in new_moderators {
let mod_id = ObjectId::new(mod_id); let mod_id = ObjectId::new(mod_id);
let mod_user: Person = mod_id.dereference(context, request_counter).await?; let mod_user: ApubPerson = mod_id.dereference(context, request_counter).await?;
if !current_moderators if !current_moderators
.clone() .clone()

View file

@ -4,13 +4,13 @@ pub mod object_id;
pub mod post_or_comment; pub mod post_or_comment;
pub mod search; pub mod search;
use crate::fetcher::object_id::ObjectId; use crate::{
fetcher::object_id::ObjectId,
objects::{community::ApubCommunity, person::ApubPerson},
};
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use lemmy_apub_lib::traits::ActorType; use lemmy_apub_lib::traits::ActorType;
use lemmy_db_schema::{ use lemmy_db_schema::naive_now;
naive_now,
source::{community::Community, person::Person},
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
@ -28,13 +28,13 @@ pub(crate) async fn get_or_fetch_and_upsert_actor(
context: &LemmyContext, context: &LemmyContext,
recursion_counter: &mut i32, recursion_counter: &mut i32,
) -> Result<Box<dyn ActorType>, LemmyError> { ) -> Result<Box<dyn ActorType>, LemmyError> {
let community_id = ObjectId::<Community>::new(apub_id.clone()); let community_id = ObjectId::<ApubCommunity>::new(apub_id.clone());
let community = community_id.dereference(context, recursion_counter).await; let community = community_id.dereference(context, recursion_counter).await;
let actor: Box<dyn ActorType> = match community { let actor: Box<dyn ActorType> = match community {
Ok(c) => Box::new(c), Ok(c) => Box::new(c),
Err(_) => { Err(_) => {
let person_id = ObjectId::new(apub_id); let person_id = ObjectId::new(apub_id);
let person: Person = person_id.dereference(context, recursion_counter).await?; let person: ApubPerson = person_id.dereference(context, recursion_counter).await?;
Box::new(person) Box::new(person)
} }
}; };

View file

@ -1,9 +1,11 @@
use crate::{fetcher::should_refetch_actor, objects::FromApub}; use crate::fetcher::should_refetch_actor;
use anyhow::anyhow; use anyhow::anyhow;
use diesel::{NotFound, PgConnection}; use diesel::NotFound;
use lemmy_api_common::blocking; use lemmy_apub_lib::{
use lemmy_apub_lib::{traits::ApubObject, APUB_JSON_CONTENT_TYPE}; traits::{ApubObject, FromApub},
use lemmy_db_schema::{newtypes::DbUrl, DbPool}; APUB_JSON_CONTENT_TYPE,
};
use lemmy_db_schema::newtypes::DbUrl;
use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError}; use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use reqwest::StatusCode; use reqwest::StatusCode;
@ -22,12 +24,12 @@ static REQUEST_LIMIT: i32 = 25;
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
pub struct ObjectId<Kind>(Url, #[serde(skip)] PhantomData<Kind>) pub struct ObjectId<Kind>(Url, #[serde(skip)] PhantomData<Kind>)
where where
Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static, Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de2> <Kind as FromApub>::ApubType: serde::Deserialize<'de2>; for<'de2> <Kind as FromApub>::ApubType: serde::Deserialize<'de2>;
impl<Kind> ObjectId<Kind> impl<Kind> ObjectId<Kind>
where where
Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static, Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>, for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{ {
pub fn new<T>(url: T) -> Self pub fn new<T>(url: T) -> Self
@ -47,7 +49,7 @@ where
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Kind, LemmyError> { ) -> Result<Kind, LemmyError> {
let db_object = self.dereference_from_db(context.pool()).await?; let db_object = self.dereference_from_db(context).await?;
// if its a local object, only fetch it from the database and not over http // if its a local object, only fetch it from the database and not over http
if self.0.domain() == Some(&Settings::get().get_hostname_without_port()?) { if self.0.domain() == Some(&Settings::get().get_hostname_without_port()?) {
@ -77,14 +79,14 @@ where
/// Fetch an object from the local db. Instead of falling back to http, this throws an error if /// Fetch an object from the local db. Instead of falling back to http, this throws an error if
/// the object is not found in the database. /// the object is not found in the database.
pub async fn dereference_local(&self, context: &LemmyContext) -> Result<Kind, LemmyError> { pub async fn dereference_local(&self, context: &LemmyContext) -> Result<Kind, LemmyError> {
let object = self.dereference_from_db(context.pool()).await?; let object = self.dereference_from_db(context).await?;
object.ok_or_else(|| anyhow!("object not found in database {}", self).into()) object.ok_or_else(|| anyhow!("object not found in database {}", self).into())
} }
/// returning none means the object was not found in local db /// returning none means the object was not found in local db
async fn dereference_from_db(&self, pool: &DbPool) -> Result<Option<Kind>, LemmyError> { async fn dereference_from_db(&self, context: &LemmyContext) -> Result<Option<Kind>, LemmyError> {
let id = self.0.clone(); let id = self.0.clone();
blocking(pool, move |conn| ApubObject::read_from_apub_id(conn, id)).await? ApubObject::read_from_apub_id(id, context).await
} }
async fn dereference_from_http( async fn dereference_from_http(
@ -113,7 +115,7 @@ where
if res.status() == StatusCode::GONE { if res.status() == StatusCode::GONE {
if let Some(db_object) = db_object { if let Some(db_object) = db_object {
blocking(context.pool(), move |conn| db_object.delete(conn)).await??; db_object.delete(context).await?;
} }
return Err(anyhow!("Fetched remote object {} which was deleted", self).into()); return Err(anyhow!("Fetched remote object {} which was deleted", self).into());
} }
@ -126,7 +128,7 @@ where
impl<Kind> Display for ObjectId<Kind> impl<Kind> Display for ObjectId<Kind>
where where
Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static, Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>, for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{ {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
@ -136,7 +138,7 @@ where
impl<Kind> From<ObjectId<Kind>> for Url impl<Kind> From<ObjectId<Kind>> for Url
where where
Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static, Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>, for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{ {
fn from(id: ObjectId<Kind>) -> Self { fn from(id: ObjectId<Kind>) -> Self {
@ -146,7 +148,7 @@ where
impl<Kind> From<ObjectId<Kind>> for DbUrl impl<Kind> From<ObjectId<Kind>> for DbUrl
where where
Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static, Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>, for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{ {
fn from(id: ObjectId<Kind>) -> Self { fn from(id: ObjectId<Kind>) -> Self {

View file

@ -1,11 +1,10 @@
use crate::objects::{comment::Note, post::Page, FromApub}; use crate::objects::{
use activitystreams::chrono::NaiveDateTime; comment::{ApubComment, Note},
use diesel::PgConnection; post::{ApubPost, Page},
use lemmy_apub_lib::traits::ApubObject;
use lemmy_db_schema::source::{
comment::{Comment, CommentForm},
post::{Post, PostForm},
}; };
use activitystreams::chrono::NaiveDateTime;
use lemmy_apub_lib::traits::{ApubObject, FromApub};
use lemmy_db_schema::source::{comment::CommentForm, post::PostForm};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Deserialize; use serde::Deserialize;
@ -13,8 +12,8 @@ use url::Url;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PostOrComment { pub enum PostOrComment {
Post(Box<Post>), Post(Box<ApubPost>),
Comment(Comment), Comment(ApubComment),
} }
pub enum PostOrCommentForm { pub enum PostOrCommentForm {
@ -31,28 +30,33 @@ pub enum PageOrNote {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ApubObject for PostOrComment { impl ApubObject for PostOrComment {
type DataType = PgConnection; type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> { fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None None
} }
// TODO: this can probably be implemented using a single sql query // TODO: this can probably be implemented using a single sql query
fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> async fn read_from_apub_id(
object_id: Url,
data: &Self::DataType,
) -> Result<Option<Self>, LemmyError>
where where
Self: Sized, Self: Sized,
{ {
let post = Post::read_from_apub_id(conn, object_id.clone())?; let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?;
Ok(match post { Ok(match post {
Some(o) => Some(PostOrComment::Post(Box::new(o))), Some(o) => Some(PostOrComment::Post(Box::new(o))),
None => Comment::read_from_apub_id(conn, object_id)?.map(PostOrComment::Comment), None => ApubComment::read_from_apub_id(object_id, data)
.await?
.map(PostOrComment::Comment),
}) })
} }
fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> { async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
match self { match self {
PostOrComment::Post(p) => p.delete(data), PostOrComment::Post(p) => p.delete(data).await,
PostOrComment::Comment(c) => c.delete(data), PostOrComment::Comment(c) => c.delete(data).await,
} }
} }
} }
@ -60,6 +64,7 @@ impl ApubObject for PostOrComment {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for PostOrComment { impl FromApub for PostOrComment {
type ApubType = PageOrNote; type ApubType = PageOrNote;
type DataType = LemmyContext;
async fn from_apub( async fn from_apub(
apub: &PageOrNote, apub: &PageOrNote,
@ -72,10 +77,10 @@ impl FromApub for PostOrComment {
{ {
Ok(match apub { Ok(match apub {
PageOrNote::Page(p) => PostOrComment::Post(Box::new( PageOrNote::Page(p) => PostOrComment::Post(Box::new(
Post::from_apub(p, context, expected_domain, request_counter).await?, ApubPost::from_apub(p, context, expected_domain, request_counter).await?,
)), )),
PageOrNote::Note(n) => PostOrComment::Comment( PageOrNote::Note(n) => PostOrComment::Comment(
Comment::from_apub(n, context, expected_domain, request_counter).await?, ApubComment::from_apub(n, context, expected_domain, request_counter).await?,
), ),
}) })
} }

View file

@ -1,18 +1,22 @@
use crate::{ use crate::{
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{comment::Note, community::Group, person::Person as ApubPerson, post::Page, FromApub}, objects::{
comment::{ApubComment, Note},
community::{ApubCommunity, Group},
person::{ApubPerson, Person},
post::{ApubPost, Page},
},
}; };
use activitystreams::chrono::NaiveDateTime; use activitystreams::chrono::NaiveDateTime;
use anyhow::anyhow; use anyhow::anyhow;
use diesel::PgConnection;
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
traits::ApubObject, traits::{ApubObject, FromApub},
webfinger::{webfinger_resolve_actor, WebfingerType}, webfinger::{webfinger_resolve_actor, WebfingerType},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{comment::Comment, community::Community, person::Person, post::Post}, source::{community::Community, person::Person as DbPerson},
DbPool, DbPool,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -73,10 +77,14 @@ async fn find_local_actor_by_name(
let name: String = name.into(); let name: String = name.into();
Ok(match kind { Ok(match kind {
WebfingerType::Group => SearchableObjects::Community( WebfingerType::Group => SearchableObjects::Community(
blocking(pool, move |conn| Community::read_from_name(conn, &name)).await??, blocking(pool, move |conn| Community::read_from_name(conn, &name))
.await??
.into(),
), ),
WebfingerType::Person => SearchableObjects::Person( WebfingerType::Person => SearchableObjects::Person(
blocking(pool, move |conn| Person::find_by_name(conn, &name)).await??, blocking(pool, move |conn| DbPerson::find_by_name(conn, &name))
.await??
.into(),
), ),
}) })
} }
@ -84,23 +92,24 @@ async fn find_local_actor_by_name(
/// The types of ActivityPub objects that can be fetched directly by searching for their ID. /// The types of ActivityPub objects that can be fetched directly by searching for their ID.
#[derive(Debug)] #[derive(Debug)]
pub enum SearchableObjects { pub enum SearchableObjects {
Person(Person), Person(ApubPerson),
Community(Community), Community(ApubCommunity),
Post(Post), Post(ApubPost),
Comment(Comment), Comment(ApubComment),
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum SearchableApubTypes { pub enum SearchableApubTypes {
Group(Group), Group(Group),
Person(ApubPerson), Person(Person),
Page(Page), Page(Page),
Note(Note), Note(Note),
} }
#[async_trait::async_trait(?Send)]
impl ApubObject for SearchableObjects { impl ApubObject for SearchableObjects {
type DataType = PgConnection; type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> { fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
match self { match self {
@ -116,32 +125,35 @@ impl ApubObject for SearchableObjects {
// a single query. // a single query.
// we could skip this and always return an error, but then it would always fetch objects // we could skip this and always return an error, but then it would always fetch objects
// over http, and not be able to mark objects as deleted that were deleted by remote server. // over http, and not be able to mark objects as deleted that were deleted by remote server.
fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> { async fn read_from_apub_id(
let c = Community::read_from_apub_id(conn, object_id.clone())?; object_id: Url,
context: &LemmyContext,
) -> Result<Option<Self>, LemmyError> {
let c = ApubCommunity::read_from_apub_id(object_id.clone(), context).await?;
if let Some(c) = c { if let Some(c) = c {
return Ok(Some(SearchableObjects::Community(c))); return Ok(Some(SearchableObjects::Community(c)));
} }
let p = Person::read_from_apub_id(conn, object_id.clone())?; let p = ApubPerson::read_from_apub_id(object_id.clone(), context).await?;
if let Some(p) = p { if let Some(p) = p {
return Ok(Some(SearchableObjects::Person(p))); return Ok(Some(SearchableObjects::Person(p)));
} }
let p = Post::read_from_apub_id(conn, object_id.clone())?; let p = ApubPost::read_from_apub_id(object_id.clone(), context).await?;
if let Some(p) = p { if let Some(p) = p {
return Ok(Some(SearchableObjects::Post(p))); return Ok(Some(SearchableObjects::Post(p)));
} }
let c = Comment::read_from_apub_id(conn, object_id)?; let c = ApubComment::read_from_apub_id(object_id, context).await?;
if let Some(c) = c { if let Some(c) = c {
return Ok(Some(SearchableObjects::Comment(c))); return Ok(Some(SearchableObjects::Comment(c)));
} }
Ok(None) Ok(None)
} }
fn delete(self, conn: &Self::DataType) -> Result<(), LemmyError> { async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
match self { match self {
SearchableObjects::Person(p) => p.delete(conn), SearchableObjects::Person(p) => p.delete(data).await,
SearchableObjects::Community(c) => c.delete(conn), SearchableObjects::Community(c) => c.delete(data).await,
SearchableObjects::Post(p) => p.delete(conn), SearchableObjects::Post(p) => p.delete(data).await,
SearchableObjects::Comment(c) => c.delete(conn), SearchableObjects::Comment(c) => c.delete(data).await,
} }
} }
} }
@ -149,6 +161,7 @@ impl ApubObject for SearchableObjects {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for SearchableObjects { impl FromApub for SearchableObjects {
type ApubType = SearchableApubTypes; type ApubType = SearchableApubTypes;
type DataType = LemmyContext;
async fn from_apub( async fn from_apub(
apub: &Self::ApubType, apub: &Self::ApubType,
@ -159,10 +172,10 @@ impl FromApub for SearchableObjects {
use SearchableApubTypes as SAT; use SearchableApubTypes as SAT;
use SearchableObjects as SO; use SearchableObjects as SO;
Ok(match apub { Ok(match apub {
SAT::Group(g) => SO::Community(Community::from_apub(g, context, ed, rc).await?), SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, ed, rc).await?),
SAT::Person(p) => SO::Person(Person::from_apub(p, context, ed, rc).await?), SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, ed, rc).await?),
SAT::Page(p) => SO::Post(Post::from_apub(p, context, ed, rc).await?), SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, ed, rc).await?),
SAT::Note(n) => SO::Comment(Comment::from_apub(n, context, ed, rc).await?), SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, ed, rc).await?),
}) })
} }
} }

View file

@ -1,10 +1,11 @@
use crate::{ use crate::{
http::{create_apub_response, create_apub_tombstone_response}, http::{create_apub_response, create_apub_tombstone_response},
objects::ToApub, objects::comment::ApubComment,
}; };
use actix_web::{body::Body, web, web::Path, HttpResponse}; use actix_web::{body::Body, web, web::Path, HttpResponse};
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::traits::ToApub;
use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud}; use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -21,7 +22,9 @@ pub(crate) async fn get_apub_comment(
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let id = CommentId(info.comment_id.parse::<i32>()?); let id = CommentId(info.comment_id.parse::<i32>()?);
let comment = blocking(context.pool(), move |conn| Comment::read(conn, id)).await??; let comment: ApubComment = blocking(context.pool(), move |conn| Comment::read(conn, id))
.await??
.into();
if !comment.local { if !comment.local {
return Err(NotFound.into()); return Err(NotFound.into());
} }

View file

@ -14,7 +14,7 @@ use crate::{
payload_to_string, payload_to_string,
receive_activity, receive_activity,
}, },
objects::ToApub, objects::community::ApubCommunity,
}; };
use activitystreams::{ use activitystreams::{
base::{AnyBase, BaseExt}, base::{AnyBase, BaseExt},
@ -23,7 +23,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::traits::{ActivityFields, ActivityHandler}; use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ToApub};
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::{
community_follower_view::CommunityFollowerView, community_follower_view::CommunityFollowerView,
@ -44,10 +44,11 @@ pub(crate) async fn get_apub_community_http(
info: web::Path<CommunityQuery>, info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let community = blocking(context.pool(), move |conn| { let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name) Community::read_from_name(conn, &info.community_name)
}) })
.await??; .await??
.into();
if !community.deleted { if !community.deleted {
let apub = community.to_apub(context.pool()).await?; let apub = community.to_apub(context.pool()).await?;
@ -173,10 +174,11 @@ pub(crate) async fn get_apub_community_moderators(
info: web::Path<CommunityQuery>, info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let community = blocking(context.pool(), move |conn| { let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name) Community::read_from_name(conn, &info.community_name)
}) })
.await??; .await??
.into();
// The attributed to, is an ordered vector with the creator actor_ids first, // The attributed to, is an ordered vector with the creator actor_ids first,
// then the rest of the moderators // then the rest of the moderators

View file

@ -16,7 +16,7 @@ use crate::{
payload_to_string, payload_to_string,
receive_activity, receive_activity,
}, },
objects::ToApub, objects::person::ApubPerson,
}; };
use activitystreams::{ use activitystreams::{
base::BaseExt, base::BaseExt,
@ -24,7 +24,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::traits::{ActivityFields, ActivityHandler}; use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ToApub};
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;
@ -44,10 +44,11 @@ pub(crate) async fn get_apub_person_http(
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let user_name = info.into_inner().user_name; let user_name = info.into_inner().user_name;
// TODO: this needs to be able to read deleted persons, so that it can send tombstones // TODO: this needs to be able to read deleted persons, so that it can send tombstones
let person = blocking(context.pool(), move |conn| { let person: ApubPerson = blocking(context.pool(), move |conn| {
Person::find_by_name(conn, &user_name) Person::find_by_name(conn, &user_name)
}) })
.await??; .await??
.into();
if !person.deleted { if !person.deleted {
let apub = person.to_apub(context.pool()).await?; let apub = person.to_apub(context.pool()).await?;

View file

@ -1,10 +1,11 @@
use crate::{ use crate::{
http::{create_apub_response, create_apub_tombstone_response}, http::{create_apub_response, create_apub_tombstone_response},
objects::ToApub, objects::post::ApubPost,
}; };
use actix_web::{body::Body, web, HttpResponse}; use actix_web::{body::Body, web, HttpResponse};
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::traits::ToApub;
use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud}; use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -21,7 +22,9 @@ pub(crate) async fn get_apub_post(
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let id = PostId(info.post_id.parse::<i32>()?); let id = PostId(info.post_id.parse::<i32>()?);
let post = blocking(context.pool(), move |conn| Post::read(conn, id)).await??; let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, id))
.await??
.into();
if !post.local { if !post.local {
return Err(NotFound.into()); return Err(NotFound.into());
} }

View file

@ -3,11 +3,12 @@ use crate::{
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
migrations::CommentInReplyToMigration, migrations::CommentInReplyToMigration,
objects::{create_tombstone, FromApub, Source, ToApub}, objects::{create_tombstone, person::ApubPerson, post::ApubPost, Source},
PostOrComment, PostOrComment,
}; };
use activitystreams::{ use activitystreams::{
base::AnyBase, base::AnyBase,
chrono::NaiveDateTime,
object::{kind::NoteType, Tombstone}, object::{kind::NoteType, Tombstone},
primitives::OneOrMany, primitives::OneOrMany,
unparsed::Unparsed, unparsed::Unparsed,
@ -16,7 +17,7 @@ use anyhow::{anyhow, Context};
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
traits::ActorType, traits::{ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown, PublicUrl}, values::{MediaTypeHtml, MediaTypeMarkdown, PublicUrl},
verify::verify_domains_match, verify::verify_domains_match,
}; };
@ -50,7 +51,7 @@ pub struct Note {
context: OneOrMany<AnyBase>, context: OneOrMany<AnyBase>,
r#type: NoteType, r#type: NoteType,
id: Url, id: Url,
pub(crate) attributed_to: ObjectId<Person>, pub(crate) attributed_to: ObjectId<ApubPerson>,
/// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain /// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain
/// the community ID, as it would be incompatible with Pleroma (and we can get the community from /// the community ID, as it would be incompatible with Pleroma (and we can get the community from
/// the post in [`in_reply_to`]). /// the post in [`in_reply_to`]).
@ -78,7 +79,7 @@ impl Note {
&self, &self,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(Post, Option<CommentId>), LemmyError> { ) -> Result<(ApubPost, Option<CommentId>), LemmyError> {
match &self.in_reply_to { match &self.in_reply_to {
CommentInReplyToMigration::Old(in_reply_to) => { CommentInReplyToMigration::Old(in_reply_to) => {
// This post, or the parent comment might not yet exist on this server yet, fetch them. // This post, or the parent comment might not yet exist on this server yet, fetch them.
@ -90,7 +91,7 @@ impl Note {
// Nested comments will automatically get fetched recursively // Nested comments will automatically get fetched recursively
let parent_id: Option<CommentId> = match in_reply_to.get(1) { let parent_id: Option<CommentId> = match in_reply_to.get(1) {
Some(comment_id) => { Some(comment_id) => {
let comment_id = ObjectId::<Comment>::new(comment_id.clone()); let comment_id = ObjectId::<ApubComment>::new(comment_id.clone());
let parent_comment = Box::pin(comment_id.dereference(context, request_counter)).await?; let parent_comment = Box::pin(comment_id.dereference(context, request_counter)).await?;
Some(parent_comment.id) Some(parent_comment.id)
@ -104,16 +105,16 @@ impl Note {
let parent = Box::pin(in_reply_to.dereference(context, request_counter).await?); let parent = Box::pin(in_reply_to.dereference(context, request_counter).await?);
match parent.deref() { match parent.deref() {
PostOrComment::Post(p) => { PostOrComment::Post(p) => {
// Workaround because I cant figure ut how to get the post out of the box (and we dont // Workaround because I cant figure out how to get the post out of the box (and we dont
// want to stackoverflow in a deep comment hierarchy). // want to stackoverflow in a deep comment hierarchy).
let post_id = p.id; let post_id = p.id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
Ok((post, None)) Ok((post.into(), None))
} }
PostOrComment::Comment(c) => { PostOrComment::Comment(c) => {
let post_id = c.post_id; let post_id = c.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
Ok((post, Some(c.id))) Ok((post.into(), Some(c.id)))
} }
} }
} }
@ -138,7 +139,7 @@ impl Note {
verify_domains_match(self.attributed_to.inner(), &self.id)?; verify_domains_match(self.attributed_to.inner(), &self.id)?;
verify_person_in_community( verify_person_in_community(
&self.attributed_to, &self.attributed_to,
&ObjectId::new(community.actor_id()), &ObjectId::new(community.actor_id),
context, context,
request_counter, request_counter,
) )
@ -147,9 +148,57 @@ impl Note {
} }
} }
#[derive(Clone, Debug)]
pub struct ApubComment(Comment);
impl Deref for ApubComment {
type Target = Comment;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Comment> for ApubComment {
fn from(c: Comment) -> Self {
ApubComment { 0: c }
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ToApub for Comment { impl ApubObject for ApubComment {
type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
) -> Result<Option<Self>, LemmyError> {
Ok(
blocking(context.pool(), move |conn| {
Comment::read_from_apub_id(conn, object_id)
})
.await??
.map(Into::into),
)
}
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| {
Comment::update_deleted(conn, self.id, true)
})
.await??;
Ok(())
}
}
#[async_trait::async_trait(?Send)]
impl ToApub for ApubComment {
type ApubType = Note; type ApubType = Note;
type TombstoneType = Tombstone;
type DataType = DbPool;
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> { async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let creator_id = self.creator_id; let creator_id = self.creator_id;
@ -200,8 +249,9 @@ impl ToApub for Comment {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for Comment { impl FromApub for ApubComment {
type ApubType = Note; type ApubType = Note;
type DataType = LemmyContext;
/// Converts a `Note` to `Comment`. /// Converts a `Note` to `Comment`.
/// ///
@ -211,7 +261,7 @@ impl FromApub for Comment {
context: &LemmyContext, context: &LemmyContext,
expected_domain: &Url, expected_domain: &Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Comment, LemmyError> { ) -> Result<ApubComment, LemmyError> {
let ap_id = Some(note.id(expected_domain)?.clone().into()); let ap_id = Some(note.id(expected_domain)?.clone().into());
let creator = note let creator = note
.attributed_to .attributed_to
@ -238,6 +288,7 @@ impl FromApub for Comment {
ap_id, ap_id,
local: Some(false), local: Some(false),
}; };
Ok(blocking(context.pool(), move |conn| Comment::upsert(conn, &form)).await??) let comment = blocking(context.pool(), move |conn| Comment::upsert(conn, &form)).await??;
Ok(comment.into())
} }
} }

View file

@ -4,12 +4,13 @@ use crate::{
fetcher::community::{fetch_community_outbox, update_community_mods}, fetcher::community::{fetch_community_outbox, update_community_mods},
generate_moderators_url, generate_moderators_url,
generate_outbox_url, generate_outbox_url,
objects::{create_tombstone, FromApub, ImageObject, Source, ToApub}, objects::{create_tombstone, ImageObject, Source},
CommunityType, CommunityType,
}; };
use activitystreams::{ use activitystreams::{
actor::{kind::GroupType, Endpoints}, actor::{kind::GroupType, Endpoints},
base::AnyBase, base::AnyBase,
chrono::NaiveDateTime,
object::{kind::ImageType, Tombstone}, object::{kind::ImageType, Tombstone},
primitives::OneOrMany, primitives::OneOrMany,
unparsed::Unparsed, unparsed::Unparsed,
@ -19,7 +20,7 @@ use itertools::Itertools;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
signatures::PublicKey, signatures::PublicKey,
traits::ActorType, traits::{ActorType, ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown}, values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match, verify::verify_domains_match,
}; };
@ -37,6 +38,7 @@ use lemmy_utils::{
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
use std::ops::Deref;
use url::Url; use url::Url;
#[skip_serializing_none] #[skip_serializing_none]
@ -117,9 +119,83 @@ impl Group {
} }
} }
#[derive(Clone, Debug)]
pub struct ApubCommunity(Community);
impl Deref for ApubCommunity {
type Target = Community;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Community> for ApubCommunity {
fn from(c: Community) -> Self {
ApubCommunity { 0: c }
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ToApub for Community { impl ApubObject for ApubCommunity {
type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
}
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
) -> Result<Option<Self>, LemmyError> {
Ok(
blocking(context.pool(), move |conn| {
Community::read_from_apub_id(conn, object_id)
})
.await??
.map(Into::into),
)
}
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| {
Community::update_deleted(conn, self.id, true)
})
.await??;
Ok(())
}
}
impl ActorType for ApubCommunity {
fn is_local(&self) -> bool {
self.local
}
fn actor_id(&self) -> Url {
self.actor_id.to_owned().into()
}
fn name(&self) -> String {
self.name.clone()
}
fn public_key(&self) -> Option<String> {
self.public_key.to_owned()
}
fn private_key(&self) -> Option<String> {
self.private_key.to_owned()
}
fn inbox_url(&self) -> Url {
self.inbox_url.clone().into()
}
fn shared_inbox_url(&self) -> Option<Url> {
self.shared_inbox_url.clone().map(|s| s.into_inner())
}
}
#[async_trait::async_trait(?Send)]
impl ToApub for ApubCommunity {
type ApubType = Group; type ApubType = Group;
type TombstoneType = Tombstone;
type DataType = DbPool;
async fn to_apub(&self, _pool: &DbPool) -> Result<Group, LemmyError> { async fn to_apub(&self, _pool: &DbPool) -> Result<Group, LemmyError> {
let source = self.description.clone().map(|bio| Source { let source = self.description.clone().map(|bio| Source {
@ -174,8 +250,9 @@ impl ToApub for Community {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for Community { impl FromApub for ApubCommunity {
type ApubType = Group; type ApubType = Group;
type DataType = LemmyContext;
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators. /// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
async fn from_apub( async fn from_apub(
@ -183,7 +260,7 @@ impl FromApub for Community {
context: &LemmyContext, context: &LemmyContext,
expected_domain: &Url, expected_domain: &Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Community, LemmyError> { ) -> Result<ApubCommunity, LemmyError> {
let form = Group::from_apub_to_form(group, expected_domain, &context.settings()).await?; let form = Group::from_apub_to_form(group, expected_domain, &context.settings()).await?;
let community = blocking(context.pool(), move |conn| Community::upsert(conn, &form)).await??; let community = blocking(context.pool(), move |conn| Community::upsert(conn, &form)).await??;
@ -192,7 +269,7 @@ impl FromApub for Community {
// TODO: doing this unconditionally might cause infinite loop for some reason // TODO: doing this unconditionally might cause infinite loop for some reason
fetch_community_outbox(context, &group.outbox, request_counter).await?; fetch_community_outbox(context, &group.outbox, request_counter).await?;
Ok(community) Ok(community.into())
} }
} }

View file

@ -5,43 +5,14 @@ use activitystreams::{
use anyhow::anyhow; use anyhow::anyhow;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use lemmy_apub_lib::values::MediaTypeMarkdown; use lemmy_apub_lib::values::MediaTypeMarkdown;
use lemmy_db_schema::DbPool;
use lemmy_utils::{utils::convert_datetime, LemmyError}; use lemmy_utils::{utils::convert_datetime, LemmyError};
use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
pub(crate) mod comment; pub mod comment;
pub(crate) mod community; pub mod community;
pub(crate) mod person; pub mod person;
pub(crate) mod post; pub mod post;
pub(crate) mod private_message; pub mod private_message;
/// Trait for converting an object or actor into the respective ActivityPub type.
#[async_trait::async_trait(?Send)]
pub(crate) trait ToApub {
type ApubType;
async fn to_apub(&self, pool: &DbPool) -> Result<Self::ApubType, LemmyError>;
fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
}
#[async_trait::async_trait(?Send)]
pub trait FromApub {
type ApubType;
/// Converts an object from ActivityPub type to Lemmy internal type.
///
/// * `apub` The object to read from
/// * `context` LemmyContext which holds DB pool, HTTP client etc
/// * `expected_domain` Domain where the object was received from. None in case of mod action.
/// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
async fn from_apub(
apub: &Self::ApubType,
context: &LemmyContext,
expected_domain: &Url,
request_counter: &mut i32,
) -> Result<Self, LemmyError>
where
Self: Sized;
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]

View file

@ -2,20 +2,21 @@ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
context::lemmy_context, context::lemmy_context,
generate_outbox_url, generate_outbox_url,
objects::{FromApub, ImageObject, Source, ToApub}, objects::{ImageObject, Source},
}; };
use activitystreams::{ use activitystreams::{
actor::Endpoints, actor::Endpoints,
base::AnyBase, base::AnyBase,
chrono::{DateTime, FixedOffset}, chrono::NaiveDateTime,
object::{kind::ImageType, Tombstone}, object::{kind::ImageType, Tombstone},
primitives::OneOrMany, primitives::OneOrMany,
unparsed::Unparsed, unparsed::Unparsed,
}; };
use chrono::{DateTime, FixedOffset};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
signatures::PublicKey, signatures::PublicKey,
traits::ActorType, traits::{ActorType, ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown}, values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match, verify::verify_domains_match,
}; };
@ -31,6 +32,7 @@ use lemmy_utils::{
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
use std::ops::Deref;
use url::Url; use url::Url;
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
@ -79,9 +81,85 @@ impl Person {
} }
} }
#[derive(Clone, Debug)]
pub struct ApubPerson(DbPerson);
impl Deref for ApubPerson {
type Target = DbPerson;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<DbPerson> for ApubPerson {
fn from(p: DbPerson) -> Self {
ApubPerson { 0: p }
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ToApub for DbPerson { impl ApubObject for ApubPerson {
type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
}
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
) -> Result<Option<Self>, LemmyError> {
Ok(
blocking(context.pool(), move |conn| {
DbPerson::read_from_apub_id(conn, object_id)
})
.await??
.map(Into::into),
)
}
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| {
DbPerson::update_deleted(conn, self.id, true)
})
.await??;
Ok(())
}
}
impl ActorType for ApubPerson {
fn is_local(&self) -> bool {
self.local
}
fn actor_id(&self) -> Url {
self.actor_id.to_owned().into_inner()
}
fn name(&self) -> String {
self.name.clone()
}
fn public_key(&self) -> Option<String> {
self.public_key.to_owned()
}
fn private_key(&self) -> Option<String> {
self.private_key.to_owned()
}
fn inbox_url(&self) -> Url {
self.inbox_url.clone().into()
}
fn shared_inbox_url(&self) -> Option<Url> {
self.shared_inbox_url.clone().map(|s| s.into_inner())
}
}
#[async_trait::async_trait(?Send)]
impl ToApub for ApubPerson {
type ApubType = Person; type ApubType = Person;
type TombstoneType = Tombstone;
type DataType = DbPool;
async fn to_apub(&self, _pool: &DbPool) -> Result<Person, LemmyError> { async fn to_apub(&self, _pool: &DbPool) -> Result<Person, LemmyError> {
let kind = if self.bot_account { let kind = if self.bot_account {
@ -133,15 +211,16 @@ impl ToApub for DbPerson {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for DbPerson { impl FromApub for ApubPerson {
type ApubType = Person; type ApubType = Person;
type DataType = LemmyContext;
async fn from_apub( async fn from_apub(
person: &Person, person: &Person,
context: &LemmyContext, context: &LemmyContext,
expected_domain: &Url, expected_domain: &Url,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<DbPerson, LemmyError> { ) -> Result<ApubPerson, LemmyError> {
let actor_id = Some(person.id(expected_domain)?.clone().into()); let actor_id = Some(person.id(expected_domain)?.clone().into());
let name = person.preferred_username.clone(); let name = person.preferred_username.clone();
let display_name: Option<String> = person.name.clone(); let display_name: Option<String> = person.name.clone();
@ -184,6 +263,6 @@ impl FromApub for DbPerson {
DbPerson::upsert(conn, &person_form) DbPerson::upsert(conn, &person_form)
}) })
.await??; .await??;
Ok(person) Ok(person.into())
} }
} }

View file

@ -2,7 +2,7 @@ use crate::{
activities::{extract_community, verify_person_in_community}, activities::{extract_community, verify_person_in_community},
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{create_tombstone, FromApub, ImageObject, Source, ToApub}, objects::{create_tombstone, person::ApubPerson, ImageObject, Source},
}; };
use activitystreams::{ use activitystreams::{
base::AnyBase, base::AnyBase,
@ -14,10 +14,10 @@ use activitystreams::{
public, public,
unparsed::Unparsed, unparsed::Unparsed,
}; };
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset, NaiveDateTime};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
traits::ActorType, traits::{ActorType, ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown}, values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match, verify::verify_domains_match,
}; };
@ -39,6 +39,7 @@ use lemmy_utils::{
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
use std::ops::Deref;
use url::Url; use url::Url;
#[skip_serializing_none] #[skip_serializing_none]
@ -49,7 +50,7 @@ pub struct Page {
context: OneOrMany<AnyBase>, context: OneOrMany<AnyBase>,
r#type: PageType, r#type: PageType,
id: Url, id: Url,
pub(crate) attributed_to: ObjectId<Person>, pub(crate) attributed_to: ObjectId<ApubPerson>,
to: [Url; 2], to: [Url; 2],
name: String, name: String,
content: Option<String>, content: Option<String>,
@ -80,7 +81,7 @@ impl Page {
/// ///
/// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]]. /// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]].
pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result<bool, LemmyError> { pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result<bool, LemmyError> {
let old_post = ObjectId::<Post>::new(self.id.clone()) let old_post = ObjectId::<ApubPost>::new(self.id.clone())
.dereference_local(context) .dereference_local(context)
.await; .await;
@ -112,9 +113,57 @@ impl Page {
} }
} }
#[derive(Clone, Debug)]
pub struct ApubPost(Post);
impl Deref for ApubPost {
type Target = Post;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Post> for ApubPost {
fn from(p: Post) -> Self {
ApubPost { 0: p }
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ToApub for Post { impl ApubObject for ApubPost {
type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
) -> Result<Option<Self>, LemmyError> {
Ok(
blocking(context.pool(), move |conn| {
Post::read_from_apub_id(conn, object_id)
})
.await??
.map(Into::into),
)
}
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| {
Post::update_deleted(conn, self.id, true)
})
.await??;
Ok(())
}
}
#[async_trait::async_trait(?Send)]
impl ToApub for ApubPost {
type ApubType = Page; type ApubType = Page;
type TombstoneType = Tombstone;
type DataType = DbPool;
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network. // Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
async fn to_apub(&self, pool: &DbPool) -> Result<Page, LemmyError> { async fn to_apub(&self, pool: &DbPool) -> Result<Page, LemmyError> {
@ -165,15 +214,16 @@ impl ToApub for Post {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for Post { impl FromApub for ApubPost {
type ApubType = Page; type ApubType = Page;
type DataType = LemmyContext;
async fn from_apub( async fn from_apub(
page: &Page, page: &Page,
context: &LemmyContext, context: &LemmyContext,
expected_domain: &Url, expected_domain: &Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Post, LemmyError> { ) -> Result<ApubPost, LemmyError> {
// We can't verify the domain in case of mod action, because the mod may be on a different // We can't verify the domain in case of mod action, because the mod may be on a different
// instance from the post author. // instance from the post author.
let ap_id = if page.is_mod_action(context).await? { let ap_id = if page.is_mod_action(context).await? {
@ -222,6 +272,7 @@ impl FromApub for Post {
ap_id, ap_id,
local: Some(false), local: Some(false),
}; };
Ok(blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??) let post = blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??;
Ok(post.into())
} }
} }

View file

@ -1,10 +1,11 @@
use crate::{ use crate::{
context::lemmy_context, context::lemmy_context,
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
objects::{create_tombstone, FromApub, Source, ToApub}, objects::{create_tombstone, person::ApubPerson, Source},
}; };
use activitystreams::{ use activitystreams::{
base::AnyBase, base::AnyBase,
chrono::NaiveDateTime,
object::{kind::NoteType, Tombstone}, object::{kind::NoteType, Tombstone},
primitives::OneOrMany, primitives::OneOrMany,
unparsed::Unparsed, unparsed::Unparsed,
@ -13,6 +14,7 @@ use anyhow::anyhow;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
traits::{ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown}, values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match, verify::verify_domains_match,
}; };
@ -28,6 +30,7 @@ use lemmy_utils::{utils::convert_datetime, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
use std::ops::Deref;
use url::Url; use url::Url;
#[skip_serializing_none] #[skip_serializing_none]
@ -38,8 +41,8 @@ pub struct Note {
context: OneOrMany<AnyBase>, context: OneOrMany<AnyBase>,
r#type: NoteType, r#type: NoteType,
id: Url, id: Url,
pub(crate) attributed_to: ObjectId<Person>, pub(crate) attributed_to: ObjectId<ApubPerson>,
to: ObjectId<Person>, to: ObjectId<ApubPerson>,
content: String, content: String,
media_type: MediaTypeHtml, media_type: MediaTypeHtml,
source: Source, source: Source,
@ -75,9 +78,54 @@ impl Note {
} }
} }
#[derive(Clone, Debug)]
pub struct ApubPrivateMessage(PrivateMessage);
impl Deref for ApubPrivateMessage {
type Target = PrivateMessage;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<PrivateMessage> for ApubPrivateMessage {
fn from(pm: PrivateMessage) -> Self {
ApubPrivateMessage { 0: pm }
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ToApub for PrivateMessage { impl ApubObject for ApubPrivateMessage {
type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
async fn read_from_apub_id(
object_id: Url,
context: &LemmyContext,
) -> Result<Option<Self>, LemmyError> {
Ok(
blocking(context.pool(), move |conn| {
PrivateMessage::read_from_apub_id(conn, object_id)
})
.await??
.map(Into::into),
)
}
async fn delete(self, _context: &LemmyContext) -> Result<(), LemmyError> {
// do nothing, because pm can't be fetched over http
unimplemented!()
}
}
#[async_trait::async_trait(?Send)]
impl ToApub for ApubPrivateMessage {
type ApubType = Note; type ApubType = Note;
type TombstoneType = Tombstone;
type DataType = DbPool;
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> { async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let creator_id = self.creator_id; let creator_id = self.creator_id;
@ -116,15 +164,16 @@ impl ToApub for PrivateMessage {
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl FromApub for PrivateMessage { impl FromApub for ApubPrivateMessage {
type ApubType = Note; type ApubType = Note;
type DataType = LemmyContext;
async fn from_apub( async fn from_apub(
note: &Note, note: &Note,
context: &LemmyContext, context: &LemmyContext,
expected_domain: &Url, expected_domain: &Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<PrivateMessage, LemmyError> { ) -> Result<ApubPrivateMessage, LemmyError> {
let ap_id = Some(note.id(expected_domain)?.clone().into()); let ap_id = Some(note.id(expected_domain)?.clone().into());
let creator = note let creator = note
.attributed_to .attributed_to
@ -143,11 +192,10 @@ impl FromApub for PrivateMessage {
ap_id, ap_id,
local: Some(false), local: Some(false),
}; };
Ok( let pm = blocking(context.pool(), move |conn| {
blocking(context.pool(), move |conn| {
PrivateMessage::upsert(conn, &form) PrivateMessage::upsert(conn, &form)
}) })
.await??, .await??;
) Ok(pm.into())
} }
} }

View file

@ -27,17 +27,21 @@ pub trait ActivityHandler {
) -> Result<(), LemmyError>; ) -> Result<(), LemmyError>;
} }
#[async_trait::async_trait(?Send)]
pub trait ApubObject { pub trait ApubObject {
type DataType; type DataType;
/// If this object should be refetched after a certain interval, it should return the last refresh /// If this object should be refetched after a certain interval, it should return the last refresh
/// time here. This is mainly used to update remote actors. /// time here. This is mainly used to update remote actors.
fn last_refreshed_at(&self) -> Option<NaiveDateTime>; fn last_refreshed_at(&self) -> Option<NaiveDateTime>;
/// Try to read the object with given ID from local database. Returns Ok(None) if it doesn't exist. /// Try to read the object with given ID from local database. Returns Ok(None) if it doesn't exist.
fn read_from_apub_id(data: &Self::DataType, object_id: Url) -> Result<Option<Self>, LemmyError> async fn read_from_apub_id(
object_id: Url,
data: &Self::DataType,
) -> Result<Option<Self>, LemmyError>
where where
Self: Sized; Self: Sized;
/// Marks the object as deleted in local db. Called when a tombstone is received. /// Marks the object as deleted in local db. Called when a tombstone is received.
fn delete(self, data: &Self::DataType) -> Result<(), LemmyError>; async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError>;
} }
/// Common methods provided by ActivityPub actors (community and person). Not all methods are /// Common methods provided by ActivityPub actors (community and person). Not all methods are
@ -67,3 +71,35 @@ pub trait ActorType {
}) })
} }
} }
/// Trait for converting an object or actor into the respective ActivityPub type.
#[async_trait::async_trait(?Send)]
pub trait ToApub {
type ApubType;
type TombstoneType;
type DataType;
async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError>;
fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError>;
}
#[async_trait::async_trait(?Send)]
pub trait FromApub {
type ApubType;
type DataType;
/// Converts an object from ActivityPub type to Lemmy internal type.
///
/// * `apub` The object to read from
/// * `context` LemmyContext which holds DB pool, HTTP client etc
/// * `expected_domain` Domain where the object was received from. None in case of mod action.
/// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
async fn from_apub(
apub: &Self::ApubType,
data: &Self::DataType,
expected_domain: &Url,
request_counter: &mut i32,
) -> Result<Self, LemmyError>
where
Self: Sized;
}

View file

@ -12,7 +12,6 @@ doctest = false
[dependencies] [dependencies]
lemmy_utils = { version = "=0.13.0", path = "../utils" } lemmy_utils = { version = "=0.13.0", path = "../utils" }
lemmy_apub_lib = { version = "=0.13.0", path = "../apub_lib" }
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] } diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
diesel_migrations = "1.4.0" diesel_migrations = "1.4.0"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }

View file

@ -11,10 +11,7 @@ use crate::{
}, },
traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable}, traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable},
}; };
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_apub_lib::traits::ApubObject;
use lemmy_utils::LemmyError;
use url::Url; use url::Url;
impl Comment { impl Comment {
@ -108,6 +105,17 @@ impl Comment {
.set(comment_form) .set(comment_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
use crate::schema::comment::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
comment
.filter(ap_id.eq(object_id))
.first::<Comment>(conn)
.ok()
.map(Into::into),
)
}
} }
impl Crud for Comment { impl Crud for Comment {
@ -198,25 +206,6 @@ impl DeleteableOrRemoveable for Comment {
} }
} }
impl ApubObject for Comment {
type DataType = PgConnection;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
use crate::schema::comment::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(comment.filter(ap_id.eq(object_id)).first::<Self>(conn).ok())
}
fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> {
Comment::update_deleted(conn, self.id, true)?;
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View file

@ -14,10 +14,7 @@ use crate::{
}, },
traits::{Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable}, traits::{Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
}; };
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl}; use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_apub_lib::traits::{ActorType, ApubObject};
use lemmy_utils::LemmyError;
use url::Url; use url::Url;
mod safe_type { mod safe_type {
@ -148,6 +145,17 @@ impl Community {
.set(community_form) .set(community_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
use crate::schema::community::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
community
.filter(actor_id.eq(object_id))
.first::<Community>(conn)
.ok()
.map(Into::into),
)
}
} }
impl Joinable for CommunityModerator { impl Joinable for CommunityModerator {
@ -297,59 +305,6 @@ impl Followable for CommunityFollower {
} }
} }
impl ApubObject for Community {
type DataType = PgConnection;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
}
fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
use crate::schema::community::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
community
.filter(actor_id.eq(object_id))
.first::<Self>(conn)
.ok(),
)
}
fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> {
use crate::schema::community::dsl::*;
diesel::update(community.find(self.id))
.set((deleted.eq(true), updated.eq(naive_now())))
.get_result::<Self>(conn)?;
Ok(())
}
}
impl ActorType for Community {
fn is_local(&self) -> bool {
self.local
}
fn actor_id(&self) -> Url {
self.actor_id.to_owned().into()
}
fn name(&self) -> String {
self.name.clone()
}
fn public_key(&self) -> Option<String> {
self.public_key.to_owned()
}
fn private_key(&self) -> Option<String> {
self.private_key.to_owned()
}
fn inbox_url(&self) -> Url {
self.inbox_url.clone().into()
}
fn shared_inbox_url(&self) -> Option<Url> {
self.shared_inbox_url.clone().map(|s| s.into_inner())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View file

@ -5,10 +5,7 @@ use crate::{
source::person::{Person, PersonForm}, source::person::{Person, PersonForm},
traits::Crud, traits::Crud,
}; };
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, *}; use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, *};
use lemmy_apub_lib::traits::{ActorType, ApubObject};
use lemmy_utils::LemmyError;
use url::Url; use url::Url;
mod safe_type { mod safe_type {
@ -237,61 +234,29 @@ impl Person {
.set(person_form) .set(person_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
}
impl ApubObject for Person { pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
type DataType = PgConnection;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
}
fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
use crate::schema::person::dsl::*; use crate::schema::person::dsl::*;
let object_id: DbUrl = object_id.into(); let object_id: DbUrl = object_id.into();
Ok( Ok(
person person
.filter(deleted.eq(false)) .filter(deleted.eq(false))
.filter(actor_id.eq(object_id)) .filter(actor_id.eq(object_id))
.first::<Self>(conn) .first::<Person>(conn)
.ok(), .ok()
.map(Into::into),
) )
} }
fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> { pub fn update_deleted(
conn: &PgConnection,
person_id: PersonId,
new_deleted: bool,
) -> Result<Person, Error> {
use crate::schema::person::dsl::*; use crate::schema::person::dsl::*;
diesel::update(person.find(self.id)) diesel::update(person.find(person_id))
.set((deleted.eq(true), updated.eq(naive_now()))) .set(deleted.eq(new_deleted))
.get_result::<Self>(conn)?; .get_result::<Self>(conn)
Ok(())
}
}
impl ActorType for Person {
fn is_local(&self) -> bool {
self.local
}
fn actor_id(&self) -> Url {
self.actor_id.to_owned().into_inner()
}
fn name(&self) -> String {
self.name.clone()
}
fn public_key(&self) -> Option<String> {
self.public_key.to_owned()
}
fn private_key(&self) -> Option<String> {
self.private_key.to_owned()
}
fn inbox_url(&self) -> Url {
self.inbox_url.clone().into()
}
fn shared_inbox_url(&self) -> Option<Url> {
self.shared_inbox_url.clone().map(|s| s.into_inner())
} }
} }

View file

@ -13,10 +13,7 @@ use crate::{
}, },
traits::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable}, traits::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable},
}; };
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl}; use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_apub_lib::traits::ApubObject;
use lemmy_utils::LemmyError;
use url::Url; use url::Url;
impl Crud for Post { impl Crud for Post {
@ -164,6 +161,17 @@ impl Post {
.set(post_form) .set(post_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
use crate::schema::post::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
post
.filter(ap_id.eq(object_id))
.first::<Post>(conn)
.ok()
.map(Into::into),
)
}
} }
impl Likeable for PostLike { impl Likeable for PostLike {
@ -248,28 +256,6 @@ impl DeleteableOrRemoveable for Post {
} }
} }
impl ApubObject for Post {
type DataType = PgConnection;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
use crate::schema::post::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(post.filter(ap_id.eq(object_id)).first::<Self>(conn).ok())
}
fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> {
use crate::schema::post::dsl::*;
diesel::update(post.find(self.id))
.set((deleted.eq(true), updated.eq(naive_now())))
.get_result::<Self>(conn)?;
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View file

@ -4,9 +4,7 @@ use crate::{
source::private_message::*, source::private_message::*,
traits::{Crud, DeleteableOrRemoveable}, traits::{Crud, DeleteableOrRemoveable},
}; };
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_apub_lib::traits::ApubObject;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use url::Url; use url::Url;
@ -109,6 +107,21 @@ impl PrivateMessage {
.set(private_message_form) .set(private_message_form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
} }
pub fn read_from_apub_id(
conn: &PgConnection,
object_id: Url,
) -> Result<Option<Self>, LemmyError> {
use crate::schema::private_message::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
private_message
.filter(ap_id.eq(object_id))
.first::<PrivateMessage>(conn)
.ok()
.map(Into::into),
)
}
} }
impl DeleteableOrRemoveable for PrivateMessage { impl DeleteableOrRemoveable for PrivateMessage {
@ -118,30 +131,6 @@ impl DeleteableOrRemoveable for PrivateMessage {
} }
} }
impl ApubObject for PrivateMessage {
type DataType = PgConnection;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
use crate::schema::private_message::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
private_message
.filter(ap_id.eq(object_id))
.first::<Self>(conn)
.ok(),
)
}
fn delete(self, _conn: &PgConnection) -> Result<(), LemmyError> {
// do nothing, because pm can't be fetched over http
unimplemented!()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View file

@ -12,7 +12,13 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId}, newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
traits::DeleteableOrRemoveable, source::{
comment::Comment,
person::Person,
person_mention::{PersonMention, PersonMentionForm},
post::Post,
},
traits::{Crud, DeleteableOrRemoveable},
}; };
use lemmy_db_views::{ use lemmy_db_views::{
comment_view::CommentView, comment_view::CommentView,
@ -21,7 +27,14 @@ use lemmy_db_views::{
private_message_view::PrivateMessageView, private_message_view::PrivateMessageView,
}; };
use lemmy_db_views_actor::community_view::CommunityView; use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_utils::{ConnectionId, LemmyError}; use lemmy_utils::{
email::send_email,
settings::structs::Settings,
utils::MentionData,
ConnectionId,
LemmyError,
};
use log::error;
pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>( pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>(
post_id: PostId, post_id: PostId,
@ -167,3 +180,149 @@ pub async fn send_pm_ws_message<OP: ToString + Send + OperationType + 'static>(
Ok(res) Ok(res)
} }
pub async fn send_local_notifs(
mentions: Vec<MentionData>,
comment: &Comment,
person: &Person,
post: &Post,
do_send_email: bool,
context: &LemmyContext,
) -> Result<Vec<LocalUserId>, LemmyError> {
let mut recipient_ids = Vec::new();
// Send the local mentions
for mention in mentions
.iter()
.filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name))
.collect::<Vec<&MentionData>>()
{
let mention_name = mention.name.clone();
let user_view = blocking(context.pool(), move |conn| {
LocalUserView::read_from_name(conn, &mention_name)
})
.await?;
if let Ok(mention_user_view) = user_view {
// TODO
// At some point, make it so you can't tag the parent creator either
// This can cause two notifications, one for reply and the other for mention
recipient_ids.push(mention_user_view.local_user.id);
let user_mention_form = PersonMentionForm {
recipient_id: mention_user_view.person.id,
comment_id: comment.id,
read: None,
};
// Allow this to fail softly, since comment edits might re-update or replace it
// Let the uniqueness handle this fail
blocking(context.pool(), move |conn| {
PersonMention::create(conn, &user_mention_form)
})
.await?
.ok();
// Send an email to those local users that have notifications on
if do_send_email {
send_email_to_user(
&mention_user_view,
"Mentioned by",
"Person Mention",
&comment.content,
&context.settings(),
)
}
}
}
// Send notifs to the parent commenter / poster
match comment.parent_id {
Some(parent_id) => {
let parent_comment =
blocking(context.pool(), move |conn| Comment::read(conn, parent_id)).await?;
if let Ok(parent_comment) = parent_comment {
// Don't send a notif to yourself
if parent_comment.creator_id != person.id {
// Get the parent commenter local_user
let user_view = blocking(context.pool(), move |conn| {
LocalUserView::read_person(conn, parent_comment.creator_id)
})
.await?;
if let Ok(parent_user_view) = user_view {
recipient_ids.push(parent_user_view.local_user.id);
if do_send_email {
send_email_to_user(
&parent_user_view,
"Reply from",
"Comment Reply",
&comment.content,
&context.settings(),
)
}
}
}
}
}
// Its a post
None => {
if post.creator_id != person.id {
let creator_id = post.creator_id;
let parent_user = blocking(context.pool(), move |conn| {
LocalUserView::read_person(conn, creator_id)
})
.await?;
if let Ok(parent_user_view) = parent_user {
recipient_ids.push(parent_user_view.local_user.id);
if do_send_email {
send_email_to_user(
&parent_user_view,
"Reply from",
"Post Reply",
&comment.content,
&context.settings(),
)
}
}
}
}
};
Ok(recipient_ids)
}
pub fn send_email_to_user(
local_user_view: &LocalUserView,
subject_text: &str,
body_text: &str,
comment_content: &str,
settings: &Settings,
) {
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
return;
}
if let Some(user_email) = &local_user_view.local_user.email {
let subject = &format!(
"{} - {} {}",
subject_text, settings.hostname, local_user_view.person.name,
);
let html = &format!(
"<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
body_text,
local_user_view.person.name,
comment_content,
settings.get_protocol_and_hostname()
);
match send_email(
subject,
user_email,
&local_user_view.person.name,
html,
settings,
) {
Ok(_o) => _o,
Err(e) => error!("{}", e),
};
}
}