2 use itertools::Itertools;
5 traits::{ActorType, ApubObject},
7 use lemmy_db_schema::newtypes::DbUrl;
9 request::{retry, RecvError},
12 use lemmy_websocket::LemmyContext;
13 use serde::{Deserialize, Serialize};
17 #[derive(Serialize, Deserialize, Debug)]
18 pub struct WebfingerLink {
19 pub rel: Option<String>,
20 #[serde(rename = "type")]
21 pub kind: Option<String>,
22 pub href: Option<Url>,
25 #[derive(Serialize, Deserialize, Debug)]
26 pub struct WebfingerResponse {
28 pub links: Vec<WebfingerLink>,
31 /// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
33 #[tracing::instrument(skip_all)]
34 pub(crate) async fn webfinger_resolve_actor<Kind>(
36 context: &LemmyContext,
37 request_counter: &mut i32,
38 ) -> Result<DbUrl, LemmyError>
40 Kind: ApubObject<DataType = LemmyContext> + ActorType + Send + 'static,
41 for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
43 let protocol = context.settings().get_protocol_string();
44 let (_, domain) = identifier
47 .expect("invalid query");
48 let fetch_url = format!(
49 "{}://{}/.well-known/webfinger?resource=acct:{}",
50 protocol, domain, identifier
52 debug!("Fetching webfinger url: {}", &fetch_url);
54 *request_counter += 1;
55 if *request_counter > context.settings().http_fetch_retry_limit {
56 return Err(LemmyError::from_message("Request retry limit reached"));
59 let response = retry(|| context.client().get(&fetch_url).send()).await?;
61 let res: WebfingerResponse = response
64 .map_err(|e| RecvError(e.to_string()))?;
66 let links: Vec<Url> = res
70 if let Some(type_) = &link.kind {
71 type_.starts_with("application/")
76 .map(|l| l.href.clone())
80 let object = ObjectId::<Kind>::new(l)
81 .dereference(context, context.client(), request_counter)
84 return object.map(|o| o.actor_id().into());
87 let err = anyhow!("Failed to resolve actor for {}", identifier);
88 Err(LemmyError::from_error_message(err, "failed_to_resolve"))