-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(),
+ )
+ }
}