From eba399abe50fa1830b263dcfc93a786b99049f56 Mon Sep 17 00:00:00 2001 From: alemi <me@alemi.dev> Date: Wed, 22 Jan 2025 02:07:16 +0100 Subject: [PATCH] fix(web): rely on user_id from server this way we don't have to construct it ourselves every time with URL_BASE. i think it's a bit weaker this way tho --- web/src/app.rs | 4 +-- web/src/auth.rs | 2 +- web/src/components/post.rs | 19 ++++++------ web/src/components/user.rs | 2 +- web/src/lib.rs | 63 +++++++++++++++++--------------------- web/src/objects/item.rs | 4 +-- web/src/page/config.rs | 2 +- 7 files changed, 45 insertions(+), 51 deletions(-) diff --git a/web/src/app.rs b/web/src/app.rs index e523780..54cceff 100644 --- a/web/src/app.rs +++ b/web/src/app.rs @@ -130,7 +130,7 @@ pub fn App() -> impl IntoView { <Route path=path!("home") view=move || if auth.present() { Either::Left(view! { <Loadable - base=format!("{URL_BASE}/actors/{}/inbox/page", auth.username()) + base=format!("{}/inbox/page", auth.user_id()) element=move |obj| view! { <Item item=obj sep=true /> } /> }) @@ -155,7 +155,7 @@ pub fn App() -> impl IntoView { <Route path=path!("notifications") view=move || if auth.present() { Either::Left(view! { <Loadable - base=format!("{URL_BASE}/actors/{}/notifications/page", auth.username()) + base=format!("{}/notifications/page", auth.user_id()) element=move |obj| view! { <Item item=obj sep=true always=true /> } /> }) diff --git a/web/src/auth.rs b/web/src/auth.rs index 7e6cd67..ddee289 100644 --- a/web/src/auth.rs +++ b/web/src/auth.rs @@ -32,7 +32,7 @@ impl Auth { } pub fn outbox(&self) -> String { - format!("{URL_BASE}/actors/{}/outbox", self.username()) + format!("{}/outbox", self.user_id()) } pub async fn refresh( diff --git a/web/src/components/post.rs b/web/src/components/post.rs index 5fddf12..dd98ccc 100644 --- a/web/src/components/post.rs +++ b/web/src/components/post.rs @@ -89,19 +89,20 @@ impl Privacy { } } - pub fn address(&self, user: &str) -> (Vec<String>, Vec<String>) { + // TODO this is weird... should probably come from core or apb + pub fn address(&self, user_id: &str) -> (Vec<String>, Vec<String>) { match self { Self::Broadcast => ( vec![apb::target::PUBLIC.to_string()], - vec![format!("{URL_BASE}/actors/{user}/followers")], + vec![format!("{user_id}/followers")], ), Self::Public => ( vec![], - vec![apb::target::PUBLIC.to_string(), format!("{URL_BASE}/actors/{user}/followers")], + vec![apb::target::PUBLIC.to_string(), format!("{user_id}/followers")], ), Self::Private => ( vec![], - vec![format!("{URL_BASE}/actors/{user}/followers")], + vec![format!("{user_id}/followers")], ), Self::Direct => ( vec![], @@ -133,7 +134,7 @@ pub fn PrivacySelector(setter: WriteSignal<Privacy>) -> impl IntoView { <td> {move || { let p = privacy.get(); - let (to, cc) = p.address(&auth.username()); + let (to, cc) = p.address(&auth.user_id()); view! { <PrivacyMarker privacy=p to=to cc=cc big=true /> } @@ -166,7 +167,7 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView { if let Some((name, domain)) = stripped.split_once('@') { if let Some(tld) = domain.split('.').last() { if tld::exist(tld) { - if let Some(uid) = cache::WEBFINGER.blocking_resolve(name, domain).await { + if let Some(uid) = cache::WEBFINGER.blocking_resolve(name, domain, auth).await { out.push(TextMatch::Mention { name: name.to_string(), domain: domain.to_string(), href: uid }); } } @@ -236,7 +237,7 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView { set_posting.set(true); leptos::task::spawn_local(async move { let summary = get_if_some(summary_ref); - let (mut to_vec, cc_vec) = privacy.get().address(&auth.username()); + let (mut to_vec, cc_vec) = privacy.get().address(&auth.user_id()); let mut mention_tags : Vec<serde_json::Value> = mentions.get() .map(|x| x.take()) .unwrap_or_default() @@ -380,7 +381,7 @@ pub fn AdvancedPostBox(advanced: WriteSignal<bool>) -> impl IntoView { <td class="w-66"><input class="w-100" type="text" node_ref=bto_ref title="bto" placeholder="bto" /></td> </tr> <tr> - <td class="w-33"><input class="w-100" type="text" node_ref=cc_ref title="cc" placeholder="cc" value=format!("{URL_BASE}/actors/{}/followers", auth.username()) /></td> + <td class="w-33"><input class="w-100" type="text" node_ref=cc_ref title="cc" placeholder="cc" value=format!("{}/followers", auth.user_id()) /></td> <td class="w-33"><input class="w-100" type="text" node_ref=bcc_ref title="bcc" placeholder="bcc" /></td> </tr> </table> @@ -424,7 +425,7 @@ pub fn AdvancedPostBox(advanced: WriteSignal<bool>) -> impl IntoView { apb::Node::maybe_link(object_id) } ); - let target_url = format!("{URL_BASE}/actors/{}/outbox", auth.username()); + let target_url = auth.outbox(); match Http::post(&target_url, &payload, auth).await { Err(e) => set_error.set(Some(e.to_string())), Ok(()) => set_error.set(None), diff --git a/web/src/components/user.rs b/web/src/components/user.rs index d8a7745..7228191 100644 --- a/web/src/components/user.rs +++ b/web/src/components/user.rs @@ -31,7 +31,7 @@ pub fn ActorBanner(object: crate::Doc) -> impl IntoView { let uri = Uri::web(U::Actor, &uid); let avatar_url = object.icon_url().unwrap_or(FALLBACK_IMAGE_URL.into()); let username = object.preferred_username().unwrap_or_default().to_string(); - let domain = object.id().unwrap_or_default().replace("https://", "").split('/').next().unwrap_or_default().to_string(); + let domain = object.id().unwrap_or_default().replace("https://", "").replace("http://", "").split('/').next().unwrap_or_default().to_string(); let display_name = object.name().unwrap_or_default().to_string(); view! { <div> diff --git a/web/src/lib.rs b/web/src/lib.rs index 9df932c..9a582b7 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -179,16 +179,16 @@ impl DashmapCache<Doc> { } impl DashmapCache<String> { - pub async fn blocking_resolve(&self, user: &str, domain: &str) -> Option<String> { + pub async fn blocking_resolve(&self, user: &str, domain: &str, auth: Auth) -> Option<String> { if let Some(x) = self.resource(user, domain) { return Some(x); } - self.fetch(user, domain).await; + self.fetch(user, domain, auth).await; self.resource(user, domain) } - pub fn resolve(&self, user: &str, domain: &str) -> Option<String> { + pub fn resolve(&self, user: &str, domain: &str, auth: Auth) -> Option<String> { if let Some(x) = self.resource(user, domain) { return Some(x); } let (_self, user, domain) = (self.clone(), user.to_string(), domain.to_string()); - leptos::task::spawn_local(async move { _self.fetch(&user, &domain).await }); + leptos::task::spawn_local(async move { _self.fetch(&user, &domain, auth).await }); None } @@ -197,32 +197,20 @@ impl DashmapCache<String> { self.get(&query) } - async fn fetch(&self, user: &str, domain: &str) { + async fn fetch(&self, user: &str, domain: &str, auth: Auth) { 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::<jrd::JsonResourceDescriptor>().await { - Ok(doc) => { - if let Some(uid) = doc.links.into_iter().find(|x| x.rel == "self").and_then(|x| x.href) { - 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:?}"); + match crate::Http::fetch::<jrd::JsonResourceDescriptor>(&format!("{URL_BASE}/.well-known/webfinger?resource=acct:{query}"), auth).await { + Ok(doc) => { + if let Some(uid) = doc.links.into_iter().find(|x| x.rel == "self").and_then(|x| x.href) { + self.0.insert(query, LookupStatus::Found(uid)); + } else { self.0.insert(query, LookupStatus::NotFound); - }, + } }, Err(e) => { - tracing::error!("failed accessing webfinger server: {e:?}"); - self.0.remove(&query); + tracing::error!("could not resolve webfinbger: {e:?}"); + self.0.insert(query, LookupStatus::NotFound); }, } } @@ -237,12 +225,25 @@ pub struct IdParam { pub struct Http; impl Http { + // TODO not really great.... also checked only once + pub fn location() -> &'static str { + static LOCATION: std::sync::OnceLock<String> = std::sync::OnceLock::new(); + LOCATION.get_or_init(|| + web_sys::window() + .expect("could not access window element") + .location() + .origin() + .expect("could not access location origin") + ).as_str() + } + pub async fn request<T: serde::ser::Serialize>( method: reqwest::Method, url: &str, data: Option<&T>, auth: Auth, ) -> reqwest::Result<reqwest::Response> { + tracing::info!("making request to {url}"); use leptos::prelude::GetUntracked; // TODO while in web environments it's ok (and i'd say good!) to fetch with relative urls, @@ -252,15 +253,7 @@ impl Http { // prod deployments). relevant issue: https://github.com/seanmonstar/reqwest/issues/1433 let mut url = url.to_string(); if !url.starts_with("http") { - static LOCATION: std::sync::OnceLock<String> = std::sync::OnceLock::new(); - let base = LOCATION.get_or_init(|| - web_sys::window() - .expect("could not access window element") - .location() - .origin() - .expect("could not access location origin") - ); - url = format!("{base}{url}"); + url = format!("{}{url}", Self::location()); } let mut req = reqwest::Client::new() @@ -311,7 +304,7 @@ impl Uri { } pub fn short(url: &str) -> String { - if url.starts_with(URL_BASE) || url.starts_with('/') { + if url.starts_with(Http::location()) || url.starts_with('/') { uriproxy::decompose(url) } else if url.starts_with("https://") || url.starts_with("http://") { uriproxy::compact(url) diff --git a/web/src/objects/item.rs b/web/src/objects/item.rs index 41e206d..d56f240 100644 --- a/web/src/objects/item.rs +++ b/web/src/objects/item.rs @@ -267,7 +267,7 @@ pub fn LikeButton( let (mut to, cc) = if private { (vec![], vec![]) } else { - privacy.get().address(&auth.username()) + privacy.get().address(&auth.user_id()) }; to.push(author.clone()); let payload = serde_json::Value::Object(serde_json::Map::default()) @@ -343,7 +343,7 @@ pub fn RepostButton(n: i32, target: String, author: String) -> impl IntoView { if !auth.present() { return; } if !clicked.get() { return; } set_clicked.set(false); - let (mut to, cc) = privacy.get().address(&auth.username()); + let (mut to, cc) = privacy.get().address(&auth.user_id()); to.push(author.clone()); let payload = serde_json::Value::Object(serde_json::Map::default()) .set_activity_type(Some(apb::ActivityType::Announce)) diff --git a/web/src/page/config.rs b/web/src/page/config.rs index e23568d..938d75e 100644 --- a/web/src/page/config.rs +++ b/web/src/page/config.rs @@ -187,7 +187,7 @@ pub fn ConfigPage(setter: WriteSignal<crate::Config>) -> impl IntoView { )); leptos::task::spawn_local(async move { - if let Err(e) = Http::post(&format!("{id}/outbox"), &payload, auth).await { + if let Err(e) = Http::post(&auth.outbox(), &payload, auth).await { tracing::error!("could not send update activity: {e}"); } });