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>(
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 *request_counter += 1;
51 if *request_counter > context.settings().federation.http_fetch_retry_limit {
52 return Err(LemmyError::from_message("Request retry limit reached"));
55 let response = context.client().get(&fetch_url).send().await?;
57 let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?;
59 let links: Vec<Url> = res
63 if let Some(type_) = &link.kind {
64 type_.starts_with("application/")
69 .filter_map(|l| l.href.clone())
72 let object_id = ObjectId::<Kind>::new(l);
73 let object = if local_only {
74 object_id.dereference_local(context).await
77 .dereference(context, local_instance(context), request_counter)
81 return object.map(|o| o.actor_id().into());
84 let err = anyhow!("Failed to resolve actor for {}", identifier);
85 Err(LemmyError::from_error_message(err, "failed_to_resolve"))