Move ApubObject trait to library

This commit is contained in:
Felix Ableitner 2021-10-04 12:27:24 +02:00
parent 28ca5da78f
commit 03cba11976
35 changed files with 296 additions and 257 deletions

3
Cargo.lock generated
View file

@ -1711,6 +1711,7 @@ dependencies = [
"lazy_static", "lazy_static",
"lemmy_api_common", "lemmy_api_common",
"lemmy_apub", "lemmy_apub",
"lemmy_apub_lib",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"lemmy_db_views", "lemmy_db_views",
@ -1836,6 +1837,8 @@ dependencies = [
"chrono", "chrono",
"diesel", "diesel",
"diesel-derive-newtype", "diesel-derive-newtype",
"lemmy_apub_lib",
"lemmy_utils",
"log", "log",
"serde", "serde",
"serde_json", "serde_json",

View file

@ -7,6 +7,7 @@ license = "AGPL-3.0"
[dependencies] [dependencies]
lemmy_apub = { version = "=0.13.0", path = "../apub" } lemmy_apub = { version = "=0.13.0", path = "../apub" }
lemmy_apub_lib = { version = "=0.13.0", path = "../apub_lib" }
lemmy_utils = { version = "=0.13.0", path = "../utils" } lemmy_utils = { version = "=0.13.0", path = "../utils" }
lemmy_db_queries = { version = "=0.13.0", path = "../db_queries" } lemmy_db_queries = { version = "=0.13.0", path = "../db_queries" }
lemmy_db_schema = { version = "=0.13.0", path = "../db_schema" } lemmy_db_schema = { version = "=0.13.0", path = "../db_schema" }

View file

@ -7,13 +7,14 @@ use lemmy_api_common::{
is_admin, is_admin,
}; };
use lemmy_apub::{ use lemmy_apub::{
fetcher::object_id::ObjectId,
generate_apub_endpoint, generate_apub_endpoint,
generate_followers_url, generate_followers_url,
generate_inbox_url, generate_inbox_url,
generate_shared_inbox_url, generate_shared_inbox_url,
EndpointType, EndpointType,
}; };
use lemmy_db_queries::{diesel_option_overwrite_to_url, ApubObject, Crud, Followable, Joinable}; use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud, Followable, Joinable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{ community::{
Community, Community,
@ -67,11 +68,8 @@ impl PerformCrud for CreateCommunity {
&data.name, &data.name,
&context.settings().get_protocol_and_hostname(), &context.settings().get_protocol_and_hostname(),
)?; )?;
let actor_id_cloned = community_actor_id.to_owned(); let community_actor_id_wrapped = ObjectId::<Community>::new(community_actor_id.clone());
let community_dupe = blocking(context.pool(), move |conn| { let community_dupe = community_actor_id_wrapped.dereference_local(context).await;
Community::read_from_apub_id(conn, &actor_id_cloned)
})
.await?;
if community_dupe.is_ok() { if community_dupe.is_ok() {
return Err(ApiError::err("community_already_exists").into()); return Err(ApiError::err("community_already_exists").into());
} }

View file

@ -1,14 +1,8 @@
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, EndpointType}; use lemmy_apub::{build_actor_id_from_shortname, fetcher::object_id::ObjectId, EndpointType};
use lemmy_db_queries::{ use lemmy_db_queries::{from_opt_str_to_opt_enum, DeleteableOrRemoveable, ListingType, SortType};
from_opt_str_to_opt_enum,
ApubObject,
DeleteableOrRemoveable,
ListingType,
SortType,
};
use lemmy_db_schema::source::community::*; use lemmy_db_schema::source::community::*;
use lemmy_db_views_actor::{ use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView, community_moderator_view::CommunityModeratorView,
@ -38,12 +32,11 @@ 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())?;
blocking(context.pool(), move |conn| { ObjectId::<Community>::new(community_actor_id)
Community::read_from_apub_id(conn, &community_actor_id) .dereference(context, &mut 0)
}) .await
.await? .map_err(|_| ApiError::err("couldnt_find_community"))?
.map_err(|_| ApiError::err("couldnt_find_community"))? .id
.id
} }
}; };

View file

@ -1,8 +1,8 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*}; use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
use lemmy_apub::{build_actor_id_from_shortname, EndpointType}; use lemmy_apub::{build_actor_id_from_shortname, fetcher::object_id::ObjectId, EndpointType};
use lemmy_db_queries::{from_opt_str_to_opt_enum, ApubObject, SortType}; use lemmy_db_queries::{from_opt_str_to_opt_enum, SortType};
use lemmy_db_schema::source::person::*; use lemmy_db_schema::source::person::*;
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::{
@ -45,10 +45,9 @@ 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 = blocking(context.pool(), move |conn| { let person = ObjectId::<Person>::new(actor_id)
Person::read_from_apub_id(conn, &actor_id) .dereference(context, &mut 0)
}) .await;
.await?;
person person
.map_err(|_| ApiError::err("couldnt_find_that_username_or_email"))? .map_err(|_| ApiError::err("couldnt_find_that_username_or_email"))?
.id .id

View file

@ -88,7 +88,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).await?; verify_mod_action(&self.actor, self.cc[0].clone(), 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

@ -98,7 +98,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).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
Ok(()) Ok(())
} }

View file

@ -90,7 +90,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).await?; verify_mod_action(&self.actor, self.cc[0].clone(), 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

@ -85,7 +85,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).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }

View file

@ -20,7 +20,7 @@ use activitystreams::{
}; };
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler, Data}; use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler, Data};
use lemmy_db_queries::{ApubObject, Crud}; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityForm}, community::{Community, CommunityForm},
person::Person, person::Person,
@ -85,20 +85,17 @@ 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).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
Ok(()) Ok(())
} }
async fn receive( async fn receive(
self, self,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let cc = self.cc[0].clone().into(); let cc = self.cc[0].clone();
let community = blocking(context.pool(), move |conn| { let community = cc.dereference(context, request_counter).await?;
Community::read_from_apub_id(conn, &cc)
})
.await??;
let updated_community = Group::from_apub_to_form( let updated_community = Group::from_apub_to_form(
&self.object, &self.object,

View file

@ -7,16 +7,11 @@ use crate::{
fetcher::object_id::ObjectId, fetcher::object_id::ObjectId,
ActorType, ActorType,
}; };
use diesel::PgConnection;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{verify_domains_match, ActivityFields}; use lemmy_apub_lib::{verify_domains_match, ActivityFields, ApubObject};
use lemmy_db_queries::{ use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
source::{comment::Comment_, community::Community_, post::Post_}, use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
ApubObject,
};
use lemmy_db_schema::{
source::{comment::Comment, community::Community, person::Person, post::Post},
DbUrl,
};
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},
@ -70,29 +65,32 @@ impl DeletableObjects {
ap_id: &Url, ap_id: &Url,
context: &LemmyContext, context: &LemmyContext,
) -> Result<DeletableObjects, LemmyError> { ) -> Result<DeletableObjects, LemmyError> {
let id: DbUrl = ap_id.clone().into(); if let Some(c) =
DeletableObjects::read_type_from_db::<Community>(ap_id.clone(), context).await?
if let Some(c) = DeletableObjects::read_type_from_db::<Community>(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>(id.clone(), context).await? { if let Some(p) = DeletableObjects::read_type_from_db::<Post>(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>(id.clone(), context).await? { if let Some(c) = DeletableObjects::read_type_from_db::<Comment>(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 // TODO: a method like this should be provided by fetcher module
async fn read_type_from_db<Type: ApubObject + Send + 'static>( async fn read_type_from_db<Type>(
ap_id: DbUrl, ap_id: Url,
context: &LemmyContext, context: &LemmyContext,
) -> Result<Option<Type>, LemmyError> { ) -> Result<Option<Type>, LemmyError>
where
Type: ApubObject<DataType = PgConnection> + Send + 'static,
{
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
Type::read_from_apub_id(conn, &ap_id).ok() Type::read_from_apub_id(conn, ap_id)
}) })
.await .await?
} }
} }
@ -114,7 +112,13 @@ pub(in crate::activities) async fn verify_delete_activity(
verify_person_in_community(&actor, community_id, context, request_counter).await?; verify_person_in_community(&actor, community_id, context, request_counter).await?;
} }
// community deletion is always a mod (or admin) action // community deletion is always a mod (or admin) action
verify_mod_action(&actor, ObjectId::new(c.actor_id()), context).await?; verify_mod_action(
&actor,
ObjectId::new(c.actor_id()),
context,
request_counter,
)
.await?;
} }
DeletableObjects::Post(p) => { DeletableObjects::Post(p) => {
verify_delete_activity_post_or_comment( verify_delete_activity_post_or_comment(
@ -153,7 +157,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).await?; verify_mod_action(&actor, community_id.clone(), 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)?;

View file

@ -18,7 +18,7 @@ use activitystreams::{
}; };
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler, Data}; use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler, Data};
use lemmy_db_queries::{ApubObject, Followable}; use lemmy_db_queries::Followable;
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower}, community::{Community, CommunityFollower},
person::Person, person::Person,
@ -44,18 +44,17 @@ pub struct AcceptFollowCommunity {
} }
impl AcceptFollowCommunity { impl AcceptFollowCommunity {
pub async fn send(follow: FollowCommunity, context: &LemmyContext) -> Result<(), LemmyError> { pub async fn send(
let community_id = follow.object.clone(); follow: FollowCommunity,
let community = blocking(context.pool(), move |conn| { context: &LemmyContext,
Community::read_from_apub_id(conn, &community_id.into()) request_counter: &mut i32,
}) ) -> Result<(), LemmyError> {
.await??; let community = follow.object.dereference_local(context).await?;
let person_id = follow.actor().clone(); let person = follow
let person = blocking(context.pool(), move |conn| { .actor
Person::read_from_apub_id(conn, &person_id.into()) .clone()
}) .dereference(context, request_counter)
.await??; .await?;
let accept = AcceptFollowCommunity { let accept = AcceptFollowCommunity {
actor: ObjectId::new(community.actor_id()), actor: ObjectId::new(community.actor_id()),
to: ObjectId::new(person.actor_id()), to: ObjectId::new(person.actor_id()),

View file

@ -31,7 +31,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 FollowCommunity { pub struct FollowCommunity {
actor: ObjectId<Person>, pub(in crate::activities::following) actor: ObjectId<Person>,
// 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<Community>,
pub(in crate::activities::following) object: ObjectId<Community>, pub(in crate::activities::following) object: ObjectId<Community>,
@ -117,6 +117,6 @@ impl ActivityHandler for FollowCommunity {
}) })
.await?; .await?;
AcceptFollowCommunity::send(self, context).await AcceptFollowCommunity::send(self, context, request_counter).await
} }
} }

View file

@ -7,11 +7,7 @@ use crate::{
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{verify_domains_match, ActivityFields}; use lemmy_apub_lib::{verify_domains_match, ActivityFields};
use lemmy_db_queries::ApubObject; use lemmy_db_schema::source::{community::Community, person::Person};
use lemmy_db_schema::{
source::{community::Community, person::Person},
DbUrl,
};
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;
@ -104,18 +100,12 @@ pub(crate) async fn verify_mod_action(
actor_id: &ObjectId<Person>, actor_id: &ObjectId<Person>,
community_id: ObjectId<Community>, community_id: ObjectId<Community>,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = blocking(context.pool(), move |conn| { let community = community_id.dereference_local(context).await?;
Community::read_from_apub_id(conn, &community_id.into())
})
.await??;
if community.local { if community.local {
let actor_id: DbUrl = actor_id.clone().into(); let actor = actor_id.dereference(context, request_counter).await?;
let actor = blocking(context.pool(), move |conn| {
Person::read_from_apub_id(conn, &actor_id)
})
.await??;
// Note: this will also return true for admins in addition to mods, but as we dont know about // Note: this will also return true for admins in addition to mods, but as we dont know about
// remote admins, it doesnt make any difference. // remote admins, it doesnt make any difference.

View file

@ -106,9 +106,9 @@ impl ActivityHandler for CreateOrUpdatePost {
} }
} }
CreateOrUpdateType::Update => { CreateOrUpdateType::Update => {
let is_mod_action = self.object.is_mod_action(context.pool()).await?; let is_mod_action = self.object.is_mod_action(context).await?;
if is_mod_action { if is_mod_action {
verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; verify_mod_action(&self.actor, self.cc[0].clone(), 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())?;

View file

@ -13,7 +13,7 @@ use activitystreams::{
}; };
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler, Data}; use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler, Data};
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud}; use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud};
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
@ -25,7 +25,7 @@ use url::Url;
pub struct DeletePrivateMessage { pub struct DeletePrivateMessage {
actor: ObjectId<Person>, actor: ObjectId<Person>,
to: ObjectId<Person>, to: ObjectId<Person>,
pub(in crate::activities::private_message) object: Url, pub(in crate::activities::private_message) object: ObjectId<PrivateMessage>,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: DeleteType, kind: DeleteType,
id: Url, id: Url,
@ -44,7 +44,7 @@ impl DeletePrivateMessage {
Ok(DeletePrivateMessage { Ok(DeletePrivateMessage {
actor: ObjectId::new(actor.actor_id()), actor: ObjectId::new(actor.actor_id()),
to: ObjectId::new(actor.actor_id()), to: ObjectId::new(actor.actor_id()),
object: pm.ap_id.clone().into(), object: ObjectId::new(pm.ap_id.clone()),
kind: DeleteType::Delete, kind: DeleteType::Delete,
id: generate_activity_id( id: generate_activity_id(
DeleteType::Delete, DeleteType::Delete,
@ -80,7 +80,7 @@ impl ActivityHandler for DeletePrivateMessage {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?; verify_activity(self, &context.settings())?;
verify_person(&self.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
verify_domains_match(self.actor.inner(), &self.object)?; verify_domains_match(self.actor.inner(), self.object.inner())?;
Ok(()) Ok(())
} }
@ -89,11 +89,7 @@ impl ActivityHandler for DeletePrivateMessage {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let ap_id = self.object.clone(); let private_message = self.object.dereference_local(context).await?;
let private_message = blocking(context.pool(), move |conn| {
PrivateMessage::read_from_apub_id(conn, &ap_id.into())
})
.await??;
let deleted_private_message = blocking(context.pool(), move |conn| { let deleted_private_message = blocking(context.pool(), move |conn| {
PrivateMessage::update_deleted(conn, private_message.id, true) PrivateMessage::update_deleted(conn, private_message.id, true)
}) })

View file

@ -24,7 +24,7 @@ use lemmy_apub_lib::{
ActivityHandler, ActivityHandler,
Data, Data,
}; };
use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud}; use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud};
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
@ -86,7 +86,7 @@ impl ActivityHandler for UndoDeletePrivateMessage {
verify_activity(self, &context.settings())?; verify_activity(self, &context.settings())?;
verify_person(&self.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
verify_urls_match(self.actor(), self.object.actor())?; verify_urls_match(self.actor(), self.object.actor())?;
verify_domains_match(self.actor(), &self.object.object)?; verify_domains_match(self.actor(), self.object.object.inner())?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -97,10 +97,7 @@ impl ActivityHandler for UndoDeletePrivateMessage {
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let ap_id = self.object.object.clone(); let ap_id = self.object.object.clone();
let private_message = blocking(context.pool(), move |conn| { let private_message = ap_id.dereference_local(context).await?;
PrivateMessage::read_from_apub_id(conn, &ap_id.into())
})
.await??;
let deleted_private_message = blocking(context.pool(), move |conn| { let deleted_private_message = blocking(context.pool(), move |conn| {
PrivateMessage::update_deleted(conn, private_message.id, false) PrivateMessage::update_deleted(conn, private_message.id, false)

View file

@ -6,7 +6,13 @@ use lemmy_db_queries::source::{
person::Person_, person::Person_,
post::Post_, post::Post_,
}; };
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_db_schema::source::{
comment::Comment,
community::Community,
person::Person,
post::Post,
private_message::PrivateMessage,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -83,3 +89,11 @@ impl DeletableApubObject for PostOrComment {
Ok(()) Ok(())
} }
} }
#[async_trait::async_trait(?Send)]
impl DeletableApubObject for PrivateMessage {
async fn delete(self, _context: &LemmyContext) -> Result<(), LemmyError> {
// do nothing, because pm can't be fetched over http
unimplemented!()
}
}

View file

@ -4,9 +4,10 @@ use crate::{
APUB_JSON_CONTENT_TYPE, APUB_JSON_CONTENT_TYPE,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use diesel::NotFound; use diesel::{NotFound, PgConnection};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_db_queries::{ApubObject, DbPool}; use lemmy_apub_lib::ApubObject;
use lemmy_db_queries::DbPool;
use lemmy_db_schema::DbUrl; use lemmy_db_schema::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;
@ -26,12 +27,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 + DeletableApubObject + Send + 'static, Kind: FromApub + ApubObject<DataType = PgConnection> + DeletableApubObject + 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 + DeletableApubObject + Send + 'static, Kind: FromApub + ApubObject<DataType = PgConnection> + DeletableApubObject + 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
@ -46,12 +47,12 @@ where
} }
/// Fetches an activitypub object, either from local database (if possible), or over http. /// Fetches an activitypub object, either from local database (if possible), or over http.
pub(crate) async fn dereference( pub async fn dereference(
&self, &self,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Kind, LemmyError> { ) -> Result<Kind, LemmyError> {
let db_object = self.dereference_locally(context.pool()).await?; let db_object = self.dereference_from_db(context.pool()).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()?) {
@ -66,30 +67,32 @@ where
// TODO: rename to should_refetch_object() // TODO: rename to should_refetch_object()
if should_refetch_actor(last_refreshed_at) { if should_refetch_actor(last_refreshed_at) {
return self return self
.dereference_remotely(context, request_counter, Some(object)) .dereference_from_http(context, request_counter, Some(object))
.await; .await;
} }
} }
Ok(object) Ok(object)
} else { } else {
self self
.dereference_remotely(context, request_counter, None) .dereference_from_http(context, request_counter, None)
.await .await
} }
} }
/// returning none means the object was not found in local db /// Fetch an object from the local db. Instead of falling back to http, this throws an error if
async fn dereference_locally(&self, pool: &DbPool) -> Result<Option<Kind>, LemmyError> { /// the object is not found in the database.
let id: DbUrl = self.0.clone().into(); pub async fn dereference_local(&self, context: &LemmyContext) -> Result<Kind, LemmyError> {
let object = blocking(pool, move |conn| ApubObject::read_from_apub_id(conn, &id)).await?; let object = self.dereference_from_db(context.pool()).await?;
match object { object.ok_or_else(|| anyhow!("object not found in database {}", self).into())
Ok(o) => Ok(Some(o)),
Err(NotFound {}) => Ok(None),
Err(e) => Err(e.into()),
}
} }
async fn dereference_remotely( /// returning none means the object was not found in local db
async fn dereference_from_db(&self, pool: &DbPool) -> Result<Option<Kind>, LemmyError> {
let id = self.0.clone();
blocking(pool, move |conn| ApubObject::read_from_apub_id(conn, id)).await?
}
async fn dereference_from_http(
&self, &self,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
@ -128,7 +131,7 @@ where
impl<Kind> Display for ObjectId<Kind> impl<Kind> Display for ObjectId<Kind>
where where
Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, Kind: FromApub + ApubObject<DataType = PgConnection> + DeletableApubObject + 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 {
@ -138,7 +141,7 @@ where
impl<Kind> From<ObjectId<Kind>> for Url impl<Kind> From<ObjectId<Kind>> for Url
where where
Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, Kind: FromApub + ApubObject<DataType = PgConnection> + DeletableApubObject + 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 {
@ -148,7 +151,7 @@ where
impl<Kind> From<ObjectId<Kind>> for DbUrl impl<Kind> From<ObjectId<Kind>> for DbUrl
where where
Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, Kind: FromApub + ApubObject<DataType = PgConnection> + DeletableApubObject + 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,13 +1,10 @@
use crate::objects::{comment::Note, post::Page, FromApub}; use crate::objects::{comment::Note, post::Page, FromApub};
use activitystreams::chrono::NaiveDateTime; use activitystreams::chrono::NaiveDateTime;
use diesel::{result::Error, PgConnection}; use diesel::PgConnection;
use lemmy_db_queries::ApubObject; use lemmy_apub_lib::ApubObject;
use lemmy_db_schema::{ use lemmy_db_schema::source::{
source::{ comment::{Comment, CommentForm},
comment::{Comment, CommentForm}, post::{Post, PostForm},
post::{Post, PostForm},
},
DbUrl,
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -33,19 +30,23 @@ pub enum PageOrNote {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ApubObject for PostOrComment { impl ApubObject for PostOrComment {
type DataType = PgConnection;
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: &DbUrl) -> Result<Self, Error> fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError>
where where
Self: Sized, Self: Sized,
{ {
let post = Post::read_from_apub_id(conn, object_id); let post = Post::read_from_apub_id(conn, object_id.clone())?;
Ok(match post { Ok(match post {
Ok(o) => PostOrComment::Post(Box::new(o)), Some(o) => Some(PostOrComment::Post(Box::new(o))),
Err(_) => PostOrComment::Comment(Box::new(Comment::read_from_apub_id(conn, object_id)?)), None => {
Comment::read_from_apub_id(conn, object_id)?.map(|c| PostOrComment::Comment(Box::new(c)))
}
}) })
} }
} }

View file

@ -4,19 +4,18 @@ use crate::{
}; };
use activitystreams::chrono::NaiveDateTime; use activitystreams::chrono::NaiveDateTime;
use anyhow::anyhow; use anyhow::anyhow;
use diesel::{result::Error, PgConnection}; use diesel::PgConnection;
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::webfinger::{webfinger_resolve_actor, WebfingerType}; use lemmy_apub_lib::{
webfinger::{webfinger_resolve_actor, WebfingerType},
ApubObject,
};
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{community::Community_, person::Person_}, source::{community::Community_, person::Person_},
ApubObject,
DbPool, DbPool,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
source::{comment::Comment, community::Community, person::Person, post::Post},
DbUrl,
};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::Deserialize; use serde::Deserialize;
@ -102,6 +101,8 @@ pub enum SearchableApubTypes {
} }
impl ApubObject for SearchableObjects { impl ApubObject for SearchableObjects {
type DataType = PgConnection;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> { fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
match self { match self {
SearchableObjects::Person(p) => p.last_refreshed_at(), SearchableObjects::Person(p) => p.last_refreshed_at(),
@ -114,23 +115,26 @@ impl ApubObject for SearchableObjects {
// TODO: this is inefficient, because if the object is not in local db, it will run 4 db queries // TODO: this is inefficient, because if the object is not in local db, it will run 4 db queries
// before finally returning an error. it would be nice if we could check all 4 tables in // before finally returning an error. it would be nice if we could check all 4 tables in
// a single query. // a single query.
// we could skip this and always return an error, but then it would not be able to mark // we could skip this and always return an error, but then it would always fetch objects
// 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: &DbUrl) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
let c = Community::read_from_apub_id(conn, object_id); let c = Community::read_from_apub_id(conn, object_id.clone())?;
if let Ok(c) = c { if let Some(c) = c {
return Ok(SearchableObjects::Community(c)); return Ok(Some(SearchableObjects::Community(c)));
} }
let p = Person::read_from_apub_id(conn, object_id); let p = Person::read_from_apub_id(conn, object_id.clone())?;
if let Ok(p) = p { if let Some(p) = p {
return Ok(SearchableObjects::Person(p)); return Ok(Some(SearchableObjects::Person(p)));
} }
let p = Post::read_from_apub_id(conn, object_id); let p = Post::read_from_apub_id(conn, object_id.clone())?;
if let Ok(p) = p { if let Some(p) = p {
return Ok(SearchableObjects::Post(p)); return Ok(Some(SearchableObjects::Post(p)));
} }
let c = Comment::read_from_apub_id(conn, object_id); let c = Comment::read_from_apub_id(conn, object_id)?;
Ok(SearchableObjects::Comment(c?)) if let Some(c) = c {
return Ok(Some(SearchableObjects::Comment(c)));
}
Ok(None)
} }
} }

View file

@ -21,7 +21,7 @@ use lemmy_apub_lib::{
values::{MediaTypeHtml, MediaTypeMarkdown}, values::{MediaTypeHtml, MediaTypeMarkdown},
verify_domains_match, verify_domains_match,
}; };
use lemmy_db_queries::{source::post::Post_, ApubObject, Crud, DbPool}; use lemmy_db_queries::{source::post::Post_, Crud, DbPool};
use lemmy_db_schema::{ use lemmy_db_schema::{
self, self,
source::{ source::{
@ -78,12 +78,10 @@ impl Page {
/// the current value, it is a mod action and needs to be verified as such. /// the current value, it is a mod action and needs to be verified as such.
/// ///
/// 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, pool: &DbPool) -> Result<bool, LemmyError> { pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result<bool, LemmyError> {
let post_id = self.id.clone(); let old_post = ObjectId::<Post>::new(self.id.clone())
let old_post = blocking(pool, move |conn| { .dereference_local(context)
Post::read_from_apub_id(conn, &post_id.into()) .await;
})
.await?;
let is_mod_action = if let Ok(old_post) = old_post { let is_mod_action = if let Ok(old_post) = old_post {
self.stickied != Some(old_post.stickied) || self.comments_enabled != Some(!old_post.locked) self.stickied != Some(old_post.stickied) || self.comments_enabled != Some(!old_post.locked)
@ -101,7 +99,7 @@ impl Page {
let community = extract_community(&self.to, context, request_counter).await?; let community = extract_community(&self.to, context, request_counter).await?;
check_slurs(&self.name, &context.settings().slur_regex())?; check_slurs(&self.name, &context.settings().slur_regex())?;
verify_domains_match(self.attributed_to.inner(), &self.id)?; verify_domains_match(self.attributed_to.inner(), &self.id.clone())?;
verify_person_in_community( verify_person_in_community(
&self.attributed_to, &self.attributed_to,
&ObjectId::new(community.actor_id()), &ObjectId::new(community.actor_id()),
@ -177,7 +175,7 @@ impl FromApub for Post {
) -> Result<Post, LemmyError> { ) -> Result<Post, 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.pool()).await? { let ap_id = if page.is_mod_action(context).await? {
page.id_unchecked() page.id_unchecked()
} else { } else {
page.id(expected_domain)? page.id(expected_domain)?

View file

@ -1,6 +1,6 @@
pub mod values; pub mod values;
use activitystreams::error::DomainError; use activitystreams::{chrono::NaiveDateTime, error::DomainError};
pub use lemmy_apub_lib_derive::*; pub use lemmy_apub_lib_derive::*;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use std::{ops::Deref, sync::Arc}; use std::{ops::Deref, sync::Arc};
@ -30,6 +30,17 @@ pub trait ActivityHandler {
) -> Result<(), LemmyError>; ) -> Result<(), LemmyError>;
} }
pub trait ApubObject {
type DataType;
/// 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.
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.
fn read_from_apub_id(data: &Self::DataType, object_id: Url) -> Result<Option<Self>, LemmyError>
where
Self: Sized;
}
pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> { pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
if a.domain() != b.domain() { if a.domain() != b.domain() {
return Err(DomainError.into()); return Err(DomainError.into());

View file

@ -12,7 +12,6 @@ extern crate diesel_migrations;
#[cfg(test)] #[cfg(test)]
extern crate serial_test; extern crate serial_test;
use chrono::NaiveDateTime;
use diesel::{result::Error, *}; use diesel::{result::Error, *};
use lemmy_db_schema::{CommunityId, DbUrl, PersonId}; use lemmy_db_schema::{CommunityId, DbUrl, PersonId};
use lemmy_utils::ApiError; use lemmy_utils::ApiError;
@ -155,16 +154,6 @@ pub trait DeleteableOrRemoveable {
fn blank_out_deleted_or_removed_info(self) -> Self; fn blank_out_deleted_or_removed_info(self) -> Self;
} }
// TODO: move this to apub lib
pub trait ApubObject {
/// 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.
fn last_refreshed_at(&self) -> Option<NaiveDateTime>;
fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error>
where
Self: Sized;
}
pub trait MaybeOptional<T> { pub trait MaybeOptional<T> {
fn get_optional(self) -> Option<T>; fn get_optional(self) -> Option<T>;
} }

View file

@ -1,5 +1,4 @@
use crate::{ApubObject, Crud, DeleteableOrRemoveable, Likeable, Saveable}; use crate::{Crud, DeleteableOrRemoveable, Likeable, Saveable};
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
@ -179,17 +178,6 @@ impl Crud for Comment {
} }
} }
impl ApubObject for Comment {
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::comment::dsl::*;
comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
}
}
impl Likeable for CommentLike { impl Likeable for CommentLike {
type Form = CommentLikeForm; type Form = CommentLikeForm;
type IdType = CommentId; type IdType = CommentId;

View file

@ -1,5 +1,4 @@
use crate::{ApubObject, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable}; use crate::{Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable};
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
@ -93,19 +92,6 @@ impl Crud for Community {
} }
} }
impl ApubObject for Community {
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
}
fn read_from_apub_id(conn: &PgConnection, for_actor_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::community::dsl::*;
community
.filter(actor_id.eq(for_actor_id))
.first::<Self>(conn)
}
}
pub trait Community_ { pub trait Community_ {
fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error>; fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error>;
fn update_deleted( fn update_deleted(

View file

@ -1,11 +1,9 @@
use crate::{ApubObject, Crud}; use crate::Crud;
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
schema::person::dsl::*, schema::person::dsl::*,
source::person::{Person, PersonForm}, source::person::{Person, PersonForm},
DbUrl,
PersonId, PersonId,
}; };
@ -181,20 +179,6 @@ impl Crud for Person {
} }
} }
impl ApubObject for Person {
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
}
fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::person::dsl::*;
person
.filter(deleted.eq(false))
.filter(actor_id.eq(object_id))
.first::<Self>(conn)
}
}
pub trait Person_ { pub trait Person_ {
fn ban_person(conn: &PgConnection, person_id: PersonId, ban: bool) -> Result<Person, Error>; fn ban_person(conn: &PgConnection, person_id: PersonId, ban: bool) -> Result<Person, Error>;
fn add_admin(conn: &PgConnection, person_id: PersonId, added: bool) -> Result<Person, Error>; fn add_admin(conn: &PgConnection, person_id: PersonId, added: bool) -> Result<Person, Error>;

View file

@ -1,5 +1,4 @@
use crate::{ApubObject, Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable}; use crate::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable};
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
@ -193,17 +192,6 @@ impl Post_ for Post {
} }
} }
impl ApubObject for Post {
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::post::dsl::*;
post.filter(ap_id.eq(object_id)).first::<Self>(conn)
}
}
impl Likeable for PostLike { impl Likeable for PostLike {
type Form = PostLikeForm; type Form = PostLikeForm;
type IdType = PostId; type IdType = PostId;

View file

@ -1,5 +1,4 @@
use crate::{ApubObject, Crud, DeleteableOrRemoveable}; use crate::{Crud, DeleteableOrRemoveable};
use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{naive_now, source::private_message::*, DbUrl, PersonId, PrivateMessageId}; use lemmy_db_schema::{naive_now, source::private_message::*, DbUrl, PersonId, PrivateMessageId};
@ -30,22 +29,6 @@ impl Crud for PrivateMessage {
} }
} }
impl ApubObject for PrivateMessage {
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error>
where
Self: Sized,
{
use lemmy_db_schema::schema::private_message::dsl::*;
private_message
.filter(ap_id.eq(object_id))
.first::<Self>(conn)
}
}
pub trait PrivateMessage_ { pub trait PrivateMessage_ {
fn update_ap_id( fn update_ap_id(
conn: &PgConnection, conn: &PgConnection,

View file

@ -9,6 +9,8 @@ license = "AGPL-3.0"
doctest = false doctest = false
[dependencies] [dependencies]
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"] }
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
serde = { version = "1.0.130", features = ["derive"] } serde = { version = "1.0.130", features = ["derive"] }

View file

@ -6,7 +6,12 @@ use crate::{
PersonId, PersonId,
PostId, PostId,
}; };
use chrono::NaiveDateTime;
use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_apub_lib::ApubObject;
use lemmy_utils::LemmyError;
use serde::Serialize; use serde::Serialize;
use url::Url;
// WITH RECURSIVE MyTree AS ( // WITH RECURSIVE MyTree AS (
// SELECT * FROM comment WHERE parent_id IS NULL // SELECT * FROM comment WHERE parent_id IS NULL
@ -104,3 +109,17 @@ pub struct CommentSavedForm {
pub comment_id: CommentId, pub comment_id: CommentId,
pub person_id: PersonId, pub person_id: PersonId,
} }
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())
}
}

View file

@ -4,7 +4,12 @@ use crate::{
DbUrl, DbUrl,
PersonId, PersonId,
}; };
use chrono::NaiveDateTime;
use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_apub_lib::ApubObject;
use lemmy_utils::LemmyError;
use serde::Serialize; use serde::Serialize;
use url::Url;
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "community"] #[table_name = "community"]
@ -124,3 +129,22 @@ pub struct CommunityFollowerForm {
pub person_id: PersonId, pub person_id: PersonId,
pub pending: bool, pub pending: bool,
} }
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(),
)
}
}

View file

@ -3,7 +3,12 @@ use crate::{
DbUrl, DbUrl,
PersonId, PersonId,
}; };
use chrono::NaiveDateTime;
use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_apub_lib::ApubObject;
use lemmy_utils::LemmyError;
use serde::Serialize; use serde::Serialize;
use url::Url;
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "person"] #[table_name = "person"]
@ -170,3 +175,23 @@ pub struct PersonForm {
pub admin: Option<bool>, pub admin: Option<bool>,
pub bot_account: Option<bool>, pub bot_account: Option<bool>,
} }
impl ApubObject for Person {
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::*;
let object_id: DbUrl = object_id.into();
Ok(
person
.filter(deleted.eq(false))
.filter(actor_id.eq(object_id))
.first::<Self>(conn)
.ok(),
)
}
}

View file

@ -5,7 +5,12 @@ use crate::{
PersonId, PersonId,
PostId, PostId,
}; };
use chrono::NaiveDateTime;
use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_apub_lib::ApubObject;
use lemmy_utils::LemmyError;
use serde::Serialize; use serde::Serialize;
use url::Url;
#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "post"] #[table_name = "post"]
@ -106,3 +111,17 @@ pub struct PostReadForm {
pub post_id: PostId, pub post_id: PostId,
pub person_id: PersonId, pub person_id: PersonId,
} }
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())
}
}

View file

@ -1,5 +1,10 @@
use crate::{schema::private_message, DbUrl, PersonId, PrivateMessageId}; use crate::{schema::private_message, DbUrl, PersonId, PrivateMessageId};
use chrono::NaiveDateTime;
use diesel::{ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use lemmy_apub_lib::ApubObject;
use lemmy_utils::LemmyError;
use serde::Serialize; use serde::Serialize;
use url::Url;
#[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)]
#[table_name = "private_message"] #[table_name = "private_message"]
@ -29,3 +34,22 @@ pub struct PrivateMessageForm {
pub ap_id: Option<DbUrl>, pub ap_id: Option<DbUrl>,
pub local: Option<bool>, pub local: Option<bool>,
} }
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(),
)
}
}