"cc": [
"https://ds9.lemmy.ml/c/main/"
],
- "object": ...
+ "object": "https://enterprise.lemmy.ml/p/123"
}
```
"cc": [
"https://ds9.lemmy.ml/c/main/"
],
- "object": ...
+ "object": "https://enterprise.lemmy.ml/p/123"
}
```
post::Post,
site::*,
user_view::*,
+ ApubObject,
Bannable,
Crud,
Followable,
let actor_id = make_apub_endpoint(EndpointType::Community, &data.name).to_string();
let actor_id_cloned = actor_id.to_owned();
let community_dupe = blocking(context.pool(), move |conn| {
- Community::read_from_actor_id(conn, &actor_id_cloned)
+ Community::read_from_apub_id(conn, &actor_id_cloned)
})
.await?;
if community_dupe.is_ok() {
-use crate::{
- activities::receive::get_actor_as_user,
- fetcher::get_or_fetch_and_insert_comment,
- ActorType,
- FromApub,
- NoteExt,
-};
+use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, NoteExt};
use activitystreams::{
activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update},
base::ExtendsExt,
};
-use anyhow::{anyhow, Context};
+use anyhow::Context;
use lemmy_db::{
- comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
+ comment::{Comment, CommentLike, CommentLikeForm},
comment_view::CommentView,
post::Post,
- Crud,
Likeable,
};
use lemmy_structs::{blocking, comment::CommentResponse, send_local_notifs};
let note = NoteExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let comment =
- CommentForm::from_apub(¬e, context, Some(user.actor_id()?), request_counter).await?;
+ let comment = Comment::from_apub(¬e, context, user.actor_id()?, request_counter).await?;
let post_id = comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
- if post.locked {
- return Err(anyhow!("Post is locked").into());
- }
-
- let inserted_comment =
- blocking(context.pool(), move |conn| Comment::upsert(conn, &comment)).await??;
// Note:
// Although mentions could be gotten from the post tags (they are included there), or the ccs,
// Its much easier to scrape them from the comment body, since the API has to do that
// anyway.
- let mentions = scrape_text_for_mentions(&inserted_comment.content);
- let recipient_ids = send_local_notifs(
- mentions,
- inserted_comment.clone(),
- &user,
- post,
- context.pool(),
- true,
- )
- .await?;
+ let mentions = scrape_text_for_mentions(&comment.content);
+ let recipient_ids =
+ send_local_notifs(mentions, comment.clone(), &user, post, context.pool(), true).await?;
// Refetch the view
let comment_view = blocking(context.pool(), move |conn| {
- CommentView::read(conn, inserted_comment.id, None)
+ CommentView::read(conn, comment.id, None)
})
.await??;
.context(location_info!())?;
let user = get_actor_as_user(&update, context, request_counter).await?;
- let comment =
- CommentForm::from_apub(¬e, context, Some(user.actor_id()?), request_counter).await?;
-
- let original_comment_id =
- get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
- .await?
- .id;
-
- let updated_comment = blocking(context.pool(), move |conn| {
- Comment::update(conn, original_comment_id, &comment)
- })
- .await??;
+ let comment = Comment::from_apub(¬e, context, user.actor_id()?, request_counter).await?;
- let post_id = updated_comment.post_id;
+ let comment_id = comment.id;
+ let post_id = comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
- let mentions = scrape_text_for_mentions(&updated_comment.content);
- let recipient_ids = send_local_notifs(
- mentions,
- updated_comment,
- &user,
- post,
- context.pool(),
- false,
- )
- .await?;
+ let mentions = scrape_text_for_mentions(&comment.content);
+ let recipient_ids =
+ send_local_notifs(mentions, comment, &user, post, context.pool(), false).await?;
// Refetch the view
let comment_view = blocking(context.pool(), move |conn| {
- CommentView::read(conn, original_comment_id, None)
+ CommentView::read(conn, comment_id, None)
})
.await??;
pub(crate) async fn receive_like_comment(
like: Like,
+ comment: Comment,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let note = NoteExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
- .context(location_info!())?;
let user = get_actor_as_user(&like, context, request_counter).await?;
- let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
-
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
- .await?
- .id;
-
+ let comment_id = comment.id;
let like_form = CommentLikeForm {
comment_id,
post_id: comment.post_id,
pub(crate) async fn receive_dislike_comment(
dislike: Dislike,
+ comment: Comment,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let note = NoteExt::from_any_base(
- dislike
- .object()
- .to_owned()
- .one()
- .context(location_info!())?,
- )?
- .context(location_info!())?;
let user = get_actor_as_user(&dislike, context, request_counter).await?;
- let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
-
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
- .await?
- .id;
-
+ let comment_id = comment.id;
let like_form = CommentLikeForm {
comment_id,
post_id: comment.post_id,
-use crate::{
- activities::receive::get_actor_as_user,
- fetcher::get_or_fetch_and_insert_comment,
- FromApub,
- NoteExt,
-};
-use activitystreams::{activity::*, prelude::*};
-use anyhow::Context;
+use crate::activities::receive::get_actor_as_user;
+use activitystreams::activity::{Dislike, Like};
use lemmy_db::{
- comment::{Comment, CommentForm, CommentLike},
+ comment::{Comment, CommentLike},
comment_view::CommentView,
Likeable,
};
use lemmy_structs::{blocking, comment::CommentResponse};
-use lemmy_utils::{location_info, LemmyError};
+use lemmy_utils::LemmyError;
use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation};
pub(crate) async fn receive_undo_like_comment(
like: &Like,
+ comment: Comment,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let user = get_actor_as_user(like, context, request_counter).await?;
- let note = NoteExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
- .context(location_info!())?;
-
- let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
-
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
- .await?
- .id;
+ let comment_id = comment.id;
let user_id = user.id;
blocking(context.pool(), move |conn| {
CommentLike::remove(conn, user_id, comment_id)
pub(crate) async fn receive_undo_dislike_comment(
dislike: &Dislike,
+ comment: Comment,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let user = get_actor_as_user(dislike, context, request_counter).await?;
- let note = NoteExt::from_any_base(
- dislike
- .object()
- .to_owned()
- .one()
- .context(location_info!())?,
- )?
- .context(location_info!())?;
-
- let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
-
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
- .await?
- .id;
+ let comment_id = comment.id;
let user_id = user.id;
blocking(context.pool(), move |conn| {
CommentLike::remove(conn, user_id, comment_id)
base::{AnyBase, ExtendsExt},
};
use anyhow::Context;
-use lemmy_db::{community::Community, community_view::CommunityView};
+use lemmy_db::{community::Community, community_view::CommunityView, ApubObject};
use lemmy_structs::{blocking, community::CommunityResponse};
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
.single_xsd_any_uri()
.context(location_info!())?;
let community = blocking(context.pool(), move |conn| {
- Community::read_from_actor_id(conn, community_uri.as_str())
+ Community::read_from_apub_id(conn, community_uri.as_str())
})
.await??;
.single_xsd_any_uri()
.context(location_info!())?;
let community = blocking(context.pool(), move |conn| {
- Community::read_from_actor_id(conn, community_uri.as_str())
+ Community::read_from_apub_id(conn, community_uri.as_str())
})
.await??;
-use crate::{
- activities::receive::get_actor_as_user,
- fetcher::get_or_fetch_and_insert_post,
- ActorType,
- FromApub,
- PageExt,
-};
+use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, PageExt};
use activitystreams::{
activity::{Create, Dislike, Like, Remove, Update},
prelude::*,
};
use anyhow::Context;
use lemmy_db::{
- post::{Post, PostForm, PostLike, PostLikeForm},
+ post::{Post, PostLike, PostLikeForm},
post_view::PostView,
- Crud,
Likeable,
};
use lemmy_structs::{blocking, post::PostResponse};
let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, Some(user.actor_id()?), request_counter).await?;
-
- // Using an upsert, since likes (which fetch the post), sometimes come in before the create
- // resulting in double posts.
- let inserted_post = blocking(context.pool(), move |conn| Post::upsert(conn, &post)).await??;
+ let post = Post::from_apub(&page, context, user.actor_id()?, request_counter).await?;
// Refetch the view
- let inserted_post_id = inserted_post.id;
+ let post_id = post.id;
let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, inserted_post_id, None)
+ PostView::read(conn, post_id, None)
})
.await??;
let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, Some(user.actor_id()?), request_counter).await?;
-
- let original_post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
- .await?
- .id;
-
- blocking(context.pool(), move |conn| {
- Post::update(conn, original_post_id, &post)
- })
- .await??;
+ let post = Post::from_apub(&page, context, user.actor_id()?, request_counter).await?;
+ let post_id = post.id;
// Refetch the view
let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, original_post_id, None)
+ PostView::read(conn, post_id, None)
})
.await??;
pub(crate) async fn receive_like_post(
like: Like,
+ post: Post,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let user = get_actor_as_user(&like, context, request_counter).await?;
- let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
- .context(location_info!())?;
-
- let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
- .await?
- .id;
+ let post_id = post.id;
let like_form = PostLikeForm {
post_id,
user_id: user.id,
pub(crate) async fn receive_dislike_post(
dislike: Dislike,
+ post: Post,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let user = get_actor_as_user(&dislike, context, request_counter).await?;
- let page = PageExt::from_any_base(
- dislike
- .object()
- .to_owned()
- .one()
- .context(location_info!())?,
- )?
- .context(location_info!())?;
-
- let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
- .await?
- .id;
+ let post_id = post.id;
let like_form = PostLikeForm {
post_id,
user_id: user.id,
-use crate::{
- activities::receive::get_actor_as_user,
- fetcher::get_or_fetch_and_insert_post,
- FromApub,
- PageExt,
-};
-use activitystreams::{activity::*, prelude::*};
-use anyhow::Context;
+use crate::activities::receive::get_actor_as_user;
+use activitystreams::activity::{Dislike, Like};
use lemmy_db::{
- post::{Post, PostForm, PostLike},
+ post::{Post, PostLike},
post_view::PostView,
Likeable,
};
use lemmy_structs::{blocking, post::PostResponse};
-use lemmy_utils::{location_info, LemmyError};
+use lemmy_utils::LemmyError;
use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
pub(crate) async fn receive_undo_like_post(
like: &Like,
+ post: Post,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let user = get_actor_as_user(like, context, request_counter).await?;
- let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
- .context(location_info!())?;
-
- let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
- .await?
- .id;
+ let post_id = post.id;
let user_id = user.id;
blocking(context.pool(), move |conn| {
PostLike::remove(conn, user_id, post_id)
pub(crate) async fn receive_undo_dislike_post(
dislike: &Dislike,
+ post: Post,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let user = get_actor_as_user(dislike, context, request_counter).await?;
- let page = PageExt::from_any_base(
- dislike
- .object()
- .to_owned()
- .one()
- .context(location_info!())?,
- )?
- .context(location_info!())?;
-
- let post = PostForm::from_apub(&page, context, None, request_counter).await?;
-
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
- .await?
- .id;
+ let post_id = post.id;
let user_id = user.id;
blocking(context.pool(), move |conn| {
PostLike::remove(conn, user_id, post_id)
check_is_apub_id_valid,
fetcher::get_or_fetch_and_upsert_user,
inbox::get_activity_to_and_cc,
- FromApub,
+ objects::FromApub,
NoteExt,
};
use activitystreams::{
public,
};
use anyhow::{anyhow, Context};
-use lemmy_db::{
- private_message::{PrivateMessage, PrivateMessageForm},
- private_message_view::PrivateMessageView,
- Crud,
-};
+use lemmy_db::{private_message::PrivateMessage, private_message_view::PrivateMessageView};
use lemmy_structs::{blocking, user::PrivateMessageResponse};
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::{messages::SendUserRoomMessage, LemmyContext, UserOperation};
.context(location_info!())?;
let private_message =
- PrivateMessageForm::from_apub(¬e, context, Some(expected_domain), request_counter).await?;
-
- let inserted_private_message = blocking(&context.pool(), move |conn| {
- PrivateMessage::create(conn, &private_message)
- })
- .await??;
+ PrivateMessage::from_apub(¬e, context, expected_domain, request_counter).await?;
let message = blocking(&context.pool(), move |conn| {
- PrivateMessageView::read(conn, inserted_private_message.id)
+ PrivateMessageView::read(conn, private_message.id)
})
.await??;
.to_owned();
let note = NoteExt::from_any_base(object)?.context(location_info!())?;
- let private_message_form =
- PrivateMessageForm::from_apub(¬e, context, Some(expected_domain), request_counter).await?;
-
- let private_message_ap_id = private_message_form
- .ap_id
- .as_ref()
- .context(location_info!())?
- .clone();
- let private_message = blocking(&context.pool(), move |conn| {
- PrivateMessage::read_from_apub_id(conn, &private_message_ap_id)
- })
- .await??;
-
- let private_message_id = private_message.id;
- blocking(&context.pool(), move |conn| {
- PrivateMessage::update(conn, private_message_id, &private_message_form)
- })
- .await??;
+ let private_message =
+ PrivateMessage::from_apub(¬e, context, expected_domain, request_counter).await?;
let private_message_id = private_message.id;
let message = blocking(&context.pool(), move |conn| {
activity_queue::{send_comment_mentions, send_to_community},
extensions::context::lemmy_context,
fetcher::get_or_fetch_and_upsert_user,
+ objects::ToApub,
ActorType,
ApubLikeableType,
ApubObjectType,
- ToApub,
};
use activitystreams::{
activity::{
#[async_trait::async_trait(?Send)]
impl ApubLikeableType for Comment {
async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
- let note = self.to_apub(context.pool()).await?;
-
let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
})
.await??;
- let mut like = Like::new(creator.actor_id.to_owned(), note.into_any_base()?);
+ let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
like
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(LikeType::Like)?)
}
async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
- let note = self.to_apub(context.pool()).await?;
-
let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
})
.await??;
- let mut dislike = Dislike::new(creator.actor_id.to_owned(), note.into_any_base()?);
+ let mut dislike = Dislike::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
dislike
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(DislikeType::Dislike)?)
creator: &User_,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let note = self.to_apub(context.pool()).await?;
-
let post_id = self.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
})
.await??;
- let mut like = Like::new(creator.actor_id.to_owned(), note.into_any_base()?);
+ let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
like
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(DislikeType::Dislike)?)
activities::send::generate_activity_id,
activity_queue::send_to_community,
extensions::context::lemmy_context,
+ objects::ToApub,
ActorType,
ApubLikeableType,
ApubObjectType,
- ToApub,
};
use activitystreams::{
activity::{
#[async_trait::async_trait(?Send)]
impl ApubLikeableType for Post {
async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
- let page = self.to_apub(context.pool()).await?;
-
let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
.await??;
- let mut like = Like::new(creator.actor_id.to_owned(), page.into_any_base()?);
+ let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
like
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(LikeType::Like)?)
}
async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
- let page = self.to_apub(context.pool()).await?;
-
let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
.await??;
- let mut dislike = Dislike::new(creator.actor_id.to_owned(), page.into_any_base()?);
+ let mut dislike = Dislike::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
dislike
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(DislikeType::Dislike)?)
creator: &User_,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let page = self.to_apub(context.pool()).await?;
-
let community_id = self.community_id;
let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
.await??;
- let mut like = Like::new(creator.actor_id.to_owned(), page.into_any_base()?);
+ let mut like = Like::new(creator.actor_id.to_owned(), Url::parse(&self.ap_id)?);
like
.set_many_contexts(lemmy_context()?)
.set_id(generate_activity_id(LikeType::Like)?)
activities::send::generate_activity_id,
activity_queue::send_activity_single_dest,
extensions::context::lemmy_context,
+ objects::ToApub,
ActorType,
ApubObjectType,
- ToApub,
};
use activitystreams::{
activity::{
use lemmy_db::{
community::{Community, CommunityFollower, CommunityFollowerForm},
user::User_,
+ ApubObject,
DbPool,
Followable,
};
) -> Result<(), LemmyError> {
let follow_actor_id = follow_actor_id.to_string();
let community = blocking(context.pool(), move |conn| {
- Community::read_from_actor_id(conn, &follow_actor_id)
+ Community::read_from_apub_id(conn, &follow_actor_id)
})
.await??;
) -> Result<(), LemmyError> {
let follow_actor_id = follow_actor_id.to_string();
let community = blocking(context.pool(), move |conn| {
- Community::read_from_actor_id(conn, &follow_actor_id)
+ Community::read_from_apub_id(conn, &follow_actor_id)
})
.await??;
/// * `activity` the apub activity to be sent
/// * `creator` the local actor which created the activity
/// * `inbox` the inbox url where the activity should be delivered to
-pub async fn send_activity_single_dest<T, Kind>(
+pub(crate) async fn send_activity_single_dest<T, Kind>(
activity: T,
creator: &dyn ActorType,
inbox: Url,
/// * `community` the sending community
/// * `sender_shared_inbox` in case of an announce, this should be the shared inbox of the inner
/// activities creator, as receiving a known activity will cause an error
-pub async fn send_to_community_followers<T, Kind>(
+pub(crate) async fn send_to_community_followers<T, Kind>(
activity: T,
community: &Community,
context: &LemmyContext,
/// * `creator` the creator of the activity
/// * `community` the destination community
///
-pub async fn send_to_community<T, Kind>(
+pub(crate) async fn send_to_community<T, Kind>(
activity: T,
creator: &User_,
community: &Community,
/// * `creator` user who created the comment
/// * `mentions` list of inboxes of users which are mentioned in the comment
/// * `activity` either a `Create/Note` or `Update/Note`
-pub async fn send_comment_mentions<T, Kind>(
+pub(crate) async fn send_comment_mentions<T, Kind>(
creator: &User_,
mentions: Vec<Url>,
activity: T,
}
/// Verifies the HTTP signature on an incoming inbox request.
-pub fn verify_signature(request: &HttpRequest, actor: &dyn ActorType) -> Result<(), LemmyError> {
+pub(crate) fn verify_signature(
+ request: &HttpRequest,
+ actor: &dyn ActorType,
+) -> Result<(), LemmyError> {
let public_key = actor.public_key().context(location_info!())?;
let verified = CONFIG2
.begin_verify(
use crate::{
check_is_apub_id_valid,
+ objects::FromApub,
ActorType,
- FromApub,
GroupExt,
NoteExt,
PageExt,
use chrono::NaiveDateTime;
use diesel::result::Error::NotFound;
use lemmy_db::{
- comment::{Comment, CommentForm},
+ comment::Comment,
comment_view::CommentView,
- community::{Community, CommunityForm, CommunityModerator, CommunityModeratorForm},
+ community::{Community, CommunityModerator, CommunityModeratorForm},
community_view::CommunityView,
naive_now,
- post::{Post, PostForm},
+ post::Post,
post_view::PostView,
- user::{UserForm, User_},
+ user::User_,
user_view::UserView,
- Crud,
+ ApubObject,
Joinable,
SearchType,
};
response
}
SearchAcceptedObjects::Page(p) => {
- let post_form = PostForm::from_apub(&p, context, Some(query_url), recursion_counter).await?;
+ let p = Post::from_apub(&p, context, query_url, recursion_counter).await?;
- let p = blocking(context.pool(), move |conn| Post::upsert(conn, &post_form)).await??;
response.posts =
vec![blocking(context.pool(), move |conn| PostView::read(conn, p.id, None)).await??];
response
}
SearchAcceptedObjects::Comment(c) => {
- let comment_form =
- CommentForm::from_apub(&c, context, Some(query_url), recursion_counter).await?;
+ let c = Comment::from_apub(&c, context, query_url, recursion_counter).await?;
- let c = blocking(context.pool(), move |conn| {
- Comment::upsert(conn, &comment_form)
- })
- .await??;
response.comments = vec![
blocking(context.pool(), move |conn| {
CommentView::read(conn, c.id, None)
) -> Result<User_, LemmyError> {
let apub_id_owned = apub_id.to_owned();
let user = blocking(context.pool(), move |conn| {
- User_::read_from_actor_id(conn, apub_id_owned.as_ref())
+ User_::read_from_apub_id(conn, apub_id_owned.as_ref())
})
.await?;
return Ok(u);
}
- let mut uf = UserForm::from_apub(
- &person?,
- context,
- Some(apub_id.to_owned()),
- recursion_counter,
- )
- .await?;
- uf.last_refreshed_at = Some(naive_now());
- let user = blocking(context.pool(), move |conn| User_::update(conn, u.id, &uf)).await??;
+ let user = User_::from_apub(&person?, context, apub_id.to_owned(), recursion_counter).await?;
+
+ let user_id = user.id;
+ blocking(context.pool(), move |conn| {
+ User_::mark_as_updated(conn, user_id)
+ })
+ .await??;
Ok(user)
}
let person =
fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
- let uf = UserForm::from_apub(
- &person,
- context,
- Some(apub_id.to_owned()),
- recursion_counter,
- )
- .await?;
- let user = blocking(context.pool(), move |conn| User_::upsert(conn, &uf)).await??;
+ let user = User_::from_apub(&person, context, apub_id.to_owned(), recursion_counter).await?;
Ok(user)
}
) -> Result<Community, LemmyError> {
let apub_id_owned = apub_id.to_owned();
let community = blocking(context.pool(), move |conn| {
- Community::read_from_actor_id(conn, apub_id_owned.as_str())
+ Community::read_from_apub_id(conn, apub_id_owned.as_str())
})
.await?;
}
let group = group?;
- let cf =
- CommunityForm::from_apub(&group, context, Some(apub_id.to_owned()), recursion_counter).await?;
- let community = blocking(context.pool(), move |conn| Community::upsert(conn, &cf)).await??;
+ let community =
+ Community::from_apub(&group, context, apub_id.to_owned(), recursion_counter).await?;
// Also add the community moderators too
let attributed_to = group.inner.attributed_to().context(location_info!())?;
}
for o in outbox_items {
let page = PageExt::from_any_base(o)?.context(location_info!())?;
+ let page_id = page.id_unchecked().context(location_info!())?;
- // The post creator may be from a blocked instance,
- // if it errors, then continue
- let post = match PostForm::from_apub(&page, context, None, recursion_counter).await {
- Ok(post) => post,
- Err(_) => continue,
- };
- let post_ap_id = post.ap_id.as_ref().context(location_info!())?.clone();
- // Check whether the post already exists in the local db
- let existing = blocking(context.pool(), move |conn| {
- Post::read_from_apub_id(conn, &post_ap_id)
- })
- .await?;
- match existing {
- Ok(e) => blocking(context.pool(), move |conn| Post::update(conn, e.id, &post)).await??,
- Err(_) => blocking(context.pool(), move |conn| Post::upsert(conn, &post)).await??,
- };
+ // The post creator may be from a blocked instance, if it errors, then skip it
+ if check_is_apub_id_valid(page_id).is_err() {
+ continue;
+ }
+ Post::from_apub(&page, context, page_id.to_owned(), recursion_counter).await?;
// TODO: we need to send a websocket update here
}
Ok(p) => Ok(p),
Err(NotFound {}) => {
debug!("Fetching and creating remote post: {}", post_ap_id);
- let post =
+ let page =
fetch_remote_object::<PageExt>(context.client(), post_ap_id, recursion_counter).await?;
- let post_form = PostForm::from_apub(
- &post,
- context,
- Some(post_ap_id.to_owned()),
- recursion_counter,
- )
- .await?;
-
- let post = blocking(context.pool(), move |conn| Post::upsert(conn, &post_form)).await??;
+ let post = Post::from_apub(&page, context, post_ap_id.to_owned(), recursion_counter).await?;
Ok(post)
}
);
let comment =
fetch_remote_object::<NoteExt>(context.client(), comment_ap_id, recursion_counter).await?;
- let comment_form = CommentForm::from_apub(
+ let comment = Comment::from_apub(
&comment,
context,
- Some(comment_ap_id.to_owned()),
+ comment_ap_id.to_owned(),
recursion_counter,
)
.await?;
- let post_id = comment_form.post_id;
+ let post_id = comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
if post.locked {
return Err(anyhow!("Post is locked").into());
}
- let comment = blocking(context.pool(), move |conn| {
- Comment::upsert(conn, &comment_form)
- })
- .await??;
-
Ok(comment)
}
Err(e) => Err(e.into()),
use crate::{
http::{create_apub_response, create_apub_tombstone_response},
- ToApub,
+ objects::ToApub,
};
use actix_web::{body::Body, web, web::Path, HttpResponse};
use diesel::result::Error::NotFound;
use crate::{
extensions::context::lemmy_context,
http::{create_apub_response, create_apub_tombstone_response},
+ objects::ToApub,
ActorType,
- ToApub,
};
use activitystreams::{
base::{AnyBase, BaseExt, ExtendsExt},
use crate::{
http::{create_apub_response, create_apub_tombstone_response},
- ToApub,
+ objects::ToApub,
};
use actix_web::{body::Body, web, HttpResponse};
use diesel::result::Error::NotFound;
-use crate::{extensions::context::lemmy_context, http::create_apub_response, ActorType, ToApub};
+use crate::{
+ extensions::context::lemmy_context,
+ http::create_apub_response,
+ objects::ToApub,
+ ActorType,
+};
use activitystreams::{
base::BaseExt,
collection::{CollectionExt, OrderedCollection},
community::{Community, CommunityFollower, CommunityFollowerForm},
community_view::CommunityUserBanView,
user::User_,
+ ApubObject,
DbPool,
Followable,
};
// unconditionally.
let actor_id = actor.actor_id_str();
let user = blocking(&context.pool(), move |conn| {
- User_::read_from_actor_id(&conn, &actor_id)
+ User_::read_from_apub_id(&conn, &actor_id)
})
.await??;
check_community_or_site_ban(&user, &to_community, context.pool()).await?;
verify_activity_domains_valid(&follow, &user_url, false)?;
let user = blocking(&context.pool(), move |conn| {
- User_::read_from_actor_id(&conn, user_url.as_str())
+ User_::read_from_apub_id(&conn, user_url.as_str())
})
.await??;
let community_follower_form = CommunityFollowerForm {
Ok(())
}
-async fn check_community_or_site_ban(
+pub(crate) async fn check_community_or_site_ban(
user: &User_,
community: &Community,
pool: &DbPool,
};
use actix_web::HttpRequest;
use anyhow::{anyhow, Context};
-use lemmy_db::{activity::Activity, community::Community, user::User_, DbPool};
+use lemmy_db::{activity::Activity, community::Community, user::User_, ApubObject, DbPool};
use lemmy_structs::blocking;
use lemmy_utils::{location_info, settings::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
) -> Result<bool, LemmyError> {
for url in to_and_cc {
let url = url.to_string();
- let user = blocking(&pool, move |conn| User_::read_from_actor_id(&conn, &url)).await?;
+ let user = blocking(&pool, move |conn| User_::read_from_apub_id(&conn, &url)).await?;
if let Ok(u) = user {
if u.local {
return Ok(true);
if url.ends_with("/followers") {
let community_url = url.replace("/followers", "");
let community = blocking(&pool, move |conn| {
- Community::read_from_actor_id(&conn, &community_url)
+ Community::read_from_apub_id(&conn, &community_url)
})
.await??;
if !community.local {
receive_unhandled_activity,
verify_activity_domains_valid,
},
+ fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
inbox::is_addressed_to_public,
};
use activitystreams::{
};
use anyhow::Context;
use diesel::result::Error::NotFound;
-use lemmy_db::{comment::Comment, post::Post, site::Site, Crud};
+use lemmy_db::{comment::Comment, post::Post, site::Site, ApubObject, Crud};
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext;
verify_activity_domains_valid(&like, &expected_domain, false)?;
is_addressed_to_public(&like)?;
- match like.object().as_single_kind_str() {
- Some("Page") => receive_like_post(like, context, request_counter).await,
- Some("Note") => receive_like_comment(like, context, request_counter).await,
- _ => receive_unhandled_activity(like),
+ let object_id = get_like_object_id(&like)?;
+ match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+ PostOrComment::Post(post) => receive_like_post(like, post, context, request_counter).await,
+ PostOrComment::Comment(comment) => {
+ receive_like_comment(like, comment, context, request_counter).await
+ }
}
}
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
is_addressed_to_public(&dislike)?;
- match dislike.object().as_single_kind_str() {
- Some("Page") => receive_dislike_post(dislike, context, request_counter).await,
- Some("Note") => receive_dislike_comment(dislike, context, request_counter).await,
- _ => receive_unhandled_activity(dislike),
+ let object_id = get_like_object_id(&dislike)?;
+ match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+ PostOrComment::Post(post) => {
+ receive_dislike_post(dislike, post, context, request_counter).await
+ }
+ PostOrComment::Comment(comment) => {
+ receive_dislike_comment(dislike, comment, context, request_counter).await
+ }
}
}
verify_activity_domains_valid(&like, &expected_domain, false)?;
is_addressed_to_public(&like)?;
- let type_ = like
- .object()
- .as_single_kind_str()
- .context(location_info!())?;
- match type_ {
- "Note" => receive_undo_like_comment(&like, context, request_counter).await,
- "Page" => receive_undo_like_post(&like, context, request_counter).await,
- _ => receive_unhandled_activity(like),
+ let object_id = get_like_object_id(&like)?;
+ match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+ PostOrComment::Post(post) => {
+ receive_undo_like_post(&like, post, context, request_counter).await
+ }
+ PostOrComment::Comment(comment) => {
+ receive_undo_like_comment(&like, comment, context, request_counter).await
+ }
}
}
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
is_addressed_to_public(&dislike)?;
- let type_ = dislike
- .object()
- .as_single_kind_str()
- .context(location_info!())?;
- match type_ {
- "Note" => receive_undo_dislike_comment(&dislike, context, request_counter).await,
- "Page" => receive_undo_dislike_post(&dislike, context, request_counter).await,
- _ => receive_unhandled_activity(dislike),
+ let object_id = get_like_object_id(&dislike)?;
+ match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
+ PostOrComment::Post(post) => {
+ receive_undo_dislike_post(&dislike, post, context, request_counter).await
+ }
+ PostOrComment::Comment(comment) => {
+ receive_undo_dislike_comment(&dislike, comment, context, request_counter).await
+ }
}
}
return Err(NotFound.into());
}
+
+async fn fetch_post_or_comment_by_id(
+ apub_id: &Url,
+ context: &LemmyContext,
+ request_counter: &mut i32,
+) -> Result<PostOrComment, LemmyError> {
+ if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await {
+ return Ok(PostOrComment::Post(post));
+ }
+
+ if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await {
+ return Ok(PostOrComment::Comment(comment));
+ }
+
+ return Err(NotFound.into());
+}
+
+fn get_like_object_id<Activity>(like_or_dislike: &Activity) -> Result<Url, LemmyError>
+where
+ Activity: ActorAndObjectRefExt,
+{
+ // TODO: For backwards compatibility with older Lemmy versions where like.object contains a full
+ // post/comment. This can be removed after some time, using
+ // `activity.oject().as_single_xsd_any_uri()` instead.
+ let object = like_or_dislike.object();
+ if let Some(xsd_uri) = object.as_single_xsd_any_uri() {
+ Ok(xsd_uri.to_owned())
+ } else {
+ Ok(
+ object
+ .to_owned()
+ .one()
+ .context(location_info!())?
+ .id()
+ .context(location_info!())?
+ .to_owned(),
+ )
+ }
+}
use activitystreams::{activity::ActorAndObject, prelude::*};
use actix_web::{web, HttpRequest, HttpResponse};
use anyhow::Context;
-use lemmy_db::{community::Community, DbPool};
+use lemmy_db::{community::Community, ApubObject, DbPool};
use lemmy_structs::blocking;
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext;
) -> Result<Option<Community>, LemmyError> {
for url in to_and_cc {
let url = url.to_string();
- let community = blocking(&pool, move |conn| {
- Community::read_from_actor_id(&conn, &url)
- })
- .await?;
+ let community = blocking(&pool, move |conn| Community::read_from_apub_id(&conn, &url)).await?;
if let Ok(c) = community {
if c.local {
return Ok(Some(c));
community::{Community, CommunityFollower},
private_message::PrivateMessage,
user::User_,
+ ApubObject,
Followable,
};
use lemmy_structs::blocking;
) -> Result<CommunityOrPrivateMessage, LemmyError> {
let ap_id = apub_id.to_string();
let community = blocking(context.pool(), move |conn| {
- Community::read_from_actor_id(conn, &ap_id)
+ Community::read_from_apub_id(conn, &ap_id)
})
.await?;
if let Ok(c) = community {
activity::Follow,
actor::{ApActor, Group, Person},
base::AnyBase,
- object::{ApObject, Note, Page, Tombstone},
+ object::{ApObject, Note, Page},
};
use activitystreams_ext::{Ext1, Ext2};
use anyhow::{anyhow, Context};
}
}
-/// Trait for converting an object or actor into the respective ActivityPub type.
-#[async_trait::async_trait(?Send)]
-pub 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` If present, ensure that the domains of this and of the apub object ID are
- /// identical
- async fn from_apub(
- apub: &Self::ApubType,
- context: &LemmyContext,
- expected_domain: Option<Url>,
- request_counter: &mut i32,
- ) -> Result<Self, LemmyError>
- where
- Self: Sized;
-}
-
/// Common functions for ActivityPub objects, which are implemented by most (but not all) objects
/// and actors in Lemmy.
#[async_trait::async_trait(?Send)]
/// Store a sent or received activity in the database, for logging purposes. These records are not
/// persistent.
-pub async fn insert_activity<T>(
+pub(crate) async fn insert_activity<T>(
ap_id: &Url,
activity: T,
local: bool,
},
objects::{
check_object_domain,
+ check_object_for_community_or_site_ban,
create_tombstone,
+ get_object_from_apub,
get_source_markdown_value,
set_content_and_source,
+ FromApub,
+ FromApubToForm,
+ ToApub,
},
- FromApub,
NoteExt,
- ToApub,
};
use activitystreams::{
object::{kind::NoteType, ApObject, Note, Tombstone},
prelude::*,
};
-use anyhow::Context;
+use anyhow::{anyhow, Context};
use lemmy_db::{
comment::{Comment, CommentForm},
community::Community,
}
#[async_trait::async_trait(?Send)]
-impl FromApub for CommentForm {
+impl FromApub for Comment {
type ApubType = NoteExt;
- /// Converts a `Note` to `CommentForm`.
+ /// Converts a `Note` to `Comment`.
///
/// If the parent community, post and comment(s) are not known locally, these are also fetched.
async fn from_apub(
note: &NoteExt,
context: &LemmyContext,
- expected_domain: Option<Url>,
+ expected_domain: Url,
+ request_counter: &mut i32,
+ ) -> Result<Comment, LemmyError> {
+ check_object_for_community_or_site_ban(note, context, request_counter).await?;
+
+ let comment: Comment =
+ get_object_from_apub(note, context, expected_domain, request_counter).await?;
+
+ let post_id = comment.post_id;
+ let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+ if post.locked {
+ // This is not very efficient because a comment gets inserted just to be deleted right
+ // afterwards, but it seems to be the easiest way to implement it.
+ blocking(context.pool(), move |conn| {
+ Comment::delete(conn, comment.id)
+ })
+ .await??;
+ return Err(anyhow!("Post is locked").into());
+ } else {
+ Ok(comment)
+ }
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<NoteExt> for CommentForm {
+ async fn from_apub(
+ note: &NoteExt,
+ context: &LemmyContext,
+ expected_domain: Url,
request_counter: &mut i32,
) -> Result<CommentForm, LemmyError> {
let creator_actor_id = ¬e
objects::{
check_object_domain,
create_tombstone,
+ get_object_from_apub,
get_source_markdown_value,
set_content_and_source,
+ FromApub,
+ FromApubToForm,
+ ToApub,
},
ActorType,
- FromApub,
GroupExt,
- ToApub,
};
use activitystreams::{
actor::{kind::GroupType, ApActor, Endpoints, Group},
create_tombstone(self.deleted, &self.actor_id, self.updated, GroupType::Group)
}
}
+
#[async_trait::async_trait(?Send)]
-impl FromApub for CommunityForm {
+impl FromApub for Community {
type ApubType = GroupExt;
+ /// Converts a `Group` to `Community`.
+ async fn from_apub(
+ group: &GroupExt,
+ context: &LemmyContext,
+ expected_domain: Url,
+ request_counter: &mut i32,
+ ) -> Result<Community, LemmyError> {
+ get_object_from_apub(group, context, expected_domain, request_counter).await
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<GroupExt> for CommunityForm {
async fn from_apub(
group: &GroupExt,
context: &LemmyContext,
- expected_domain: Option<Url>,
+ expected_domain: Url,
request_counter: &mut i32,
) -> Result<Self, LemmyError> {
let creator_and_moderator_uris = group.inner.attributed_to().context(location_info!())?;
-use crate::check_is_apub_id_valid;
+use crate::{
+ check_is_apub_id_valid,
+ fetcher::{get_or_fetch_and_upsert_community, get_or_fetch_and_upsert_user},
+ inbox::community_inbox::check_community_or_site_ban,
+};
use activitystreams::{
base::{AsBase, BaseExt, ExtendsExt},
markers::Base,
};
use anyhow::{anyhow, Context};
use chrono::NaiveDateTime;
-use lemmy_utils::{location_info, utils::convert_datetime, LemmyError};
+use lemmy_db::{ApubObject, Crud, DbPool};
+use lemmy_structs::blocking;
+use lemmy_utils::{location_info, settings::Settings, utils::convert_datetime, LemmyError};
+use lemmy_websocket::LemmyContext;
use url::Url;
pub(crate) mod comment;
pub(crate) mod private_message;
pub(crate) mod user;
+/// 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(crate) 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
+ async fn from_apub(
+ apub: &Self::ApubType,
+ context: &LemmyContext,
+ expected_domain: Url,
+ request_counter: &mut i32,
+ ) -> Result<Self, LemmyError>
+ where
+ Self: Sized;
+}
+
+#[async_trait::async_trait(?Send)]
+pub(in crate::objects) trait FromApubToForm<ApubType> {
+ async fn from_apub(
+ apub: &ApubType,
+ context: &LemmyContext,
+ expected_domain: Url,
+ request_counter: &mut i32,
+ ) -> Result<Self, LemmyError>
+ where
+ Self: Sized;
+}
+
/// Updated is actually the deletion time
fn create_tombstone<T>(
deleted: bool,
pub(in crate::objects) fn check_object_domain<T, Kind>(
apub: &T,
- expected_domain: Option<Url>,
+ expected_domain: Url,
) -> Result<String, LemmyError>
where
T: Base + AsBase<Kind>,
{
- let object_id = if let Some(url) = expected_domain {
- let domain = url.domain().context(location_info!())?;
- apub.id(domain)?.context(location_info!())?
- } else {
- apub.id_unchecked().context(location_info!())?
- };
+ let domain = expected_domain.domain().context(location_info!())?;
+ let object_id = apub.id(domain)?.context(location_info!())?;
check_is_apub_id_valid(&object_id)?;
Ok(object_id.to_string())
}
Ok(())
}
}
+
+/// Converts an ActivityPub object (eg `Note`) to a database object (eg `Comment`). If an object
+/// with the same ActivityPub ID already exists in the database, it is returned directly. Otherwise
+/// the apub object is parsed, inserted and returned.
+pub(in crate::objects) async fn get_object_from_apub<From, Kind, To, ToForm>(
+ from: &From,
+ context: &LemmyContext,
+ expected_domain: Url,
+ request_counter: &mut i32,
+) -> Result<To, LemmyError>
+where
+ From: BaseExt<Kind>,
+ To: ApubObject<ToForm> + Crud<ToForm> + Send + 'static,
+ ToForm: FromApubToForm<From> + Send + 'static,
+{
+ let object_id = from.id_unchecked().context(location_info!())?.to_owned();
+ let domain = object_id.domain().context(location_info!())?;
+
+ // if its a local object, return it directly from the database
+ if Settings::get().hostname == domain {
+ let object = blocking(context.pool(), move |conn| {
+ To::read_from_apub_id(conn, object_id.as_str())
+ })
+ .await??;
+ Ok(object)
+ }
+ // otherwise parse and insert, assuring that it comes from the right domain
+ else {
+ let to_form = ToForm::from_apub(&from, context, expected_domain, request_counter).await?;
+
+ let to = blocking(context.pool(), move |conn| To::upsert(conn, &to_form)).await??;
+ Ok(to)
+ }
+}
+
+pub(in crate::objects) async fn check_object_for_community_or_site_ban<T, Kind>(
+ object: &T,
+ context: &LemmyContext,
+ request_counter: &mut i32,
+) -> Result<(), LemmyError>
+where
+ T: ObjectExt<Kind>,
+{
+ let user_id = object
+ .attributed_to()
+ .context(location_info!())?
+ .as_single_xsd_any_uri()
+ .context(location_info!())?;
+ let user = get_or_fetch_and_upsert_user(user_id, context, request_counter).await?;
+ let community_id = object
+ .to()
+ .context(location_info!())?
+ .as_single_xsd_any_uri()
+ .context(location_info!())?;
+ let community = get_or_fetch_and_upsert_community(community_id, context, request_counter).await?;
+ check_community_or_site_ban(&user, &community, context.pool()).await
+}
fetcher::{get_or_fetch_and_upsert_community, get_or_fetch_and_upsert_user},
objects::{
check_object_domain,
+ check_object_for_community_or_site_ban,
create_tombstone,
+ get_object_from_apub,
get_source_markdown_value,
set_content_and_source,
+ FromApub,
+ FromApubToForm,
+ ToApub,
},
- FromApub,
PageExt,
- ToApub,
};
use activitystreams::{
object::{kind::PageType, ApObject, Image, Page, Tombstone},
}
#[async_trait::async_trait(?Send)]
-impl FromApub for PostForm {
+impl FromApub for Post {
type ApubType = PageExt;
/// Converts a `PageExt` to `PostForm`.
async fn from_apub(
page: &PageExt,
context: &LemmyContext,
- expected_domain: Option<Url>,
+ expected_domain: Url,
+ request_counter: &mut i32,
+ ) -> Result<Post, LemmyError> {
+ check_object_for_community_or_site_ban(page, context, request_counter).await?;
+ get_object_from_apub(page, context, expected_domain, request_counter).await
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<PageExt> for PostForm {
+ async fn from_apub(
+ page: &PageExt,
+ context: &LemmyContext,
+ expected_domain: Url,
request_counter: &mut i32,
) -> Result<PostForm, LemmyError> {
let ext = &page.ext_one;
objects::{
check_object_domain,
create_tombstone,
+ get_object_from_apub,
get_source_markdown_value,
set_content_and_source,
+ FromApub,
+ FromApubToForm,
+ ToApub,
},
- FromApub,
NoteExt,
- ToApub,
};
use activitystreams::{
object::{kind::NoteType, ApObject, Note, Tombstone},
}
#[async_trait::async_trait(?Send)]
-impl FromApub for PrivateMessageForm {
+impl FromApub for PrivateMessage {
type ApubType = NoteExt;
async fn from_apub(
note: &NoteExt,
context: &LemmyContext,
- expected_domain: Option<Url>,
+ expected_domain: Url,
+ request_counter: &mut i32,
+ ) -> Result<PrivateMessage, LemmyError> {
+ get_object_from_apub(note, context, expected_domain, request_counter).await
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<NoteExt> for PrivateMessageForm {
+ async fn from_apub(
+ note: &NoteExt,
+ context: &LemmyContext,
+ expected_domain: Url,
request_counter: &mut i32,
) -> Result<PrivateMessageForm, LemmyError> {
let creator_actor_id = note
use crate::{
extensions::context::lemmy_context,
- objects::{check_object_domain, get_source_markdown_value, set_content_and_source},
+ objects::{
+ check_object_domain,
+ get_source_markdown_value,
+ set_content_and_source,
+ FromApub,
+ FromApubToForm,
+ ToApub,
+ },
ActorType,
- FromApub,
PersonExt,
- ToApub,
};
use activitystreams::{
actor::{ApActor, Endpoints, Person},
use lemmy_db::{
naive_now,
user::{UserForm, User_},
+ ApubObject,
DbPool,
};
+use lemmy_structs::blocking;
use lemmy_utils::{
location_info,
+ settings::Settings,
utils::{check_slurs, check_slurs_opt, convert_datetime},
LemmyError,
};
}
#[async_trait::async_trait(?Send)]
-impl FromApub for UserForm {
+impl FromApub for User_ {
type ApubType = PersonExt;
+ async fn from_apub(
+ person: &PersonExt,
+ context: &LemmyContext,
+ expected_domain: Url,
+ request_counter: &mut i32,
+ ) -> Result<User_, LemmyError> {
+ let user_id = person.id_unchecked().context(location_info!())?.to_owned();
+ let domain = user_id.domain().context(location_info!())?;
+ if domain == Settings::get().hostname {
+ let user = blocking(context.pool(), move |conn| {
+ User_::read_from_apub_id(conn, user_id.as_str())
+ })
+ .await??;
+ Ok(user)
+ } else {
+ let user_form =
+ UserForm::from_apub(person, context, expected_domain, request_counter).await?;
+ let user = blocking(context.pool(), move |conn| User_::upsert(conn, &user_form)).await??;
+ Ok(user)
+ }
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl FromApubToForm<PersonExt> for UserForm {
async fn from_apub(
person: &PersonExt,
_context: &LemmyContext,
- expected_domain: Option<Url>,
+ expected_domain: Url,
_request_counter: &mut i32,
) -> Result<Self, LemmyError> {
let avatar = match person.icon() {
use crate::{
naive_now,
schema::{comment, comment_like, comment_saved},
+ ApubObject,
Crud,
Likeable,
Saveable,
}
}
+impl ApubObject<CommentForm> for Comment {
+ fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+ use crate::schema::comment::dsl::*;
+ comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
+ }
+
+ fn upsert(conn: &PgConnection, comment_form: &CommentForm) -> Result<Self, Error> {
+ use crate::schema::comment::dsl::*;
+ insert_into(comment)
+ .values(comment_form)
+ .on_conflict(ap_id)
+ .do_update()
+ .set(comment_form)
+ .get_result::<Self>(conn)
+ }
+}
+
impl Comment {
pub fn update_ap_id(
conn: &PgConnection,
.get_result::<Self>(conn)
}
- pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
- use crate::schema::comment::dsl::*;
- comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
- }
-
pub fn permadelete_for_creator(
conn: &PgConnection,
for_creator_id: i32,
.set((content.eq(new_content), updated.eq(naive_now())))
.get_result::<Self>(conn)
}
-
- pub fn upsert(conn: &PgConnection, comment_form: &CommentForm) -> Result<Self, Error> {
- use crate::schema::comment::dsl::*;
- insert_into(comment)
- .values(comment_form)
- .on_conflict(ap_id)
- .do_update()
- .set(comment_form)
- .get_result::<Self>(conn)
- }
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug, Clone)]
use crate::{
naive_now,
schema::{community, community_follower, community_moderator, community_user_ban},
+ ApubObject,
Bannable,
Crud,
Followable,
}
}
-impl Community {
- pub fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Self, Error> {
+impl ApubObject<CommunityForm> for Community {
+ fn read_from_apub_id(conn: &PgConnection, for_actor_id: &str) -> Result<Self, Error> {
use crate::schema::community::dsl::*;
community
- .filter(local.eq(true))
- .filter(name.eq(community_name))
+ .filter(actor_id.eq(for_actor_id))
.first::<Self>(conn)
}
- pub fn read_from_actor_id(conn: &PgConnection, for_actor_id: &str) -> Result<Self, Error> {
+ fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
+ use crate::schema::community::dsl::*;
+ insert_into(community)
+ .values(community_form)
+ .on_conflict(actor_id)
+ .do_update()
+ .set(community_form)
+ .get_result::<Self>(conn)
+ }
+}
+
+impl Community {
+ pub fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Self, Error> {
use crate::schema::community::dsl::*;
community
- .filter(actor_id.eq(for_actor_id))
+ .filter(local.eq(true))
+ .filter(name.eq(community_name))
.first::<Self>(conn)
}
.unwrap_or_default()
.contains(&user_id)
}
-
- pub fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
- use crate::schema::community::dsl::*;
- insert_into(community)
- .values(community_form)
- .on_conflict(actor_id)
- .do_update()
- .set(community_form)
- .get_result::<Self>(conn)
- }
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
Self: Sized;
}
+pub trait ApubObject<T> {
+ fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error>
+ where
+ Self: Sized;
+ fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error>
+ where
+ Self: Sized;
+}
+
pub trait MaybeOptional<T> {
fn get_optional(self) -> Option<T>;
}
use crate::{
naive_now,
schema::{post, post_like, post_read, post_saved},
+ ApubObject,
Crud,
Likeable,
Readable,
}
}
+impl Crud<PostForm> for Post {
+ fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
+ use crate::schema::post::dsl::*;
+ post.find(post_id).first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, post_id: i32) -> Result<usize, Error> {
+ use crate::schema::post::dsl::*;
+ diesel::delete(post.find(post_id)).execute(conn)
+ }
+
+ fn create(conn: &PgConnection, new_post: &PostForm) -> Result<Self, Error> {
+ use crate::schema::post::dsl::*;
+ insert_into(post).values(new_post).get_result::<Self>(conn)
+ }
+
+ fn update(conn: &PgConnection, post_id: i32, new_post: &PostForm) -> Result<Self, Error> {
+ use crate::schema::post::dsl::*;
+ diesel::update(post.find(post_id))
+ .set(new_post)
+ .get_result::<Self>(conn)
+ }
+}
+
+impl ApubObject<PostForm> for Post {
+ fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+ use crate::schema::post::dsl::*;
+ post.filter(ap_id.eq(object_id)).first::<Self>(conn)
+ }
+
+ fn upsert(conn: &PgConnection, post_form: &PostForm) -> Result<Post, Error> {
+ use crate::schema::post::dsl::*;
+ insert_into(post)
+ .values(post_form)
+ .on_conflict(ap_id)
+ .do_update()
+ .set(post_form)
+ .get_result::<Self>(conn)
+ }
+}
+
impl Post {
pub fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
.load::<Self>(conn)
}
- pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
- use crate::schema::post::dsl::*;
- post.filter(ap_id.eq(object_id)).first::<Self>(conn)
- }
-
pub fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: String) -> Result<Self, Error> {
use crate::schema::post::dsl::*;
pub fn is_post_creator(user_id: i32, post_creator_id: i32) -> bool {
user_id == post_creator_id
}
-
- pub fn upsert(conn: &PgConnection, post_form: &PostForm) -> Result<Post, Error> {
- use crate::schema::post::dsl::*;
- insert_into(post)
- .values(post_form)
- .on_conflict(ap_id)
- .do_update()
- .set(post_form)
- .get_result::<Self>(conn)
- }
-}
-
-impl Crud<PostForm> for Post {
- fn read(conn: &PgConnection, post_id: i32) -> Result<Self, Error> {
- use crate::schema::post::dsl::*;
- post.find(post_id).first::<Self>(conn)
- }
-
- fn delete(conn: &PgConnection, post_id: i32) -> Result<usize, Error> {
- use crate::schema::post::dsl::*;
- diesel::delete(post.find(post_id)).execute(conn)
- }
-
- fn create(conn: &PgConnection, new_post: &PostForm) -> Result<Self, Error> {
- use crate::schema::post::dsl::*;
- insert_into(post).values(new_post).get_result::<Self>(conn)
- }
-
- fn update(conn: &PgConnection, post_id: i32, new_post: &PostForm) -> Result<Self, Error> {
- use crate::schema::post::dsl::*;
- diesel::update(post.find(post_id))
- .set(new_post)
- .get_result::<Self>(conn)
- }
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
-use crate::{naive_now, schema::private_message, Crud};
+use crate::{naive_now, schema::private_message, ApubObject, Crud};
use diesel::{dsl::*, result::Error, *};
#[derive(Queryable, Identifiable, PartialEq, Debug)]
}
}
+impl ApubObject<PrivateMessageForm> for PrivateMessage {
+ fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error>
+ where
+ Self: Sized,
+ {
+ use crate::schema::private_message::dsl::*;
+ private_message
+ .filter(ap_id.eq(object_id))
+ .first::<Self>(conn)
+ }
+
+ fn upsert(conn: &PgConnection, private_message_form: &PrivateMessageForm) -> Result<Self, Error> {
+ use crate::schema::private_message::dsl::*;
+ insert_into(private_message)
+ .values(private_message_form)
+ .on_conflict(ap_id)
+ .do_update()
+ .set(private_message_form)
+ .get_result::<Self>(conn)
+ }
+}
+
impl PrivateMessage {
pub fn update_ap_id(
conn: &PgConnection,
.get_result::<Self>(conn)
}
- pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
- use crate::schema::private_message::dsl::*;
- private_message
- .filter(ap_id.eq(object_id))
- .first::<Self>(conn)
- }
-
pub fn update_content(
conn: &PgConnection,
private_message_id: i32,
.set(read.eq(true))
.get_results::<Self>(conn)
}
-
- // TODO use this
- pub fn upsert(
- conn: &PgConnection,
- private_message_form: &PrivateMessageForm,
- ) -> Result<Self, Error> {
- use crate::schema::private_message::dsl::*;
- insert_into(private_message)
- .values(private_message_form)
- .on_conflict(ap_id)
- .do_update()
- .set(private_message_form)
- .get_result::<Self>(conn)
- }
}
#[cfg(test)]
is_email_regex,
naive_now,
schema::{user_, user_::dsl::*},
+ ApubObject,
Crud,
};
use bcrypt::{hash, DEFAULT_COST};
}
}
+impl ApubObject<UserForm> for User_ {
+ fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+ use crate::schema::user_::dsl::*;
+ user_
+ .filter(deleted.eq(false))
+ .filter(actor_id.eq(object_id))
+ .first::<Self>(conn)
+ }
+
+ fn upsert(conn: &PgConnection, user_form: &UserForm) -> Result<User_, Error> {
+ insert_into(user_)
+ .values(user_form)
+ .on_conflict(actor_id)
+ .do_update()
+ .set(user_form)
+ .get_result::<Self>(conn)
+ }
+}
+
impl User_ {
pub fn register(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
let mut edited_user = form.clone();
.get_result::<Self>(conn)
}
- pub fn read_from_actor_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
- use crate::schema::user_::dsl::*;
- user_
- .filter(deleted.eq(false))
- .filter(actor_id.eq(object_id))
- .first::<Self>(conn)
- }
-
pub fn find_by_email_or_username(
conn: &PgConnection,
username_or_email: &str,
)
}
- pub fn upsert(conn: &PgConnection, user_form: &UserForm) -> Result<User_, Error> {
- insert_into(user_)
- .values(user_form)
- .on_conflict(actor_id)
- .do_update()
- .set(user_form)
+ pub fn mark_as_updated(conn: &PgConnection, user_id: i32) -> Result<User_, Error> {
+ diesel::update(user_.find(user_id))
+ .set((last_refreshed_at.eq(naive_now()),))
.get_result::<Self>(conn)
}