]> Untitled Git - lemmy.git/blobdiff - crates/apub/src/fetcher/mod.rs
Addressing slow profile queries. #2777 (#2830)
[lemmy.git] / crates / apub / src / fetcher / mod.rs
index db39d2282b2c47520ea333b41de1a131cc76156b..4f72d1488d5d8d9995e5efc99c1c92cb624d4435 100644 (file)
@@ -1,56 +1,64 @@
-pub mod object_id;
-pub mod post_or_comment;
-pub mod search;
-
-use crate::{
-  fetcher::object_id::ObjectId,
-  objects::{community::ApubCommunity, person::ApubPerson},
+use activitypub_federation::{
+  config::Data,
+  fetch::webfinger::webfinger_resolve_actor,
+  traits::{Actor, Object},
 };
-use chrono::NaiveDateTime;
-use lemmy_apub_lib::traits::ActorType;
-use lemmy_db_schema::naive_now;
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-use url::Url;
+use diesel::NotFound;
+use itertools::Itertools;
+use lemmy_api_common::context::LemmyContext;
+use lemmy_db_schema::traits::ApubActor;
+use lemmy_db_views::structs::LocalUserView;
+use lemmy_utils::error::LemmyError;
 
-static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60;
-static ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG: i64 = 10;
+pub mod post_or_comment;
+pub mod search;
+pub mod user_or_community;
 
-/// Get a remote actor from its apub ID (either a person or a community). Thin wrapper around
-/// `get_or_fetch_and_upsert_person()` and `get_or_fetch_and_upsert_community()`.
+/// Resolve actor identifier like `!news@example.com` to user or community object.
 ///
-/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
-/// Otherwise it is fetched from the remote instance, stored and returned.
-pub(crate) async fn get_or_fetch_and_upsert_actor(
-  apub_id: Url,
-  context: &LemmyContext,
-  recursion_counter: &mut i32,
-) -> Result<Box<dyn ActorType>, LemmyError> {
-  let community_id = ObjectId::<ApubCommunity>::new(apub_id.clone());
-  let community = community_id.dereference(context, recursion_counter).await;
-  let actor: Box<dyn ActorType> = match community {
-    Ok(c) => Box::new(c),
-    Err(_) => {
-      let person_id = ObjectId::new(apub_id);
-      let person: ApubPerson = person_id.dereference(context, recursion_counter).await?;
-      Box::new(person)
+/// In case the requesting user is logged in and the object was not found locally, it is attempted
+/// to fetch via webfinger from the original instance.
+#[tracing::instrument(skip_all)]
+pub async fn resolve_actor_identifier<ActorType, DbActor>(
+  identifier: &str,
+  context: &Data<LemmyContext>,
+  local_user_view: &Option<LocalUserView>,
+  include_deleted: bool,
+) -> Result<ActorType, LemmyError>
+where
+  ActorType: Object<DataType = LemmyContext, Error = LemmyError>
+    + Object
+    + Actor
+    + From<DbActor>
+    + Send
+    + 'static,
+  for<'de2> <ActorType as Object>::Kind: serde::Deserialize<'de2>,
+  DbActor: ApubActor + Send + 'static,
+{
+  // remote actor
+  if identifier.contains('@') {
+    let (name, domain) = identifier
+      .splitn(2, '@')
+      .collect_tuple()
+      .expect("invalid query");
+    let actor = DbActor::read_from_name_and_domain(context.pool(), name, domain).await;
+    if actor.is_ok() {
+      Ok(actor?.into())
+    } else if local_user_view.is_some() {
+      // Fetch the actor from its home instance using webfinger
+      let actor: ActorType = webfinger_resolve_actor(identifier, context).await?;
+      Ok(actor)
+    } else {
+      Err(NotFound.into())
     }
-  };
-  Ok(actor)
-}
-
-/// Determines when a remote actor should be refetched from its instance. In release builds, this is
-/// `ACTOR_REFETCH_INTERVAL_SECONDS` after the last refetch, in debug builds
-/// `ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG`.
-///
-/// TODO it won't pick up new avatars, summaries etc until a day after.
-/// Actors need an "update" activity pushed to other servers to fix this.
-fn should_refetch_object(last_refreshed: NaiveDateTime) -> bool {
-  let update_interval = if cfg!(debug_assertions) {
-    // avoid infinite loop when fetching community outbox
-    chrono::Duration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG)
-  } else {
-    chrono::Duration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS)
-  };
-  last_refreshed.lt(&(naive_now() - update_interval))
+  }
+  // local actor
+  else {
+    let identifier = identifier.to_string();
+    Ok(
+      DbActor::read_from_name(context.pool(), &identifier, include_deleted)
+        .await?
+        .into(),
+    )
+  }
 }