Files
k-ap/src/service/fetch.rs
Gabriel Kaszewski 7171a1791a feat: actor cache TTL with staleness-aware re-fetch
Adds fetched_at to RemoteActor, configurable TTL via builder
(.actor_cache_ttl_secs, default 24h), and get_or_refresh_remote_actor
helper that re-fetches stale actors from origin.

Closes #3
2026-05-30 02:46:54 +02:00

63 lines
2.2 KiB
Rust

use activitypub_federation::fetch::object_id::ObjectId;
use url::Url;
use crate::actors::DbActor;
use crate::repository::RemoteActor;
use super::ActivityPubService;
impl ActivityPubService {
/// Fetch a remote ActivityPub resource with HTTP Signatures.
///
/// Requires `signed_fetch_actor_id` to have been set on the builder.
/// Returns the raw JSON value of the remote resource.
pub async fn signed_fetch(&self, url: &Url) -> anyhow::Result<serde_json::Value> {
let data = self.federation_config.to_request_data();
let res = activitypub_federation::fetch::fetch_object_http::<
crate::data::FederationData,
serde_json::Value,
>(url, &data)
.await
.map_err(|e| anyhow::anyhow!("{e}"))?;
Ok(res.object)
}
/// Get a cached remote actor, re-fetching from origin if stale.
///
/// Returns `None` if the actor has never been seen. Staleness is
/// determined by `actor_cache_ttl_secs` (builder config).
pub async fn get_or_refresh_remote_actor(
&self,
actor_url: &str,
) -> anyhow::Result<Option<RemoteActor>> {
let data = self.federation_config.to_request_data();
let cached = data.actor_repo.get_remote_actor(actor_url).await?;
if let Some(ref actor) = cached {
let is_fresh = actor
.fetched_at
.map(|t| {
let age = chrono::Utc::now().signed_duration_since(t);
age < chrono::Duration::from_std(data.actor_cache_ttl).unwrap_or_default()
})
.unwrap_or(true);
if is_fresh {
return Ok(cached);
}
}
let url = match Url::parse(actor_url) {
Ok(u) => u,
Err(_) => return Ok(cached),
};
match ObjectId::<DbActor>::from(url)
.dereference_forced(&data)
.await
{
Ok(_) => Ok(data.actor_repo.get_remote_actor(actor_url).await?),
Err(e) => {
tracing::warn!(actor_url, error = %e, "re-fetch failed, using stale cache");
Ok(cached)
}
}
}
}