forked from alemi/upub
chore(web): unified caches under trait
This commit is contained in:
parent
64ab2c3bb9
commit
1605557329
9 changed files with 76 additions and 51 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4892,6 +4892,7 @@ name = "upub-web"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"apb",
|
"apb",
|
||||||
|
"async-trait",
|
||||||
"chrono",
|
"chrono",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
|
|
@ -12,6 +12,8 @@ repository = "https://git.alemi.dev/upub.git"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-trait = "0.1"
|
||||||
|
lazy_static = "1.4"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
tracing-subscriber-wasm = "0.1"
|
tracing-subscriber-wasm = "0.1"
|
||||||
|
@ -29,7 +31,6 @@ apb = { path = "../apb", features = ["unstructured", "activitypub-fe", "activity
|
||||||
uriproxy = { path = "../utils/uriproxy/" }
|
uriproxy = { path = "../utils/uriproxy/" }
|
||||||
mdhtml = { path = "../utils/mdhtml/" }
|
mdhtml = { path = "../utils/mdhtml/" }
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
lazy_static = "1.4"
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
jrd = "0.1"
|
jrd = "0.1"
|
||||||
tld = "2.35"
|
tld = "2.35"
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn ActorHeader() -> impl IntoView {
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let user = std::sync::Arc::new(user);
|
let user = std::sync::Arc::new(user);
|
||||||
cache::OBJECTS.put(Uri::full(U::Actor, &id), user.clone());
|
cache::OBJECTS.store(&Uri::full(U::Actor, &id), user.clone());
|
||||||
Ok(user)
|
Ok(user)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,7 +272,7 @@ pub fn LikeButton(
|
||||||
new = new.set_likes(apb::Node::object(likes.clone().set_total_items(Some(count + 1))));
|
new = new.set_likes(apb::Node::object(likes.clone().set_total_items(Some(count + 1))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cache::OBJECTS.put(target, Arc::new(new));
|
cache::OBJECTS.store(&target, Arc::new(new));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => tracing::error!("failed sending like: {e}"),
|
Err(e) => tracing::error!("failed sending like: {e}"),
|
||||||
|
|
|
@ -46,6 +46,7 @@ pub struct FiltersConfig {
|
||||||
impl FiltersConfig {
|
impl FiltersConfig {
|
||||||
pub fn visible(&self, item: &crate::Object) -> bool {
|
pub fn visible(&self, item: &crate::Object) -> bool {
|
||||||
use apb::{Object, Activity};
|
use apb::{Object, Activity};
|
||||||
|
use crate::Cache;
|
||||||
|
|
||||||
let type_filter = match item.object_type().unwrap_or(apb::ObjectType::Object) {
|
let type_filter = match item.object_type().unwrap_or(apb::ObjectType::Object) {
|
||||||
apb::ObjectType::Note | apb::ObjectType::Document(_) => self.orphans,
|
apb::ObjectType::Note | apb::ObjectType::Document(_) => self.orphans,
|
||||||
|
|
106
web/src/lib.rs
106
web/src/lib.rs
|
@ -23,38 +23,73 @@ pub const DEFAULT_AVATAR_URL: &str = "https://cdn.alemi.dev/social/gradient.png"
|
||||||
pub const NAME: &str = "μ";
|
pub const NAME: &str = "μ";
|
||||||
pub const DEFAULT_COLOR: &str = "#BF616A";
|
pub const DEFAULT_COLOR: &str = "#BF616A";
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::{ops::Deref, sync::Arc};
|
||||||
use uriproxy::UriClass;
|
use uriproxy::UriClass;
|
||||||
|
|
||||||
pub mod cache {
|
|
||||||
use super::{ObjectCache, WebfingerCache};
|
|
||||||
lazy_static::lazy_static! {
|
|
||||||
pub static ref OBJECTS: ObjectCache = ObjectCache::default();
|
|
||||||
pub static ref WEBFINGER: WebfingerCache = WebfingerCache::default();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Object = Arc<serde_json::Value>;
|
pub type Object = Arc<serde_json::Value>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
pub mod cache {
|
||||||
pub struct ObjectCache(Arc<dashmap::DashMap<String, Object>>);
|
use super::DashmapCache;
|
||||||
|
lazy_static::lazy_static! {
|
||||||
impl ObjectCache {
|
pub static ref OBJECTS: DashmapCache<super::Object> = DashmapCache::default();
|
||||||
pub fn get(&self, k: &str) -> Option<Object> {
|
pub static ref WEBFINGER: DashmapCache<String> = DashmapCache::default();
|
||||||
self.0.get(k).map(|x| x.clone())
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_or(&self, k: &str, or: Object) -> Object {
|
#[derive(Debug)]
|
||||||
self.get(k).unwrap_or(or)
|
pub enum LookupStatus<T> {
|
||||||
|
Resolving,
|
||||||
|
Found(T),
|
||||||
|
NotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put(&self, k: String, v: Object) {
|
impl<T> LookupStatus<T> {
|
||||||
self.0.insert(k, v);
|
fn inner(&self) -> Option<&T> {
|
||||||
|
if let Self::Found(x) = self {
|
||||||
|
return Some(x);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Cache {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn lookup(&self, key: &str) -> Option<impl Deref<Target = LookupStatus<Self::Item>>>;
|
||||||
|
fn store(&self, key: &str, value: Self::Item) -> Option<Self::Item>;
|
||||||
|
|
||||||
|
fn get(&self, key: &str) -> Option<Self::Item> where Self::Item : Clone {
|
||||||
|
Some(self.lookup(key)?.deref().inner()?.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or(&self, key: &str, or: Self::Item) -> Self::Item where Self::Item : Clone {
|
||||||
|
self.get(key).unwrap_or(or)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or_default(&self, key: &str) -> Self::Item where Self::Item : Clone + Default {
|
||||||
|
self.get(key).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct DashmapCache<T>(Arc<dashmap::DashMap<String, LookupStatus<T>>>);
|
||||||
|
|
||||||
|
impl<T> Cache for DashmapCache<T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn lookup(&self, key: &str) -> Option<impl Deref<Target = LookupStatus<Self::Item>>> {
|
||||||
|
self.0.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store(&self, key: &str, value: Self::Item) -> Option<Self::Item> {
|
||||||
|
self.0.insert(key.to_string(), LookupStatus::Found(value))
|
||||||
|
.and_then(|x| if let LookupStatus::Found(x) = x { Some(x) } else { None } )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO would be cool unifying a bit the fetch code too
|
||||||
|
|
||||||
|
impl DashmapCache<Object> {
|
||||||
pub async fn fetch(&self, k: &str, kind: UriClass) -> reqwest::Result<Object> {
|
pub async fn fetch(&self, k: &str, kind: UriClass) -> reqwest::Result<Object> {
|
||||||
match self.get(k) {
|
match self.get(k) {
|
||||||
Some(x) => Ok(x),
|
Some(x) => Ok(x),
|
||||||
|
@ -63,43 +98,30 @@ impl ObjectCache {
|
||||||
.await?
|
.await?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?;
|
.await?;
|
||||||
self.put(k.to_string(), Arc::new(obj));
|
self.store(k, Arc::new(obj));
|
||||||
Ok(self.get(k).expect("not found in cache after insertion"))
|
Ok(self.get(k).expect("not found in cache after insertion"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl DashmapCache<String> {
|
||||||
enum LookupStatus {
|
|
||||||
Resolving,
|
|
||||||
Found(String),
|
|
||||||
NotFound,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct WebfingerCache(Arc<dashmap::DashMap<String, LookupStatus>>);
|
|
||||||
|
|
||||||
impl WebfingerCache {
|
|
||||||
pub async fn blocking_resolve(&self, user: &str, domain: &str) -> Option<String> {
|
pub async fn blocking_resolve(&self, user: &str, domain: &str) -> Option<String> {
|
||||||
if let Some(x) = self.get(user, domain) { return Some(x); }
|
if let Some(x) = self.resource(user, domain) { return Some(x); }
|
||||||
self.fetch(user, domain).await;
|
self.fetch(user, domain).await;
|
||||||
self.get(user, domain)
|
self.resource(user, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(&self, user: &str, domain: &str) -> Option<String> {
|
pub fn resolve(&self, user: &str, domain: &str) -> Option<String> {
|
||||||
if let Some(x) = self.get(user, domain) { return Some(x); }
|
if let Some(x) = self.resource(user, domain) { return Some(x); }
|
||||||
let (_self, user, domain) = (self.clone(), user.to_string(), domain.to_string());
|
let (_self, user, domain) = (self.clone(), user.to_string(), domain.to_string());
|
||||||
leptos::spawn_local(async move { _self.fetch(&user, &domain).await });
|
leptos::spawn_local(async move { _self.fetch(&user, &domain).await });
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, user: &str, domain: &str) -> Option<String> {
|
fn resource(&self, user: &str, domain: &str) -> Option<String> {
|
||||||
let query = format!("{user}@{domain}");
|
let query = format!("{user}@{domain}");
|
||||||
match self.0.get(&query).map(|x| (*x).clone())? {
|
self.get(&query)
|
||||||
LookupStatus::Resolving | LookupStatus::NotFound => None,
|
|
||||||
LookupStatus::Found(x) => Some(x),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch(&self, user: &str, domain: &str) {
|
async fn fetch(&self, user: &str, domain: &str) {
|
||||||
|
|
|
@ -28,10 +28,10 @@ pub fn ObjectView() -> impl IntoView {
|
||||||
if let Ok(user) = Http::fetch::<serde_json::Value>(
|
if let Ok(user) = Http::fetch::<serde_json::Value>(
|
||||||
&Uri::api(U::Actor, author, true), auth
|
&Uri::api(U::Actor, author, true), auth
|
||||||
).await {
|
).await {
|
||||||
cache::OBJECTS.put(Uri::full(U::Actor, author), Arc::new(user));
|
cache::OBJECTS.store(&Uri::full(U::Actor, author), Arc::new(user));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cache::OBJECTS.put(Uri::full(U::Object, &oid), obj.clone());
|
cache::OBJECTS.store(&Uri::full(U::Object, &oid), obj.clone());
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
URL_BASE,
|
URL_BASE,
|
||||||
Http, Uri,
|
Http, Uri,
|
||||||
cache,
|
Cache, cache, // TODO move Cache under cache
|
||||||
app::{Feeds, Loader},
|
app::{Feeds, Loader},
|
||||||
auth::Auth,
|
auth::Auth,
|
||||||
page::*,
|
page::*,
|
||||||
|
|
|
@ -115,7 +115,7 @@ async fn process_activities(activities: Vec<serde_json::Value>, auth: Auth) -> V
|
||||||
actors_seen.insert(attributed_to);
|
actors_seen.insert(attributed_to);
|
||||||
}
|
}
|
||||||
if let Ok(object_uri) = object.id() {
|
if let Ok(object_uri) = object.id() {
|
||||||
cache::OBJECTS.put(object_uri.to_string(), Arc::new(object.clone()));
|
cache::OBJECTS.store(object_uri, Arc::new(object.clone()));
|
||||||
} else {
|
} else {
|
||||||
tracing::warn!("embedded object without id: {object:?}");
|
tracing::warn!("embedded object without id: {object:?}");
|
||||||
}
|
}
|
||||||
|
@ -136,8 +136,8 @@ async fn process_activities(activities: Vec<serde_json::Value>, auth: Auth) -> V
|
||||||
let object_id = activity.object().id().str();
|
let object_id = activity.object().id().str();
|
||||||
if let Some(activity_id) = activity.id().str() {
|
if let Some(activity_id) = activity.id().str() {
|
||||||
out.push(activity_id.to_string());
|
out.push(activity_id.to_string());
|
||||||
cache::OBJECTS.put(
|
cache::OBJECTS.store(
|
||||||
activity_id.to_string(),
|
&activity_id,
|
||||||
Arc::new(activity.clone().set_object(apb::Node::maybe_link(object_id)))
|
Arc::new(activity.clone().set_object(apb::Node::maybe_link(object_id)))
|
||||||
);
|
);
|
||||||
} else if let Some(object_id) = activity.object().id().str() {
|
} else if let Some(object_id) = activity.object().id().str() {
|
||||||
|
@ -170,7 +170,7 @@ async fn process_activities(activities: Vec<serde_json::Value>, auth: Auth) -> V
|
||||||
|
|
||||||
async fn fetch_and_update(kind: U, id: String, auth: Auth) {
|
async fn fetch_and_update(kind: U, id: String, auth: Auth) {
|
||||||
match Http::fetch(&Uri::api(kind, &id, false), auth).await {
|
match Http::fetch(&Uri::api(kind, &id, false), auth).await {
|
||||||
Ok(data) => cache::OBJECTS.put(id, Arc::new(data)),
|
Ok(data) => { cache::OBJECTS.store(&id, Arc::new(data)); },
|
||||||
Err(e) => console_warn(&format!("could not fetch '{id}': {e}")),
|
Err(e) => console_warn(&format!("could not fetch '{id}': {e}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue