From b700e06d1066ef79c61669355f296ca94e24e5e6 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 23 May 2024 16:16:27 +0200 Subject: [PATCH] feat(web): webfinger cache --- web/Cargo.toml | 1 + web/src/lib.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/web/Cargo.toml b/web/Cargo.toml index 3c3ed52..7af2230 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -34,3 +34,4 @@ lazy_static = "1.4" chrono = { version = "0.4", features = ["serde"] } web-sys = { version = "0.3", features = ["Screen"] } mdhtml = { path = "../mdhtml/" } +jrd = "0.1.0" diff --git a/web/src/lib.rs b/web/src/lib.rs index bd30301..362dfc0 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -24,12 +24,13 @@ use uriproxy::UriClass; lazy_static::lazy_static! { pub static ref CACHE: ObjectCache = ObjectCache::default(); + pub static ref WEBFINGER: WebfingerCache = WebfingerCache::default(); } pub type Object = Arc; #[derive(Debug, Clone, Default)] -pub struct ObjectCache(pub Arc>); +pub struct ObjectCache(Arc>); impl ObjectCache { pub fn get(&self, k: &str) -> Option { @@ -59,6 +60,69 @@ impl ObjectCache { } } +#[derive(Debug, Clone)] +enum LookupStatus { + Resolving, + Found(String), + NotFound, +} + +#[derive(Debug, Clone, Default)] +pub struct WebfingerCache(Arc>); + +impl WebfingerCache { + pub async fn blocking_resolve(&self, user: &str, domain: &str) -> Option { + if let Some(x) = self.get(user, domain) { return Some(x); } + self.fetch(user, domain).await; + self.get(user, domain) + } + + pub fn resolve(&self, user: &str, domain: &str) -> Option { + if let Some(x) = self.get(user, domain) { return Some(x); } + let (_self, user, domain) = (self.clone(), user.to_string(), domain.to_string()); + leptos::spawn_local(async move { _self.fetch(&user, &domain).await }); + None + } + + fn get(&self, user: &str, domain: &str) -> Option { + let query = format!("{user}@{domain}"); + match self.0.get(&query).map(|x| (*x).clone())? { + LookupStatus::Resolving | LookupStatus::NotFound => None, + LookupStatus::Found(x) => Some(x), + } + } + + async fn fetch(&self, user: &str, domain: &str) { + let query = format!("{user}@{domain}"); + self.0.insert(query.to_string(), LookupStatus::Resolving); + match reqwest::get(format!("{URL_BASE}/.well-known/webfinger?resource=acct:{query}")).await { + Ok(res) => match res.error_for_status() { + Ok(res) => match res.json::().await { + Ok(doc) => { + if let Some(uid) = doc.links.into_iter().find(|x| x.rel == "self").map(|x| x.href).flatten() { + self.0.insert(query, LookupStatus::Found(uid)); + } else { + self.0.insert(query, LookupStatus::NotFound); + } + }, + Err(e) => { + tracing::error!("invalid webfinger response: {e:?}"); + self.0.remove(&query); + }, + }, + Err(e) => { + tracing::error!("could not resolve webfinbger: {e:?}"); + self.0.insert(query, LookupStatus::NotFound); + }, + }, + Err(e) => { + tracing::error!("failed accessing webfinger server: {e:?}"); + self.0.remove(&query); + }, + } + } +} + pub struct Http;