1 use crate::{local_instance, ActorType};
2 use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
4 use itertools::Itertools;
5 use lemmy_db_schema::{newtypes::DbUrl, source::local_site::LocalSite};
6 use lemmy_utils::error::LemmyError;
7 use lemmy_websocket::LemmyContext;
8 use serde::{Deserialize, Serialize};
12 #[derive(Serialize, Deserialize, Debug)]
13 pub struct WebfingerLink {
14 pub rel: Option<String>,
15 #[serde(rename = "type")]
16 pub kind: Option<String>,
17 pub href: Option<Url>,
20 #[derive(Serialize, Deserialize, Debug)]
21 pub struct WebfingerResponse {
23 pub links: Vec<WebfingerLink>,
26 /// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
28 #[tracing::instrument(skip_all)]
29 pub(crate) async fn webfinger_resolve_actor<Kind>(
32 context: &LemmyContext,
33 request_counter: &mut i32,
34 ) -> Result<DbUrl, LemmyError>
36 Kind: ApubObject<DataType = LemmyContext, Error = LemmyError> + ActorType + Send + 'static,
37 for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
39 let protocol = context.settings().get_protocol_string();
40 let (_, domain) = identifier
43 .ok_or_else(|| LemmyError::from_message("Invalid webfinger query, missing domain"))?;
44 let fetch_url = format!(
45 "{}://{}/.well-known/webfinger?resource=acct:{}",
46 protocol, domain, identifier
48 debug!("Fetching webfinger url: {}", &fetch_url);
50 let local_site = LocalSite::read(context.pool()).await;
51 let http_fetch_retry_limit = local_site
53 .map(|l| l.federation_http_fetch_retry_limit)
56 *request_counter += 1;
57 if *request_counter > http_fetch_retry_limit {
58 return Err(LemmyError::from_message("Request retry limit reached"));
61 let response = context.client().get(&fetch_url).send().await?;
63 let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?;
65 let links: Vec<Url> = res
69 if let Some(type_) = &link.kind {
70 type_.starts_with("application/")
75 .filter_map(|l| l.href.clone())
78 let object_id = ObjectId::<Kind>::new(l);
79 let object = if local_only {
80 object_id.dereference_local(context).await
83 .dereference(context, local_instance(context).await, request_counter)
87 return object.map(|o| o.actor_id().into());
90 let err = anyhow!("Failed to resolve actor for {}", identifier);
91 Err(LemmyError::from_error_message(err, "failed_to_resolve"))