]> Untitled Git - lemmy.git/commitdiff
If viewed actor isnt in db, fetch it from other instance (#2145)
authorNutomic <me@nutomic.com>
Wed, 23 Mar 2022 21:27:51 +0000 (21:27 +0000)
committerGitHub <noreply@github.com>
Wed, 23 Mar 2022 21:27:51 +0000 (21:27 +0000)
23 files changed:
crates/api/src/site.rs
crates/api_common/src/lib.rs
crates/api_crud/src/comment/read.rs
crates/api_crud/src/community/read.rs
crates/api_crud/src/post/read.rs
crates/api_crud/src/user/read.rs
crates/apub/src/activities/block/mod.rs
crates/apub/src/collections/community_moderators.rs
crates/apub/src/collections/community_outbox.rs
crates/apub/src/fetcher/mod.rs
crates/apub/src/fetcher/post_or_comment.rs
crates/apub/src/fetcher/search.rs
crates/apub/src/fetcher/user_or_community.rs
crates/apub/src/objects/comment.rs
crates/apub/src/objects/community.rs
crates/apub/src/objects/instance.rs
crates/apub/src/objects/person.rs
crates/apub/src/objects/post.rs
crates/apub/src/objects/private_message.rs
crates/apub_lib/src/traits.rs
crates/db_schema/src/impls/community.rs
crates/db_schema/src/impls/person.rs
crates/db_schema/src/traits.rs

index e978569905b450045388567190304e7b83947376..677576e1394a04603c55674c6fb84374fad23b7a 100644 (file)
@@ -8,11 +8,16 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   get_local_user_view_from_jwt_opt,
   is_admin,
-  resolve_actor_identifier,
   send_application_approved_email,
   site::*,
 };
-use lemmy_apub::fetcher::search::{search_by_apub_id, SearchableObjects};
+use lemmy_apub::{
+  fetcher::{
+    resolve_actor_identifier,
+    search::{search_by_apub_id, SearchableObjects},
+  },
+  objects::community::ApubCommunity,
+};
 use lemmy_db_schema::{
   diesel_option_overwrite,
   from_opt_str_to_opt_enum,
@@ -197,7 +202,7 @@ impl Perform for Search {
     let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All);
     let community_id = data.community_id;
     let community_actor_id = if let Some(name) = &data.community_name {
-      resolve_actor_identifier::<Community>(name, context.pool())
+      resolve_actor_identifier::<ApubCommunity, Community>(name, context)
         .await
         .ok()
         .map(|c| c.actor_id)
index a95365e9e89ec1a23d8a052e23014f6a4fc5b698..68ad36744856295828a32d71c006c0910b6c984f 100644 (file)
@@ -6,7 +6,6 @@ pub mod site;
 pub mod websocket;
 
 use crate::site::FederatedInstances;
-use itertools::Itertools;
 use lemmy_db_schema::{
   newtypes::{CommunityId, LocalUserId, PersonId, PostId},
   source::{
@@ -20,7 +19,7 @@ use lemmy_db_schema::{
     secret::Secret,
     site::Site,
   },
-  traits::{ApubActor, Crud, Readable},
+  traits::{Crud, Readable},
   DbPool,
 };
 use lemmy_db_views::{
@@ -522,39 +521,6 @@ pub async fn check_private_instance_and_federation_enabled(
   Ok(())
 }
 
-/// Resolve actor identifier (eg `!news@example.com`) from local database to avoid network requests.
-/// This only works for local actors, and remote actors which were previously fetched (so it doesnt
-/// trigger any new fetch).
-#[tracing::instrument(skip_all)]
-pub async fn resolve_actor_identifier<Actor>(
-  identifier: &str,
-  pool: &DbPool,
-) -> Result<Actor, LemmyError>
-where
-  Actor: ApubActor + Send + 'static,
-{
-  // remote actor
-  if identifier.contains('@') {
-    let (name, domain) = identifier
-      .splitn(2, '@')
-      .collect_tuple()
-      .expect("invalid query");
-    let name = name.to_string();
-    let domain = format!("{}://{}", Settings::get().get_protocol_string(), domain);
-    Ok(
-      blocking(pool, move |conn| {
-        Actor::read_from_name_and_domain(conn, &name, &domain)
-      })
-      .await??,
-    )
-  }
-  // local actor
-  else {
-    let identifier = identifier.to_string();
-    Ok(blocking(pool, move |conn| Actor::read_from_name(conn, &identifier)).await??)
-  }
-}
-
 pub async fn remove_user_data(banned_person_id: PersonId, pool: &DbPool) -> Result<(), LemmyError> {
   // Posts
   blocking(pool, move |conn: &'_ _| {
index 77ef1f962e88801bbe7fc88324808bec29f23246..d04a0b45a82fbde06b1493c2e88392df35351f4a 100644 (file)
@@ -5,8 +5,8 @@ use lemmy_api_common::{
   check_private_instance,
   comment::*,
   get_local_user_view_from_jwt_opt,
-  resolve_actor_identifier,
 };
+use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
   from_opt_str_to_opt_enum,
   source::community::Community,
@@ -78,7 +78,7 @@ impl PerformCrud for GetComments {
 
     let community_id = data.community_id;
     let community_actor_id = if let Some(name) = &data.community_name {
-      resolve_actor_identifier::<Community>(name, context.pool())
+      resolve_actor_identifier::<ApubCommunity, Community>(name, context)
         .await
         .ok()
         .map(|c| c.actor_id)
index ca30292f3761adfcb79a20a1859ac3d3c22c6c68..de567c1fb647468f085d98db18c7dbbdfac5c6b1 100644 (file)
@@ -5,8 +5,8 @@ use lemmy_api_common::{
   check_private_instance,
   community::*,
   get_local_user_view_from_jwt_opt,
-  resolve_actor_identifier,
 };
+use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
   from_opt_str_to_opt_enum,
   source::community::Community,
@@ -44,7 +44,7 @@ impl PerformCrud for GetCommunity {
       Some(id) => id,
       None => {
         let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
-        resolve_actor_identifier::<Community>(&name, context.pool())
+        resolve_actor_identifier::<ApubCommunity, Community>(&name, context)
           .await
           .map_err(|e| e.with_message("couldnt_find_community"))?
           .id
index 6b54c5b236a2cf6f701edf8a0e103ff8540d5a8a..724442245507b1545b0306f315768951d1c97e1d 100644 (file)
@@ -6,8 +6,8 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt_opt,
   mark_post_as_read,
   post::*,
-  resolve_actor_identifier,
 };
+use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
   from_opt_str_to_opt_enum,
   source::community::Community,
@@ -152,7 +152,7 @@ impl PerformCrud for GetPosts {
     let limit = data.limit;
     let community_id = data.community_id;
     let community_actor_id = if let Some(name) = &data.community_name {
-      resolve_actor_identifier::<Community>(name, context.pool())
+      resolve_actor_identifier::<ApubCommunity, Community>(name, context)
         .await
         .ok()
         .map(|c| c.actor_id)
index e98d2ff0c393b6dbd338549ba8600a6fe374930b..55cc219be99faac2958f25948eb6b27942c57fba 100644 (file)
@@ -5,8 +5,8 @@ use lemmy_api_common::{
   check_private_instance,
   get_local_user_view_from_jwt_opt,
   person::*,
-  resolve_actor_identifier,
 };
+use lemmy_apub::{fetcher::resolve_actor_identifier, objects::person::ApubPerson};
 use lemmy_db_schema::{from_opt_str_to_opt_enum, source::person::Person, SortType};
 use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
 use lemmy_db_views_actor::{
@@ -51,7 +51,7 @@ impl PerformCrud for GetPersonDetails {
           .to_owned()
           .unwrap_or_else(|| "admin".to_string());
 
-        resolve_actor_identifier::<Person>(&name, context.pool())
+        resolve_actor_identifier::<ApubPerson, Person>(&name, context)
           .await
           .map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?
           .id
index 7460acec847fd96d5cb795125434db18aaef04d7..f41e352f601afd12f53b152829d416a8da739cbf 100644 (file)
@@ -34,6 +34,7 @@ pub enum InstanceOrGroup {
 impl ApubObject for SiteOrCommunity {
   type DataType = LemmyContext;
   type ApubType = InstanceOrGroup;
+  type DbType = ();
   type TombstoneType = ();
 
   #[tracing::instrument(skip_all)]
index a9a691b8c8ed50c4ec346a83f0d96da2f7ac3bed..52825482662cdee498696a6cbf182b3b6e201ee6 100644 (file)
@@ -133,6 +133,8 @@ impl ApubObject for ApubCommunityModerators {
     // This return value is unused, so just set an empty vec
     Ok(ApubCommunityModerators { 0: vec![] })
   }
+
+  type DbType = ();
 }
 
 #[cfg(test)]
index eaa9acd9ffc5a21783264b17f0b069f67645e8e5..3b4c8534d6cd4132ceb09a34719591a09c6fb79a 100644 (file)
@@ -118,4 +118,6 @@ impl ApubObject for ApubCommunityOutbox {
     // This return value is unused, so just set an empty vec
     Ok(ApubCommunityOutbox { 0: vec![] })
   }
+
+  type DbType = ();
 }
index ff92e99fd42354b18460b7253638adb7a9336920..16173568ee6c6d7e0b2204b571c8945b1ca99634 100644 (file)
@@ -1,4 +1,63 @@
+use crate::fetcher::webfinger::webfinger_resolve_actor;
+use itertools::Itertools;
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::traits::{ActorType, ApubObject};
+use lemmy_db_schema::traits::ApubActor;
+use lemmy_utils::{settings::structs::Settings, LemmyError};
+use lemmy_websocket::LemmyContext;
+
 pub mod post_or_comment;
 pub mod search;
 pub mod user_or_community;
 pub mod webfinger;
+
+/// Resolve actor identifier (eg `!news@example.com`) from local database to avoid network requests.
+/// This only works for local actors, and remote actors which were previously fetched (so it doesnt
+/// trigger any new fetch).
+#[tracing::instrument(skip_all)]
+pub async fn resolve_actor_identifier<Actor, DbActor>(
+  identifier: &str,
+  context: &LemmyContext,
+) -> Result<DbActor, LemmyError>
+where
+  Actor:
+    ApubObject<DataType = LemmyContext> + ApubObject<DbType = DbActor> + ActorType + Send + 'static,
+  for<'de2> <Actor as ApubObject>::ApubType: serde::Deserialize<'de2>,
+  DbActor: ApubActor + Send + 'static,
+{
+  // remote actor
+  if identifier.contains('@') {
+    let (name, domain) = identifier
+      .splitn(2, '@')
+      .collect_tuple()
+      .expect("invalid query");
+    let name = name.to_string();
+    let domain = format!("{}://{}", Settings::get().get_protocol_string(), domain);
+    let actor = blocking(context.pool(), move |conn| {
+      DbActor::read_from_name_and_domain(conn, &name, &domain)
+    })
+    .await?;
+    if actor.is_ok() {
+      Ok(actor?)
+    } else {
+      // Fetch the actor from its home instance using webfinger
+      let id = webfinger_resolve_actor::<Actor>(identifier, context, &mut 0).await?;
+      let actor: DbActor = blocking(context.pool(), move |conn| {
+        DbActor::read_from_apub_id(conn, &id)
+      })
+      .await??
+      .expect("actor exists as we fetched just before");
+      Ok(actor)
+    }
+  }
+  // local actor
+  else {
+    let identifier = identifier.to_string();
+    Ok(
+      blocking(context.pool(), move |conn| {
+        DbActor::read_from_name(conn, &identifier)
+      })
+      .await??,
+    )
+  }
+}
index 2702385cb457548c6bd8c7f26b4136cdd7c5e451..f03a113ecd4b832a2e3a08981edacbd86feeea12 100644 (file)
@@ -26,6 +26,7 @@ pub enum PageOrNote {
 impl ApubObject for PostOrComment {
   type DataType = LemmyContext;
   type ApubType = PageOrNote;
+  type DbType = ();
   type TombstoneType = ();
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
index 6271cec12ca3745193cb3b337df346965c4a8ea9..e4ae45e6844bd008b242b48e477ed72e94fa3858 100644 (file)
@@ -78,6 +78,7 @@ pub enum SearchableApubTypes {
 impl ApubObject for SearchableObjects {
   type DataType = LemmyContext;
   type ApubType = SearchableApubTypes;
+  type DbType = ();
   type TombstoneType = ();
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
index 9e38e3dca0a1ee76958556e95318a4611b858a9c..ee86adcec26c50379b95ad64333fc916a59da03b 100644 (file)
@@ -26,6 +26,7 @@ pub enum PersonOrGroup {
 impl ApubObject for UserOrCommunity {
   type DataType = LemmyContext;
   type ApubType = PersonOrGroup;
+  type DbType = ();
   type TombstoneType = ();
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
index b719ba45b04ae2e6e2c9a139abbbe7234a3ebf10..2b706aac595eff78d897bd9b11c27bdb2801711e 100644 (file)
@@ -58,6 +58,7 @@ impl From<Comment> for ApubComment {
 impl ApubObject for ApubComment {
   type DataType = LemmyContext;
   type ApubType = Note;
+  type DbType = Comment;
   type TombstoneType = Tombstone;
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
index 82b6d797ccd35e660bbc6e3b423def8f3a5d9382..87646e91fb7660c82ec3c3979a9bb5c25ec85910 100644 (file)
@@ -49,6 +49,7 @@ impl From<Community> for ApubCommunity {
 impl ApubObject for ApubCommunity {
   type DataType = LemmyContext;
   type ApubType = Group;
+  type DbType = Community;
   type TombstoneType = Tombstone;
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
@@ -62,7 +63,7 @@ impl ApubObject for ApubCommunity {
   ) -> Result<Option<Self>, LemmyError> {
     Ok(
       blocking(context.pool(), move |conn| {
-        Community::read_from_apub_id(conn, object_id)
+        Community::read_from_apub_id(conn, &object_id.into())
       })
       .await??
       .map(Into::into),
index f12696c49e2660c5b5800b8a0e88aaea80459ef4..9b5ef117dfa65be8c1dbd6ef7941158aee14c277 100644 (file)
@@ -45,6 +45,7 @@ impl From<Site> for ApubSite {
 impl ApubObject for ApubSite {
   type DataType = LemmyContext;
   type ApubType = Instance;
+  type DbType = Site;
   type TombstoneType = ();
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
index 14e79e944b2b3cfcba6621f3ab932cf45820f627..26edc39b9e0c4dea0c1a1fa6135a540a7e1cddcd 100644 (file)
@@ -55,6 +55,7 @@ impl From<DbPerson> for ApubPerson {
 impl ApubObject for ApubPerson {
   type DataType = LemmyContext;
   type ApubType = Person;
+  type DbType = DbPerson;
   type TombstoneType = ();
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
@@ -68,7 +69,7 @@ impl ApubObject for ApubPerson {
   ) -> Result<Option<Self>, LemmyError> {
     Ok(
       blocking(context.pool(), move |conn| {
-        DbPerson::read_from_apub_id(conn, object_id)
+        DbPerson::read_from_apub_id(conn, &object_id.into())
       })
       .await??
       .map(Into::into),
index a002e72c720570af1dd52ae4d7c60a9b68e48847..c7d48a6862af0ac7e797554f5bdf821124d99e09 100644 (file)
@@ -57,6 +57,7 @@ impl From<Post> for ApubPost {
 impl ApubObject for ApubPost {
   type DataType = LemmyContext;
   type ApubType = Page;
+  type DbType = Post;
   type TombstoneType = Tombstone;
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
index 4896e1991dca5606cc1e21b1706eae3fe0d9233d..3768577cc7c1d8bffa0dc3e0fc9d078f8e5f9b3c 100644 (file)
@@ -46,6 +46,7 @@ impl From<PrivateMessage> for ApubPrivateMessage {
 impl ApubObject for ApubPrivateMessage {
   type DataType = LemmyContext;
   type ApubType = ChatMessage;
+  type DbType = PrivateMessage;
   type TombstoneType = ();
 
   fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
index bc57dfa4034572738f7ea77b54f4bbdb9e43120f..43fec524af7c7f114b1c0859cf858fed79acc746 100644 (file)
@@ -24,6 +24,7 @@ pub trait ActivityHandler {
 pub trait ApubObject {
   type DataType;
   type ApubType;
+  type DbType;
   type TombstoneType;
 
   /// If this object should be refetched after a certain interval, it should return the last refresh
index 43b7db3e29621a17a601a12e115c7a3d1d55ad0b..a7b50996398c59f0ec12f2a851796caab081e579 100644 (file)
@@ -24,7 +24,6 @@ use diesel::{
   RunQueryDsl,
   TextExpressionMethods,
 };
-use url::Url;
 
 mod safe_type {
   use crate::{schema::community::*, source::community::Community, traits::ToSafe};
@@ -291,9 +290,8 @@ impl Followable for CommunityFollower {
 }
 
 impl ApubActor for Community {
-  fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
+  fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error> {
     use crate::schema::community::dsl::*;
-    let object_id: DbUrl = object_id.into();
     Ok(
       community
         .filter(actor_id.eq(object_id))
index 4c052c8b61eb549326d0b067f2d52cde68d4cfa3..165d9308e45bfb1e8f0653f81e29a1f6b858b7ec 100644 (file)
@@ -15,7 +15,6 @@ use diesel::{
   RunQueryDsl,
   TextExpressionMethods,
 };
-use url::Url;
 
 mod safe_type {
   use crate::{schema::person::columns::*, source::person::Person, traits::ToSafe};
@@ -284,9 +283,8 @@ fn is_banned(banned_: bool, expires: Option<chrono::NaiveDateTime>) -> bool {
 }
 
 impl ApubActor for Person {
-  fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
+  fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error> {
     use crate::schema::person::dsl::*;
-    let object_id: DbUrl = object_id.into();
     Ok(
       person
         .filter(deleted.eq(false))
index ad984d7bf5a00b00c2a6098f941abeb455c7382b..98f97327341df42249c86dc334893fb95140f93a 100644 (file)
@@ -1,6 +1,8 @@
-use crate::newtypes::{CommunityId, PersonId};
+use crate::{
+  newtypes::{CommunityId, PersonId},
+  DbUrl,
+};
 use diesel::{result::Error, PgConnection};
-use url::Url;
 
 pub trait Crud {
   type Form;
@@ -166,7 +168,7 @@ pub trait ViewToVec {
 
 pub trait ApubActor {
   // TODO: this should be in a trait ApubObject (and implemented for Post, Comment, PrivateMessage as well)
-  fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error>
+  fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error>
   where
     Self: Sized;
   fn read_from_name(conn: &PgConnection, actor_name: &str) -> Result<Self, Error>