Aode (lion)
a1ad76b485
All checks were successful
continuous-integration/drone/push Build is passing
306 lines
8.3 KiB
Rust
306 lines
8.3 KiB
Rust
//! traits around accepting activities
|
|
|
|
use crate::{
|
|
repo::{Dereference, Repo},
|
|
session::Session,
|
|
};
|
|
use std::{rc::Rc, sync::Arc};
|
|
use url::{Host, Url};
|
|
|
|
/// The Authority that is providing the ingested data
|
|
///
|
|
/// - An Authority of None means the data is untrustworthy, as no entity can be verified to have
|
|
/// provided this data
|
|
/// - An Authority of Server means the data to be ingested has been provided on behalf of it's
|
|
/// origin server. A URL is provided in the Server variant to describe the specific URL the data
|
|
/// originates from.
|
|
/// - An Authority of Actor(Url) means the data to be ingested has been provided on behalf of the
|
|
/// Actor identified by the associated URL
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub enum Authority {
|
|
/// No Authority provided
|
|
None,
|
|
|
|
/// The Authority for ingested data is the server the data is hosted on
|
|
Server(Url),
|
|
|
|
/// The Authority for the ingested data is the provided url
|
|
Actor(Url),
|
|
}
|
|
|
|
/// Describes accepting a new Object into the system
|
|
///
|
|
/// This type is implemented by users of `apub` to hook into provied inbox methods
|
|
#[async_trait::async_trait(?Send)]
|
|
pub trait Ingest<Object> {
|
|
/// The actor that is receiving the activity
|
|
///
|
|
/// e.g.
|
|
/// - the community that is receiving a new post
|
|
/// - the server actor (in the case of the shared inbox)
|
|
type ActorId;
|
|
|
|
/// The local repository
|
|
type Local: Repo;
|
|
|
|
/// The error produced when ingesting activities
|
|
type Error: From<<Self::Local as Repo>::Error>;
|
|
|
|
/// Get the local repository
|
|
fn local_repo(&self) -> &Self::Local;
|
|
|
|
/// Determine if a given URL is local
|
|
fn is_local(&self, url: &Url) -> bool;
|
|
|
|
/// Accept and process a given activity
|
|
///
|
|
/// Args:
|
|
/// - authority: the source of the information, either the Actor that provided the object, or the URL it was fetched from
|
|
/// - actor_id: the ID of the actor accepting the object.
|
|
/// - activity: the Object being ingested
|
|
/// - remote_repo: a handle to a remote repository (probably an HTTP client) for ingesting further objects
|
|
/// - session: the request session associated with the Remote Repo
|
|
async fn ingest<Remote: Repo, S: Session>(
|
|
&self,
|
|
authority: Authority,
|
|
actor_id: Self::ActorId,
|
|
activity: &Object,
|
|
remote_repo: Remote,
|
|
session: S,
|
|
) -> Result<(), Self::Error>
|
|
where
|
|
Self::Error: From<Remote::Error>;
|
|
|
|
/// Dereference an ID from the local or remote repo
|
|
///
|
|
/// Args:
|
|
/// - id: the ID of the object to be fetched
|
|
/// - actor_id: the ID of the actor fetching the object.
|
|
/// - remote_repo: a handle to a remote repository (probably an HTTP client) for ingesting further objects
|
|
/// - session: the request session associated with the Remote Repo
|
|
async fn fetch<D: Dereference<Output = Object>, Remote: Repo, S: Session>(
|
|
&self,
|
|
id: D,
|
|
actor_id: Self::ActorId,
|
|
remote_repo: Remote,
|
|
mut session: S,
|
|
) -> Result<Option<D::Output>, Self::Error>
|
|
where
|
|
Self::ActorId: 'static,
|
|
Self::Error: From<Remote::Error>,
|
|
{
|
|
let opt = self.local_repo().fetch(&id, &mut session).await?;
|
|
|
|
if self.is_local(id.url()) {
|
|
return Ok(opt);
|
|
}
|
|
|
|
let opt = remote_repo.fetch(&id, &mut session).await?;
|
|
|
|
if let Some(object) = opt.as_ref() {
|
|
let authority = Authority::Server(id.url().clone());
|
|
self.ingest(authority, actor_id, object, remote_repo, session)
|
|
.await?;
|
|
}
|
|
|
|
Ok(opt)
|
|
}
|
|
}
|
|
|
|
/// Describes a type that can produce an Ingest
|
|
pub trait IngestFactory<A> {
|
|
/// The Ingest type
|
|
type Ingest: Ingest<A>;
|
|
|
|
/// Build the ingest type
|
|
fn build_ingest(&self) -> Self::Ingest;
|
|
}
|
|
|
|
impl std::fmt::Display for Authority {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Authority::Actor(actor_id) => write!(f, "Actor - '{}'", actor_id),
|
|
Authority::Server(url) => write!(f, "Server URL - '{}'", url),
|
|
Authority::None => write!(f, "None"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A helper for determining if a given URL matches a Host and Port
|
|
pub fn is_local(local_host: &Host<String>, local_port: Option<u16>, url: &Url) -> bool {
|
|
Some(borrow_host(local_host)) == url.host() && local_port == url.port()
|
|
}
|
|
|
|
fn borrow_host(host: &Host<String>) -> Host<&str> {
|
|
match host {
|
|
Host::Ipv4(ip) => Host::Ipv4(*ip),
|
|
Host::Ipv6(ip) => Host::Ipv6(*ip),
|
|
Host::Domain(ref domain) => Host::Domain(domain),
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait(?Send)]
|
|
impl<'a, Object, T> Ingest<Object> for &'a T
|
|
where
|
|
T: Ingest<Object>,
|
|
Object: 'static,
|
|
{
|
|
type Local = T::Local;
|
|
type ActorId = T::ActorId;
|
|
type Error = T::Error;
|
|
|
|
fn local_repo(&self) -> &Self::Local {
|
|
T::local_repo(self)
|
|
}
|
|
|
|
fn is_local(&self, url: &Url) -> bool {
|
|
T::is_local(self, url)
|
|
}
|
|
|
|
async fn ingest<Remote: Repo, S: Session>(
|
|
&self,
|
|
authority: Authority,
|
|
actor_id: Self::ActorId,
|
|
activity: &Object,
|
|
remote_repo: Remote,
|
|
session: S,
|
|
) -> Result<(), Self::Error>
|
|
where
|
|
Self::Error: From<Remote::Error>,
|
|
{
|
|
T::ingest(self, authority, actor_id, activity, remote_repo, session).await
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait(?Send)]
|
|
impl<'a, Object, T> Ingest<Object> for &'a mut T
|
|
where
|
|
T: Ingest<Object>,
|
|
Object: 'static,
|
|
{
|
|
type Local = T::Local;
|
|
type Error = T::Error;
|
|
type ActorId = T::ActorId;
|
|
|
|
fn local_repo(&self) -> &Self::Local {
|
|
T::local_repo(self)
|
|
}
|
|
|
|
fn is_local(&self, url: &Url) -> bool {
|
|
T::is_local(self, url)
|
|
}
|
|
|
|
async fn ingest<Remote: Repo, S: Session>(
|
|
&self,
|
|
authority: Authority,
|
|
actor_id: Self::ActorId,
|
|
activity: &Object,
|
|
remote_repo: Remote,
|
|
session: S,
|
|
) -> Result<(), Self::Error>
|
|
where
|
|
Self::Error: From<Remote::Error>,
|
|
{
|
|
T::ingest(self, authority, actor_id, activity, remote_repo, session).await
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait(?Send)]
|
|
impl<Object, T> Ingest<Object> for Box<T>
|
|
where
|
|
T: Ingest<Object>,
|
|
Object: 'static,
|
|
{
|
|
type Local = T::Local;
|
|
type Error = T::Error;
|
|
type ActorId = T::ActorId;
|
|
|
|
fn local_repo(&self) -> &Self::Local {
|
|
T::local_repo(self)
|
|
}
|
|
|
|
fn is_local(&self, url: &Url) -> bool {
|
|
T::is_local(self, url)
|
|
}
|
|
|
|
async fn ingest<Remote: Repo, S: Session>(
|
|
&self,
|
|
authority: Authority,
|
|
actor_id: Self::ActorId,
|
|
activity: &Object,
|
|
remote_repo: Remote,
|
|
session: S,
|
|
) -> Result<(), Self::Error>
|
|
where
|
|
Self::Error: From<Remote::Error>,
|
|
{
|
|
T::ingest(self, authority, actor_id, activity, remote_repo, session).await
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait(?Send)]
|
|
impl<Object, T> Ingest<Object> for Rc<T>
|
|
where
|
|
T: Ingest<Object>,
|
|
Object: 'static,
|
|
{
|
|
type Local = T::Local;
|
|
type Error = T::Error;
|
|
type ActorId = T::ActorId;
|
|
|
|
fn local_repo(&self) -> &Self::Local {
|
|
T::local_repo(self)
|
|
}
|
|
|
|
fn is_local(&self, url: &Url) -> bool {
|
|
T::is_local(self, url)
|
|
}
|
|
|
|
async fn ingest<Remote: Repo, S: Session>(
|
|
&self,
|
|
authority: Authority,
|
|
actor_id: Self::ActorId,
|
|
activity: &Object,
|
|
remote_repo: Remote,
|
|
session: S,
|
|
) -> Result<(), Self::Error>
|
|
where
|
|
Self::Error: From<Remote::Error>,
|
|
{
|
|
T::ingest(self, authority, actor_id, activity, remote_repo, session).await
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait(?Send)]
|
|
impl<Object, T> Ingest<Object> for Arc<T>
|
|
where
|
|
T: Ingest<Object>,
|
|
Object: 'static,
|
|
{
|
|
type Local = T::Local;
|
|
type Error = T::Error;
|
|
type ActorId = T::ActorId;
|
|
|
|
fn local_repo(&self) -> &Self::Local {
|
|
T::local_repo(self)
|
|
}
|
|
|
|
fn is_local(&self, url: &Url) -> bool {
|
|
T::is_local(self, url)
|
|
}
|
|
|
|
async fn ingest<Remote: Repo, S: Session>(
|
|
&self,
|
|
authority: Authority,
|
|
actor_id: Self::ActorId,
|
|
activity: &Object,
|
|
remote_repo: Remote,
|
|
session: S,
|
|
) -> Result<(), Self::Error>
|
|
where
|
|
Self::Error: From<Remote::Error>,
|
|
{
|
|
T::ingest(self, authority, actor_id, activity, remote_repo, session).await
|
|
}
|
|
}
|