diff --git a/Cargo.lock b/Cargo.lock index a6bb8452b..8cfcdd4dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2833,6 +2833,7 @@ dependencies = [ "kitsune-cache", "kitsune-config", "kitsune-consts", + "kitsune-core", "kitsune-db", "kitsune-embed", "kitsune-federation-filter", @@ -2958,7 +2959,6 @@ dependencies = [ "eyre", "futures-util", "garde", - "globset", "hex-simd", "http", "hyper", @@ -3298,6 +3298,7 @@ dependencies = [ "hyper", "kitsune-cache", "kitsune-consts", + "kitsune-core", "kitsune-http-client", "kitsune-type", "kitsune-util", diff --git a/crates/kitsune-activitypub/Cargo.toml b/crates/kitsune-activitypub/Cargo.toml index 9fb5cffec..72872c58f 100644 --- a/crates/kitsune-activitypub/Cargo.toml +++ b/crates/kitsune-activitypub/Cargo.toml @@ -15,6 +15,7 @@ http = "0.2.10" iso8601-timestamp = "0.2.12" kitsune-cache = { path = "../kitsune-cache" } kitsune-consts = { path = "../kitsune-consts" } +kitsune-core = { path = "../kitsune-core" } kitsune-db = { path = "../kitsune-db" } kitsune-embed = { path = "../kitsune-embed" } kitsune-federation-filter = { path = "../kitsune-federation-filter" } diff --git a/crates/kitsune-activitypub/src/fetcher/actor.rs b/crates/kitsune-activitypub/src/fetcher/actor.rs index 1b99da11a..a009ac82b 100644 --- a/crates/kitsune-activitypub/src/fetcher/actor.rs +++ b/crates/kitsune-activitypub/src/fetcher/actor.rs @@ -1,4 +1,4 @@ -use super::{FetchOptions, Fetcher}; +use super::Fetcher; use crate::{ error::{Error, Result}, process_attachments, @@ -7,6 +7,7 @@ use autometrics::autometrics; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use kitsune_cache::CacheBackend; +use kitsune_core::traits::{AccountFetchOptions, Resolver}; use kitsune_db::{ model::account::{Account, AccountConflictChangeset, NewAccount, UpdateAccountMedia}, schema::accounts, @@ -25,7 +26,7 @@ impl Fetcher { /// - Panics if the URL doesn't contain a host section #[instrument(skip(self))] #[autometrics(track_concurrency)] - pub async fn fetch_actor(&self, opts: FetchOptions<'_>) -> Result { + pub async fn fetch_actor(&self, opts: AccountFetchOptions<'_>) -> Result { // Obviously we can't hit the cache nor the database if we wanna refetch the actor if !opts.refetch { if let Some(user) = self.user_cache.get(opts.url).await? { @@ -64,7 +65,7 @@ impl Fetcher { let used_webfinger = if fetch_webfinger { match self .webfinger - .resolve_actor(&actor.preferred_username, domain) + .resolve_account(&actor.preferred_username, domain) .await? { Some(resource) if resource.uri == actor.id => { diff --git a/crates/kitsune-activitypub/src/fetcher/mod.rs b/crates/kitsune-activitypub/src/fetcher/mod.rs index fb9e12971..f7d1073f0 100644 --- a/crates/kitsune-activitypub/src/fetcher/mod.rs +++ b/crates/kitsune-activitypub/src/fetcher/mod.rs @@ -3,8 +3,9 @@ use headers::{ContentType, HeaderMapExt}; use http::HeaderValue; use kitsune_cache::ArcCache; use kitsune_consts::USER_AGENT; +use kitsune_core::traits::{AccountFetchOptions, Fetcher as FetcherTrait}; use kitsune_db::{ - model::{account::Account, post::Post}, + model::{account::Account, custom_emoji::CustomEmoji, post::Post}, PgPool, }; use kitsune_embed::Client as EmbedClient; @@ -23,31 +24,6 @@ mod actor; mod emoji; mod object; -#[derive(Clone, Debug, TypedBuilder)] -/// Options passed to the fetcher -pub struct FetchOptions<'a> { - /// Prefetched WebFinger `acct` URI - #[builder(default, setter(strip_option))] - acct: Option<(&'a str, &'a str)>, - - /// Refetch the ActivityPub entity - /// - /// This is mainly used to refresh possibly stale actors - /// - /// Default: false - #[builder(default = false)] - refetch: bool, - - /// URL of the ActivityPub entity - url: &'a str, -} - -impl<'a> From<&'a str> for FetchOptions<'a> { - fn from(value: &'a str) -> Self { - Self::builder().url(value).build() - } -} - #[derive(Clone, TypedBuilder)] pub struct Fetcher { #[builder(default = @@ -124,3 +100,19 @@ impl Fetcher { Ok(response.jsonld().await?) } } + +impl FetcherTrait for Fetcher { + type Error = Error; + + async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result { + self.fetch_actor(opts).await + } + + async fn fetch_emoji(&self, url: &str) -> Result { + self.fetch_emoji(url).await + } + + async fn fetch_post(&self, url: &str) -> Result { + self.fetch_object(url).await + } +} diff --git a/crates/kitsune-core/Cargo.toml b/crates/kitsune-core/Cargo.toml index 429301a22..d5c4a446c 100644 --- a/crates/kitsune-core/Cargo.toml +++ b/crates/kitsune-core/Cargo.toml @@ -22,7 +22,6 @@ garde = { version = "0.16.2", default-features = false, features = [ "regex", "serde", ] } -globset = "0.4.13" hex-simd = { version = "0.8.0", features = ["unstable"] } http = "0.2.10" img-parts = "0.3.0" diff --git a/crates/kitsune-core/src/lib.rs b/crates/kitsune-core/src/lib.rs index 8611afea3..18c27c37b 100644 --- a/crates/kitsune-core/src/lib.rs +++ b/crates/kitsune-core/src/lib.rs @@ -14,13 +14,14 @@ extern crate tracing; pub mod error; pub mod event; -pub mod job; -pub mod mapping; -pub mod resolve; -pub mod service; -pub mod state; -pub mod util; - +//pub mod mapping; +//pub mod resolve; +//pub mod service; +//pub mod state; +pub mod traits; +//pub mod util; + +/* use self::{ activitypub::Fetcher, job::KitsuneContextRepo, @@ -364,3 +365,5 @@ pub async fn prepare_state( webfinger, }) } + +*/ diff --git a/crates/kitsune-core/src/traits.rs b/crates/kitsune-core/src/traits.rs new file mode 100644 index 000000000..8320f3968 --- /dev/null +++ b/crates/kitsune-core/src/traits.rs @@ -0,0 +1,66 @@ +use kitsune_db::model::{account::Account, custom_emoji::CustomEmoji, post::Post}; +use serde::{Deserialize, Serialize}; +use std::future::Future; +use typed_builder::TypedBuilder; + +#[derive(Clone, Debug, TypedBuilder)] +/// Options passed to the fetcher +pub struct AccountFetchOptions<'a> { + /// Prefetched WebFinger `acct` URI + #[builder(default, setter(strip_option))] + pub acct: Option<(&'a str, &'a str)>, + + /// Refetch the ActivityPub entity + /// + /// This is mainly used to refresh possibly stale actors + /// + /// Default: false + #[builder(default = false)] + pub refetch: bool, + + /// URL of the ActivityPub entity + pub url: &'a str, +} + +impl<'a> From<&'a str> for AccountFetchOptions<'a> { + fn from(value: &'a str) -> Self { + Self::builder().url(value).build() + } +} + +/// Description of a resolved account +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AccountResource { + /// The `self` link (the account's URI) + pub uri: String, + /// The username part of the canonical `acct:` URI + pub username: String, + /// The host component of the canonical `acct:` URI + pub domain: String, +} + +pub trait Fetcher { + type Error; + + fn fetch_account( + &self, + opts: AccountFetchOptions<'_>, + ) -> impl Future> + Send; + + fn fetch_emoji( + &self, + url: &str, + ) -> impl Future> + Send; + + fn fetch_post(&self, url: &str) -> impl Future> + Send; +} + +pub trait Resolver { + type Error; + + fn resolve_account( + &self, + username: &str, + domain: &str, + ) -> impl Future, Self::Error>> + Send; +} diff --git a/crates/kitsune-webfinger/Cargo.toml b/crates/kitsune-webfinger/Cargo.toml index b7d234efc..3bf635653 100644 --- a/crates/kitsune-webfinger/Cargo.toml +++ b/crates/kitsune-webfinger/Cargo.toml @@ -10,6 +10,7 @@ futures-util = "0.3.29" http = "0.2.10" kitsune-cache = { path = "../kitsune-cache" } kitsune-consts = { path = "../kitsune-consts" } +kitsune-core = { path = "../kitsune-core" } kitsune-http-client = { path = "../kitsune-http-client" } kitsune-type = { path = "../kitsune-type" } kitsune-util = { path = "../kitsune-util" } diff --git a/crates/kitsune-webfinger/src/lib.rs b/crates/kitsune-webfinger/src/lib.rs index 3d8a90e9d..fac28266e 100644 --- a/crates/kitsune-webfinger/src/lib.rs +++ b/crates/kitsune-webfinger/src/lib.rs @@ -1,16 +1,16 @@ #[macro_use] extern crate tracing; -use crate::error::Result; +use crate::error::{Error, Result}; use autometrics::autometrics; use futures_util::future::{FutureExt, OptionFuture}; use http::{HeaderValue, StatusCode}; use kitsune_cache::{ArcCache, CacheBackend, RedisCache}; use kitsune_consts::USER_AGENT; +use kitsune_core::traits::{AccountResource, Resolver}; use kitsune_http_client::Client; use kitsune_type::webfinger::Resource; use kitsune_util::try_join; -use serde::{Deserialize, Serialize}; use std::{ptr, sync::Arc, time::Duration}; pub mod error; @@ -24,22 +24,10 @@ pub const MAX_JRD_REDIRECTS: u32 = 3; #[derive(Clone)] pub struct Webfinger { - cache: ArcCache, + cache: ArcCache, client: Client, } -#[allow(clippy::doc_markdown)] // "WebFinger" here isn't referring to the item name -/// Description of an ActivityPub actor resolved via WebFinger -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct ActorResource { - /// The `self` link (the actor's URI) - pub uri: String, - /// The username part of the canonical `acct:` URI - pub username: String, - /// The host component of the canonical `acct:` URI - pub domain: String, -} - impl Webfinger { #[must_use] pub fn with_defaults(redis_conn: deadpool_redis::Pool) -> Self { @@ -52,7 +40,7 @@ impl Webfinger { impl Webfinger { #[allow(clippy::missing_panics_doc)] // The invariants are covered. It won't panic. #[must_use] - pub fn new(cache: ArcCache) -> Self { + pub fn new(cache: ArcCache) -> Self { let client = Client::builder() .default_header("Accept", HeaderValue::from_static("application/jrd+json")) .unwrap() @@ -64,9 +52,13 @@ impl Webfinger { } #[must_use] - pub fn with_client(client: Client, cache: ArcCache) -> Self { + pub fn with_client(client: Client, cache: ArcCache) -> Self { Self { cache, client } } +} + +impl Resolver for Webfinger { + type Error = Error; /// Resolves the `acct:{username}@{domain}` URI via WebFinger to get the object ID and the /// canonical `acct:` URI of an ActivityPub actor @@ -76,11 +68,11 @@ impl Webfinger { /// which the caller should check by themselves before trusting the result. #[instrument(skip(self))] #[autometrics(track_concurrency)] - pub async fn resolve_actor( + async fn resolve_account( &self, username: &str, domain: &str, - ) -> Result> { + ) -> Result, Self::Error> { // XXX: Assigning the arguments to local bindings because the `#[instrument]` attribute // desugars to an `async move {}` block, inside which mutating the function arguments would // upset the borrowck @@ -143,7 +135,7 @@ impl Webfinger { return Ok(None); }; - let ret = ActorResource { + let ret = AccountResource { username: username.to_owned(), domain: domain.to_owned(), uri, diff --git a/crates/kitsune-webfinger/tests/basic.rs b/crates/kitsune-webfinger/tests/basic.rs index 8b3c95c94..7e3a6fcbe 100644 --- a/crates/kitsune-webfinger/tests/basic.rs +++ b/crates/kitsune-webfinger/tests/basic.rs @@ -1,5 +1,6 @@ use hyper::{Body, Request, Response}; use kitsune_cache::NoopCache; +use kitsune_core::traits::Resolver; use kitsune_http_client::Client; use kitsune_webfinger::Webfinger; use pretty_assertions::assert_eq; @@ -20,7 +21,7 @@ async fn basic() { let webfinger = Webfinger::with_client(client, Arc::new(NoopCache.into())); let resource = webfinger - .resolve_actor("0x0", "corteximplant.com") + .resolve_account("0x0", "corteximplant.com") .await .expect("Failed to fetch resource") .unwrap(); diff --git a/crates/kitsune-webfinger/tests/redirects.rs b/crates/kitsune-webfinger/tests/redirects.rs index c9062929b..ff3c5be55 100644 --- a/crates/kitsune-webfinger/tests/redirects.rs +++ b/crates/kitsune-webfinger/tests/redirects.rs @@ -1,5 +1,6 @@ use hyper::{Body, Request, Response, StatusCode}; use kitsune_cache::NoopCache; +use kitsune_core::traits::Resolver; use kitsune_http_client::Client; use kitsune_type::webfinger::Resource; use kitsune_webfinger::{Webfinger, MAX_JRD_REDIRECTS}; @@ -15,6 +16,7 @@ async fn follow_jrd_redirect() { ..simd_json::from_slice(&mut base).unwrap() }) .unwrap(); + let client = service_fn(move |req: Request<_>| { let body = body.clone(); async move { @@ -33,11 +35,12 @@ async fn follow_jrd_redirect() { } } }); + let client = Client::builder().service(client); let webfinger = Webfinger::with_client(client, Arc::new(NoopCache.into())); let resource = webfinger - .resolve_actor("0x0", "corteximplant.com") + .resolve_account("0x0", "corteximplant.com") .await .expect("Failed to fetch resource") .unwrap(); diff --git a/crates/kitsune-core/src/job/deliver/accept.rs b/kitsune-job-runner/src/job/deliver/accept.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/accept.rs rename to kitsune-job-runner/src/job/deliver/accept.rs diff --git a/crates/kitsune-core/src/job/deliver/create.rs b/kitsune-job-runner/src/job/deliver/create.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/create.rs rename to kitsune-job-runner/src/job/deliver/create.rs diff --git a/crates/kitsune-core/src/job/deliver/delete.rs b/kitsune-job-runner/src/job/deliver/delete.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/delete.rs rename to kitsune-job-runner/src/job/deliver/delete.rs diff --git a/crates/kitsune-core/src/job/deliver/favourite.rs b/kitsune-job-runner/src/job/deliver/favourite.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/favourite.rs rename to kitsune-job-runner/src/job/deliver/favourite.rs diff --git a/crates/kitsune-core/src/job/deliver/follow.rs b/kitsune-job-runner/src/job/deliver/follow.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/follow.rs rename to kitsune-job-runner/src/job/deliver/follow.rs diff --git a/crates/kitsune-core/src/job/deliver/mod.rs b/kitsune-job-runner/src/job/deliver/mod.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/mod.rs rename to kitsune-job-runner/src/job/deliver/mod.rs diff --git a/crates/kitsune-core/src/job/deliver/reject.rs b/kitsune-job-runner/src/job/deliver/reject.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/reject.rs rename to kitsune-job-runner/src/job/deliver/reject.rs diff --git a/crates/kitsune-core/src/job/deliver/unfavourite.rs b/kitsune-job-runner/src/job/deliver/unfavourite.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/unfavourite.rs rename to kitsune-job-runner/src/job/deliver/unfavourite.rs diff --git a/crates/kitsune-core/src/job/deliver/unfollow.rs b/kitsune-job-runner/src/job/deliver/unfollow.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/unfollow.rs rename to kitsune-job-runner/src/job/deliver/unfollow.rs diff --git a/crates/kitsune-core/src/job/deliver/update.rs b/kitsune-job-runner/src/job/deliver/update.rs similarity index 100% rename from crates/kitsune-core/src/job/deliver/update.rs rename to kitsune-job-runner/src/job/deliver/update.rs diff --git a/crates/kitsune-core/src/job/mailing/confirmation.rs b/kitsune-job-runner/src/job/mailing/confirmation.rs similarity index 100% rename from crates/kitsune-core/src/job/mailing/confirmation.rs rename to kitsune-job-runner/src/job/mailing/confirmation.rs diff --git a/crates/kitsune-core/src/job/mailing/mod.rs b/kitsune-job-runner/src/job/mailing/mod.rs similarity index 100% rename from crates/kitsune-core/src/job/mailing/mod.rs rename to kitsune-job-runner/src/job/mailing/mod.rs diff --git a/crates/kitsune-core/src/job/mod.rs b/kitsune-job-runner/src/job/mod.rs similarity index 100% rename from crates/kitsune-core/src/job/mod.rs rename to kitsune-job-runner/src/job/mod.rs