1 use itertools::Itertools;
4 traits::{ActorType, ApubObject},
6 use lemmy_db_schema::newtypes::DbUrl;
8 request::{retry, RecvError},
11 use lemmy_websocket::LemmyContext;
12 use serde::{Deserialize, Serialize};
16 #[derive(Serialize, Deserialize, Debug)]
17 pub struct WebfingerLink {
18 pub rel: Option<String>,
19 #[serde(rename = "type")]
20 pub kind: Option<String>,
21 pub href: Option<Url>,
24 #[derive(Serialize, Deserialize, Debug)]
25 pub struct WebfingerResponse {
27 pub links: Vec<WebfingerLink>,
30 /// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
32 #[tracing::instrument(skip_all)]
33 pub(crate) async fn webfinger_resolve_actor<Kind>(
35 context: &LemmyContext,
36 request_counter: &mut i32,
37 ) -> Result<DbUrl, LemmyError>
39 Kind: ApubObject<DataType = LemmyContext> + ActorType + Send + 'static,
40 for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
42 let protocol = context.settings().get_protocol_string();
43 let (_, domain) = identifier
46 .expect("invalid query");
47 let fetch_url = format!(
48 "{}://{}/.well-known/webfinger?resource=acct:{}",
49 protocol, domain, identifier
51 debug!("Fetching webfinger url: {}", &fetch_url);
53 *request_counter += 1;
54 if *request_counter > context.settings().http_fetch_retry_limit {
55 return Err(LemmyError::from_message("Request retry limit reached"));
58 let response = retry(|| context.client().get(&fetch_url).send()).await?;
60 let res: WebfingerResponse = response
63 .map_err(|e| RecvError(e.to_string()))?;
65 let links: Vec<Url> = res
69 if let Some(type_) = &link.kind {
70 type_.starts_with("application/")
75 .map(|l| l.href.clone())
79 let object = ObjectId::<Kind>::new(l)
80 .dereference(context, context.client(), request_counter)
83 return object.map(|o| o.actor_id().into());
86 let error = LemmyError::from(anyhow::anyhow!(
87 "Failed to resolve actor for {}",
90 Err(error.with_message("failed_to_resolve"))