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;
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>(
31 context: &LemmyContext,
32 request_counter: &mut i32,
33 ) -> Result<DbUrl, LemmyError>
35 Kind: ApubObject<DataType = LemmyContext, Error = LemmyError> + ActorType + Send + 'static,
36 for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
38 let protocol = context.settings().get_protocol_string();
39 let (_, domain) = identifier
42 .ok_or_else(|| LemmyError::from_message("Invalid webfinger query, missing domain"))?;
43 let fetch_url = format!(
44 "{}://{}/.well-known/webfinger?resource=acct:{}",
45 protocol, domain, identifier
47 debug!("Fetching webfinger url: {}", &fetch_url);
49 *request_counter += 1;
50 if *request_counter > context.settings().federation.http_fetch_retry_limit {
51 return Err(LemmyError::from_message("Request retry limit reached"));
54 let response = context.client().get(&fetch_url).send().await?;
56 let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?;
58 let links: Vec<Url> = res
62 if let Some(type_) = &link.kind {
63 type_.starts_with("application/")
68 .filter_map(|l| l.href.clone())
71 let object = ObjectId::<Kind>::new(l)
72 .dereference(context, local_instance(context), request_counter)
75 return object.map(|o| o.actor_id().into());
78 let err = anyhow!("Failed to resolve actor for {}", identifier);
79 Err(LemmyError::from_error_message(err, "failed_to_resolve"))