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