mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Create rustdoc for activitypub code
This commit is contained in:
parent
e190ecbefb
commit
695272f980
@ -19,6 +19,7 @@ use anyhow::Context;
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
||||
/// Takes an announce and passes the inner activity to the appropriate handler.
|
||||
pub async fn receive_announce(
|
||||
context: &LemmyContext,
|
||||
activity: AnyBase,
|
||||
|
@ -31,6 +31,7 @@ mod undo_comment;
|
||||
mod undo_post;
|
||||
pub mod update;
|
||||
|
||||
/// Return HTTP 501 for unsupported activities in inbox.
|
||||
fn receive_unhandled_activity<A>(activity: A) -> Result<HttpResponse, LemmyError>
|
||||
where
|
||||
A: Debug,
|
||||
@ -39,6 +40,8 @@ where
|
||||
Ok(HttpResponse::NotImplemented().finish())
|
||||
}
|
||||
|
||||
/// Reads the destination community from the activity's `cc` field. If this refers to a local
|
||||
/// community, the activity is announced to all community followers.
|
||||
async fn announce_if_community_is_local<T, Kind>(
|
||||
activity: T,
|
||||
user: &User_,
|
||||
@ -52,16 +55,12 @@ where
|
||||
{
|
||||
let cc = activity.cc().context(location_info!())?;
|
||||
let cc = cc.as_many().context(location_info!())?;
|
||||
let community_followers_uri = cc
|
||||
let community_uri = cc
|
||||
.first()
|
||||
.context(location_info!())?
|
||||
.as_xsd_any_uri()
|
||||
.context(location_info!())?;
|
||||
// TODO: this is hacky but seems to be the only way to get the community ID
|
||||
let community_uri = community_followers_uri
|
||||
.to_string()
|
||||
.replace("/followers", "");
|
||||
let community = get_or_fetch_and_upsert_community(&Url::parse(&community_uri)?, context).await?;
|
||||
let community = get_or_fetch_and_upsert_community(&community_uri, context).await?;
|
||||
|
||||
if community.local {
|
||||
community
|
||||
@ -71,6 +70,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads the actor field of an activity and returns the corresponding `User_`.
|
||||
pub(crate) async fn get_actor_as_user<T, A>(
|
||||
activity: &T,
|
||||
context: &LemmyContext,
|
||||
@ -89,6 +89,9 @@ pub(crate) enum FindResults {
|
||||
Post(Post),
|
||||
}
|
||||
|
||||
/// Tries to find a community, post or comment in the local database, without any network requests.
|
||||
/// This is used to handle deletions and removals, because in case we dont have the object, we can
|
||||
/// simply ignore the activity.
|
||||
pub(crate) async fn find_by_id(
|
||||
context: &LemmyContext,
|
||||
apub_id: Url,
|
||||
@ -123,6 +126,11 @@ pub(crate) async fn find_by_id(
|
||||
return Err(NotFound.into());
|
||||
}
|
||||
|
||||
/// Ensure that the ID of an incoming activity comes from the same domain as the actor. Optionally
|
||||
/// also checks the ID of the inner object.
|
||||
///
|
||||
/// The reason that this starts with the actor ID is that it was already confirmed as correct by the
|
||||
/// HTTP signature.
|
||||
pub(crate) fn verify_activity_domains_valid<T, Kind>(
|
||||
activity: &T,
|
||||
actor_id: Url,
|
||||
|
@ -41,7 +41,8 @@ use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ApubObjectType for Comment {
|
||||
/// Send out information about a newly created comment, to the followers of the community.
|
||||
/// Send out information about a newly created comment, to the followers of the community and
|
||||
/// mentioned users.
|
||||
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
let note = self.to_apub(context.pool()).await?;
|
||||
|
||||
@ -68,12 +69,13 @@ impl ApubObjectType for Comment {
|
||||
// Set the mention tags
|
||||
.set_many_tags(maa.get_tags()?);
|
||||
|
||||
send_to_community(&creator, &community, create.clone(), context).await?;
|
||||
send_to_community(create.clone(), &creator, &community, context).await?;
|
||||
send_comment_mentions(&creator, maa.inboxes, create, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send out information about an edited post, to the followers of the community.
|
||||
/// Send out information about an edited post, to the followers of the community and mentioned
|
||||
/// users.
|
||||
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
let note = self.to_apub(context.pool()).await?;
|
||||
|
||||
@ -100,7 +102,7 @@ impl ApubObjectType for Comment {
|
||||
// Set the mention tags
|
||||
.set_many_tags(maa.get_tags()?);
|
||||
|
||||
send_to_community(&creator, &community, update.clone(), context).await?;
|
||||
send_to_community(update.clone(), &creator, &community, context).await?;
|
||||
send_comment_mentions(&creator, maa.inboxes, update, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -122,7 +124,7 @@ impl ApubObjectType for Comment {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, delete, context).await?;
|
||||
send_to_community(delete, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -156,7 +158,7 @@ impl ApubObjectType for Comment {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, undo, context).await?;
|
||||
send_to_community(undo, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -177,7 +179,7 @@ impl ApubObjectType for Comment {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&mod_, &community, remove, context).await?;
|
||||
send_to_community(remove, &mod_, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -207,7 +209,7 @@ impl ApubObjectType for Comment {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&mod_, &community, undo, context).await?;
|
||||
send_to_community(undo, &mod_, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -233,7 +235,7 @@ impl ApubLikeableType for Comment {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, like, context).await?;
|
||||
send_to_community(like, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -256,7 +258,7 @@ impl ApubLikeableType for Comment {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, dislike, context).await?;
|
||||
send_to_community(dislike, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -291,7 +293,7 @@ impl ApubLikeableType for Comment {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, undo, context).await?;
|
||||
send_to_community(undo, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ impl ActorType for Community {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If the creator of a community deletes the community, send this to all followers.
|
||||
async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
let group = self.to_apub(context.pool()).await?;
|
||||
|
||||
@ -98,6 +99,7 @@ impl ActorType for Community {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If the creator of a community reverts the deletion of a community, send this to all followers.
|
||||
async fn send_undo_delete(
|
||||
&self,
|
||||
creator: &User_,
|
||||
@ -123,6 +125,7 @@ impl ActorType for Community {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If an admin removes a community, send this to all followers.
|
||||
async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
let mut remove = Remove::new(mod_.actor_id.to_owned(), self.actor_id()?);
|
||||
remove
|
||||
@ -135,6 +138,7 @@ impl ActorType for Community {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If an admin reverts the removal of a community, send this to all followers.
|
||||
async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
let mut remove = Remove::new(mod_.actor_id.to_owned(), self.actor_id()?);
|
||||
remove
|
||||
@ -155,6 +159,8 @@ impl ActorType for Community {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Wraps an activity sent to the community in an announce, and then sends the announce to all
|
||||
/// community followers.
|
||||
async fn send_announce(
|
||||
&self,
|
||||
activity: AnyBase,
|
||||
|
@ -8,6 +8,8 @@ pub mod post;
|
||||
pub mod private_message;
|
||||
pub mod user;
|
||||
|
||||
/// Generate a unique ID for an activity, in the format:
|
||||
/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
|
||||
fn generate_activity_id<T>(kind: T) -> Result<Url, ParseError>
|
||||
where
|
||||
T: ToString,
|
||||
|
@ -45,7 +45,7 @@ impl ApubObjectType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(creator, &community, create, context).await?;
|
||||
send_to_community(create, creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ impl ApubObjectType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(creator, &community, update, context).await?;
|
||||
send_to_community(update, creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ impl ApubObjectType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(creator, &community, delete, context).await?;
|
||||
send_to_community(delete, creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ impl ApubObjectType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(creator, &community, undo, context).await?;
|
||||
send_to_community(undo, creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ impl ApubObjectType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(mod_, &community, remove, context).await?;
|
||||
send_to_community(remove, mod_, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ impl ApubObjectType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(mod_, &community, undo, context).await?;
|
||||
send_to_community(undo, mod_, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -181,7 +181,7 @@ impl ApubLikeableType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, like, context).await?;
|
||||
send_to_community(like, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -201,7 +201,7 @@ impl ApubLikeableType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, dislike, context).await?;
|
||||
send_to_community(dislike, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -233,7 +233,7 @@ impl ApubLikeableType for Post {
|
||||
.set_to(public())
|
||||
.set_many_ccs(vec![community.actor_id()?]);
|
||||
|
||||
send_to_community(&creator, &community, undo, context).await?;
|
||||
send_to_community(undo, &creator, &community, context).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ impl ApubObjectType for PrivateMessage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send out information about an edited post, to the followers of the community.
|
||||
/// Send out information about an edited private message, to the followers of the community.
|
||||
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
let note = self.to_apub(context.pool()).await?;
|
||||
|
||||
|
@ -28,6 +28,11 @@ use serde::{export::fmt::Debug, Deserialize, Serialize};
|
||||
use std::{collections::BTreeMap, future::Future, pin::Pin};
|
||||
use url::Url;
|
||||
|
||||
/// Sends a local activity to a single, remote actor.
|
||||
///
|
||||
/// * `activity` the apub activity to be sent
|
||||
/// * `creator` the local actor which created the activity
|
||||
/// * `inbox` the inbox url where the activity should be delivered to
|
||||
pub async fn send_activity_single_dest<T, Kind>(
|
||||
activity: T,
|
||||
creator: &dyn ActorType,
|
||||
@ -59,6 +64,12 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// From a local community, send activity to all remote followers.
|
||||
///
|
||||
/// * `activity` the apub activity to send
|
||||
/// * `community` the sending community
|
||||
/// * `sender_shared_inbox` in case of an announce, this should be the shared inbox of the inner
|
||||
/// activities creator, as receiving a known activity will cause an error
|
||||
pub async fn send_to_community_followers<T, Kind>(
|
||||
activity: T,
|
||||
community: &Community,
|
||||
@ -102,10 +113,16 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends an activity from a local user to a remote community.
|
||||
///
|
||||
/// * `activity` the activity to send
|
||||
/// * `creator` the creator of the activity
|
||||
/// * `community` the destination community
|
||||
///
|
||||
pub async fn send_to_community<T, Kind>(
|
||||
activity: T,
|
||||
creator: &User_,
|
||||
community: &Community,
|
||||
activity: T,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError>
|
||||
where
|
||||
@ -140,6 +157,11 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends notification to any users mentioned in a comment
|
||||
///
|
||||
/// * `creator` user who created the comment
|
||||
/// * `mentions` list of inboxes of users which are mentioned in the comment
|
||||
/// * `activity` either a `Create/Note` or `Update/Note`
|
||||
pub async fn send_comment_mentions<T, Kind>(
|
||||
creator: &User_,
|
||||
mentions: Vec<Url>,
|
||||
@ -173,7 +195,8 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Asynchronously sends the given `activity` from `actor` to every inbox URL in `to`.
|
||||
/// Create new `SendActivityTasks`, which will deliver the given activity to inboxes, as well as
|
||||
/// handling signing and retrying failed deliveres.
|
||||
///
|
||||
/// The caller of this function needs to remove any blocked domains from `to`,
|
||||
/// using `check_is_apub_id_valid()`.
|
||||
@ -224,6 +247,8 @@ struct SendActivityTask {
|
||||
private_key: String,
|
||||
}
|
||||
|
||||
/// Signs the activity with the sending actor's key, and delivers to the given inbox. Also retries
|
||||
/// if the delivery failed.
|
||||
impl ActixJob for SendActivityTask {
|
||||
type State = MyState;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
|
||||
|
@ -5,6 +5,8 @@ use lemmy_db::{category::Category, Crud};
|
||||
use lemmy_utils::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Activitystreams extension to allow (de)serializing additional Community fields `category` and
|
||||
/// `sensitive` (called 'nsfw' in Lemmy).
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GroupExtension {
|
||||
|
@ -2,6 +2,9 @@ use activitystreams::unparsed::UnparsedMutExt;
|
||||
use activitystreams_ext::UnparsedExtension;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Activitystreams extension to allow (de)serializing additional Post fields
|
||||
/// `comemnts_enabled` (called 'locked' in Lemmy),
|
||||
/// `sensitive` (called 'nsfw') and `stickied`.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PageExtension {
|
||||
|
@ -24,11 +24,12 @@ lazy_static! {
|
||||
static ref HTTP_SIG_CONFIG: Config = Config::new();
|
||||
}
|
||||
|
||||
/// Signs request headers with the given keypair.
|
||||
/// Creates an HTTP post request to `inbox_url`, with the given `client` and `headers`, and
|
||||
/// `activity` as request body. The request is signed with `private_key` and then sent.
|
||||
pub async fn sign_and_send(
|
||||
client: &Client,
|
||||
headers: BTreeMap<String, String>,
|
||||
url: &Url,
|
||||
inbox_url: &Url,
|
||||
activity: String,
|
||||
actor_id: &Url,
|
||||
private_key: String,
|
||||
@ -43,7 +44,7 @@ pub async fn sign_and_send(
|
||||
);
|
||||
}
|
||||
let response = client
|
||||
.post(&url.to_string())
|
||||
.post(&inbox_url.to_string())
|
||||
.headers(header_map)
|
||||
.signature_with_digest(
|
||||
HTTP_SIG_CONFIG.clone(),
|
||||
@ -63,6 +64,7 @@ pub async fn sign_and_send(
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// Verifies the HTTP signature on an incoming inbox request.
|
||||
pub fn verify_signature(request: &HttpRequest, actor: &dyn ActorType) -> Result<(), LemmyError> {
|
||||
let public_key = actor.public_key().context(location_info!())?;
|
||||
let verified = CONFIG2
|
||||
@ -90,8 +92,14 @@ pub fn verify_signature(request: &HttpRequest, actor: &dyn ActorType) -> Result<
|
||||
}
|
||||
}
|
||||
|
||||
// The following is taken from here:
|
||||
// https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
|
||||
/// Extension for actor public key, which is needed on user and community for HTTP signatures.
|
||||
///
|
||||
/// Taken from: https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublicKeyExtension {
|
||||
pub public_key: PublicKey,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -101,12 +109,6 @@ pub struct PublicKey {
|
||||
pub public_key_pem: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublicKeyExtension {
|
||||
pub public_key: PublicKey,
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
pub fn to_ext(&self) -> PublicKeyExtension {
|
||||
PublicKeyExtension {
|
||||
|
@ -82,11 +82,11 @@ pub enum SearchAcceptedObjects {
|
||||
|
||||
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
||||
///
|
||||
/// Some working examples for use with the docker/federation/ setup:
|
||||
/// http://lemmy_alpha:8540/c/main, or !main@lemmy_alpha:8540
|
||||
/// http://lemmy_alpha:8540/u/lemmy_alpha, or @lemmy_alpha@lemmy_alpha:8540
|
||||
/// http://lemmy_alpha:8540/post/3
|
||||
/// http://lemmy_alpha:8540/comment/2
|
||||
/// Some working examples for use with the `docker/federation/` setup:
|
||||
/// http://lemmy_alpha:8541/c/main, or !main@lemmy_alpha:8541
|
||||
/// http://lemmy_beta:8551/u/lemmy_alpha, or @lemmy_beta@lemmy_beta:8551
|
||||
/// http://lemmy_gamma:8561/post/3
|
||||
/// http://lemmy_delta:8571/comment/2
|
||||
pub async fn search_by_apub_id(
|
||||
query: &str,
|
||||
context: &LemmyContext,
|
||||
@ -191,19 +191,27 @@ pub async fn search_by_apub_id(
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// Get a remote actor from its apub ID (either a user or a community). Thin wrapper around
|
||||
/// `get_or_fetch_and_upsert_user()` and `get_or_fetch_and_upsert_community()`.
|
||||
///
|
||||
/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
|
||||
/// Otherwise it is fetched from the remote instance, stored and returned.
|
||||
pub(crate) async fn get_or_fetch_and_upsert_actor(
|
||||
apub_id: &Url,
|
||||
context: &LemmyContext,
|
||||
) -> Result<Box<dyn ActorType>, LemmyError> {
|
||||
let user = get_or_fetch_and_upsert_user(apub_id, context).await;
|
||||
let actor: Box<dyn ActorType> = match user {
|
||||
Ok(u) => Box::new(u),
|
||||
Err(_) => Box::new(get_or_fetch_and_upsert_community(apub_id, context).await?),
|
||||
let community = get_or_fetch_and_upsert_community(apub_id, context).await;
|
||||
let actor: Box<dyn ActorType> = match community {
|
||||
Ok(c) => Box::new(c),
|
||||
Err(_) => Box::new(get_or_fetch_and_upsert_user(apub_id, context).await?),
|
||||
};
|
||||
Ok(actor)
|
||||
}
|
||||
|
||||
/// Check if a remote user exists, create if not found, if its too old update it.Fetch a user, insert/update it in the database and return the user.
|
||||
/// Get a user from its apub ID.
|
||||
///
|
||||
/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
|
||||
/// Otherwise it is fetched from the remote instance, stored and returned.
|
||||
pub(crate) async fn get_or_fetch_and_upsert_user(
|
||||
apub_id: &Url,
|
||||
context: &LemmyContext,
|
||||
@ -241,7 +249,8 @@ pub(crate) async fn get_or_fetch_and_upsert_user(
|
||||
}
|
||||
|
||||
/// Determines when a remote actor should be refetched from its instance. In release builds, this is
|
||||
/// ACTOR_REFETCH_INTERVAL_SECONDS after the last refetch, in debug builds always.
|
||||
/// `ACTOR_REFETCH_INTERVAL_SECONDS` after the last refetch, in debug builds
|
||||
/// `ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG`.
|
||||
///
|
||||
/// TODO it won't pick up new avatars, summaries etc until a day after.
|
||||
/// Actors need an "update" activity pushed to other servers to fix this.
|
||||
@ -255,7 +264,10 @@ fn should_refetch_actor(last_refreshed: NaiveDateTime) -> bool {
|
||||
last_refreshed.lt(&(naive_now() - update_interval))
|
||||
}
|
||||
|
||||
/// Check if a remote community exists, create if not found, if its too old update it.Fetch a community, insert/update it in the database and return the community.
|
||||
/// Get a community from its apub ID.
|
||||
///
|
||||
/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
|
||||
/// Otherwise it is fetched from the remote instance, stored and returned.
|
||||
pub(crate) async fn get_or_fetch_and_upsert_community(
|
||||
apub_id: &Url,
|
||||
context: &LemmyContext,
|
||||
@ -280,6 +292,9 @@ pub(crate) async fn get_or_fetch_and_upsert_community(
|
||||
}
|
||||
}
|
||||
|
||||
/// Request a community by apub ID from a remote instance, including moderators. If `community_id`,
|
||||
/// is set, this is an update for a community which is already known locally. If not, we don't know
|
||||
/// the community yet and also pull the outbox, to get some initial posts.
|
||||
async fn fetch_remote_community(
|
||||
apub_id: &Url,
|
||||
context: &LemmyContext,
|
||||
@ -358,6 +373,10 @@ async fn fetch_remote_community(
|
||||
Ok(community)
|
||||
}
|
||||
|
||||
/// Gets a post by its apub ID. If it exists locally, it is returned directly. Otherwise it is
|
||||
/// pulled from its apub ID, inserted and returned.
|
||||
///
|
||||
/// The parent community is also pulled if necessary. Comments are not pulled.
|
||||
pub(crate) async fn get_or_fetch_and_insert_post(
|
||||
post_ap_id: &Url,
|
||||
context: &LemmyContext,
|
||||
@ -383,6 +402,10 @@ pub(crate) async fn get_or_fetch_and_insert_post(
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a comment by its apub ID. If it exists locally, it is returned directly. Otherwise it is
|
||||
/// pulled from its apub ID, inserted and returned.
|
||||
///
|
||||
/// The parent community, post and comment are also pulled if necessary.
|
||||
pub(crate) async fn get_or_fetch_and_insert_comment(
|
||||
comment_ap_id: &Url,
|
||||
context: &LemmyContext,
|
||||
|
@ -15,7 +15,7 @@ pub struct CommentQuery {
|
||||
comment_id: String,
|
||||
}
|
||||
|
||||
/// Return the post json over HTTP.
|
||||
/// Return the ActivityPub json representation of a local comment over HTTP.
|
||||
pub async fn get_apub_comment(
|
||||
info: Path<CommentQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
|
@ -19,7 +19,7 @@ pub struct CommunityQuery {
|
||||
community_name: String,
|
||||
}
|
||||
|
||||
/// Return the community json over HTTP.
|
||||
/// Return the ActivityPub json representation of a local community over HTTP.
|
||||
pub async fn get_apub_community_http(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
@ -62,6 +62,8 @@ pub async fn get_apub_community_followers(
|
||||
Ok(create_apub_response(&collection))
|
||||
}
|
||||
|
||||
/// Returns the community outbox, which is populated by a maximum of 20 posts (but no other
|
||||
/// activites like votes or comments).
|
||||
pub async fn get_apub_community_outbox(
|
||||
info: web::Path<CommunityQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
|
@ -15,7 +15,7 @@ pub struct PostQuery {
|
||||
post_id: String,
|
||||
}
|
||||
|
||||
/// Return the post json over HTTP.
|
||||
/// Return the ActivityPub json representation of a local post over HTTP.
|
||||
pub async fn get_apub_post(
|
||||
info: web::Path<PostQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
|
@ -11,7 +11,7 @@ pub struct UserQuery {
|
||||
user_name: String,
|
||||
}
|
||||
|
||||
/// Return the user json over HTTP.
|
||||
/// Return the ActivityPub json representation of a local user over HTTP.
|
||||
pub async fn get_apub_user_http(
|
||||
info: web::Path<UserQuery>,
|
||||
context: web::Data<LemmyContext>,
|
||||
|
@ -25,6 +25,7 @@ use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Allowed activities for community inbox.
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub enum ValidTypes {
|
||||
@ -90,7 +91,7 @@ pub async fn community_inbox(
|
||||
res
|
||||
}
|
||||
|
||||
/// Handle a follow request from a remote user, adding it to the local database and returning an
|
||||
/// Handle a follow request from a remote user, adding the user as follower and returning an
|
||||
/// Accept activity.
|
||||
async fn handle_follow(
|
||||
activity: AnyBase,
|
||||
@ -117,6 +118,7 @@ async fn handle_follow(
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
|
||||
/// Handle `Undo/Follow` from a user, removing the user from followers list.
|
||||
async fn handle_undo_follow(
|
||||
activity: AnyBase,
|
||||
user: User_,
|
||||
|
@ -23,6 +23,7 @@ use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Allowed activity types for shared inbox.
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub enum ValidTypes {
|
||||
@ -40,7 +41,7 @@ pub enum ValidTypes {
|
||||
// but it might still work due to the anybase conversion
|
||||
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
|
||||
|
||||
/// Handler for all incoming receive to user inboxes.
|
||||
/// Handler for all incoming requests to shared inbox.
|
||||
pub async fn shared_inbox(
|
||||
request: HttpRequest,
|
||||
input: web::Json<AcceptedActivities>,
|
||||
|
@ -30,6 +30,7 @@ use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Allowed activities for user inbox.
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub enum ValidTypes {
|
||||
@ -42,7 +43,7 @@ pub enum ValidTypes {
|
||||
|
||||
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
|
||||
|
||||
/// Handler for all incoming receive to user inboxes.
|
||||
/// Handler for all incoming activities to user inboxes.
|
||||
pub async fn user_inbox(
|
||||
request: HttpRequest,
|
||||
input: web::Json<AcceptedActivities>,
|
||||
|
@ -29,13 +29,24 @@ use lemmy_websocket::LemmyContext;
|
||||
use serde::Serialize;
|
||||
use url::{ParseError, Url};
|
||||
|
||||
/// Activitystreams type for community
|
||||
type GroupExt = Ext2<ApActor<Group>, GroupExtension, PublicKeyExtension>;
|
||||
/// Activitystreams type for user
|
||||
type PersonExt = Ext1<ApActor<Person>, PublicKeyExtension>;
|
||||
/// Activitystreams type for post
|
||||
type PageExt = Ext1<Page, PageExtension>;
|
||||
|
||||
pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
|
||||
|
||||
// Checks if the ID has a valid format, correct scheme, and is in the allowed instance list.
|
||||
/// Checks if the ID is allowed for sending or receiving.
|
||||
///
|
||||
/// In particular, it checks for:
|
||||
/// - federation being enabled (if its disabled, only local URLs are allowed)
|
||||
/// - the correct scheme (either http or https)
|
||||
/// - URL being in the allowlist (if it is active)
|
||||
/// - URL not being in the blocklist (if it is active)
|
||||
///
|
||||
/// Note that only one of allowlist and blacklist can be enabled, not both.
|
||||
fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
|
||||
let settings = Settings::get();
|
||||
let domain = apub_id.domain().context(location_info!())?.to_string();
|
||||
@ -90,10 +101,11 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for converting an object or actor into the respective ActivityPub type.
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait ToApub {
|
||||
type Response;
|
||||
async fn to_apub(&self, pool: &DbPool) -> Result<Self::Response, LemmyError>;
|
||||
type ApubType;
|
||||
async fn to_apub(&self, pool: &DbPool) -> Result<Self::ApubType, LemmyError>;
|
||||
fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
|
||||
}
|
||||
|
||||
@ -104,9 +116,8 @@ pub trait FromApub {
|
||||
///
|
||||
/// * `apub` The object to read from
|
||||
/// * `context` LemmyContext which holds DB pool, HTTP client etc
|
||||
/// * `expected_domain` If present, ensure that the apub object comes from the same domain as
|
||||
/// this URL
|
||||
///
|
||||
/// * `expected_domain` If present, ensure that the domains of this and of the apub object ID are
|
||||
/// identical
|
||||
async fn from_apub(
|
||||
apub: &Self::ApubType,
|
||||
context: &LemmyContext,
|
||||
@ -116,6 +127,8 @@ pub trait FromApub {
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
/// Common functions for ActivityPub objects, which are implemented by most (but not all) objects
|
||||
/// and actors in Lemmy.
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait ApubObjectType {
|
||||
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||
@ -138,6 +151,8 @@ pub trait ApubLikeableType {
|
||||
-> Result<(), LemmyError>;
|
||||
}
|
||||
|
||||
/// Common methods provided by ActivityPub actors (community and user). Not all methods are
|
||||
/// implemented by all actors.
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait ActorType {
|
||||
fn actor_id_str(&self) -> String;
|
||||
@ -149,9 +164,6 @@ pub trait ActorType {
|
||||
/// numeric id in the database, used for insert_activity
|
||||
fn user_id(&self) -> i32;
|
||||
|
||||
// These two have default impls, since currently a community can't follow anything,
|
||||
// and a user can't be followed (yet)
|
||||
#[allow(unused_variables)]
|
||||
async fn send_follow(
|
||||
&self,
|
||||
follow_actor_id: &Url,
|
||||
@ -163,7 +175,6 @@ pub trait ActorType {
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError>;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
async fn send_accept_follow(
|
||||
&self,
|
||||
follow: Follow,
|
||||
@ -234,6 +245,8 @@ pub trait ActorType {
|
||||
}
|
||||
}
|
||||
|
||||
/// Store a sent or received activity in the database, for logging purposes. These records are not
|
||||
/// persistent.
|
||||
pub async fn insert_activity<T>(
|
||||
user_id: i32,
|
||||
data: T,
|
||||
|
@ -32,7 +32,7 @@ use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ToApub for Comment {
|
||||
type Response = Note;
|
||||
type ApubType = Note;
|
||||
|
||||
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
|
||||
let mut comment = Note::new();
|
||||
@ -82,7 +82,9 @@ impl ToApub for Comment {
|
||||
impl FromApub for CommentForm {
|
||||
type ApubType = Note;
|
||||
|
||||
/// Parse an ActivityPub note received from another instance into a Lemmy comment
|
||||
/// Converts a `Note` to `CommentForm`.
|
||||
///
|
||||
/// If the parent community, post and comment(s) are not known locally, these are also fetched.
|
||||
async fn from_apub(
|
||||
note: &Note,
|
||||
context: &LemmyContext,
|
||||
|
@ -32,9 +32,8 @@ use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ToApub for Community {
|
||||
type Response = GroupExt;
|
||||
type ApubType = GroupExt;
|
||||
|
||||
// Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
|
||||
async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
|
||||
// The attributed to, is an ordered vector with the creator actor_ids first,
|
||||
// then the rest of the moderators
|
||||
@ -108,7 +107,6 @@ impl ToApub for Community {
|
||||
impl FromApub for CommunityForm {
|
||||
type ApubType = GroupExt;
|
||||
|
||||
/// Parse an ActivityPub group received from another instance into a Lemmy community.
|
||||
async fn from_apub(
|
||||
group: &GroupExt,
|
||||
context: &LemmyContext,
|
||||
|
@ -31,7 +31,7 @@ use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ToApub for Post {
|
||||
type Response = PageExt;
|
||||
type ApubType = PageExt;
|
||||
|
||||
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
|
||||
async fn to_apub(&self, pool: &DbPool) -> Result<PageExt, LemmyError> {
|
||||
@ -94,7 +94,9 @@ impl ToApub for Post {
|
||||
impl FromApub for PostForm {
|
||||
type ApubType = PageExt;
|
||||
|
||||
/// Parse an ActivityPub page received from another instance into a Lemmy post.
|
||||
/// Converts a `PageExt` to `PostForm`.
|
||||
///
|
||||
/// If the post's community or creator are not known locally, these are also fetched.
|
||||
async fn from_apub(
|
||||
page: &PageExt,
|
||||
context: &LemmyContext,
|
||||
|
@ -23,7 +23,7 @@ use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ToApub for PrivateMessage {
|
||||
type Response = Note;
|
||||
type ApubType = Note;
|
||||
|
||||
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
|
||||
let mut private_message = Note::new();
|
||||
@ -58,7 +58,6 @@ impl ToApub for PrivateMessage {
|
||||
impl FromApub for PrivateMessageForm {
|
||||
type ApubType = Note;
|
||||
|
||||
/// Parse an ActivityPub note received from another instance into a Lemmy Private message
|
||||
async fn from_apub(
|
||||
note: &Note,
|
||||
context: &LemmyContext,
|
||||
|
@ -21,11 +21,9 @@ use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ToApub for User_ {
|
||||
type Response = PersonExt;
|
||||
type ApubType = PersonExt;
|
||||
|
||||
// Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
|
||||
async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
|
||||
// TODO go through all these to_string and to_owned()
|
||||
let mut person = Person::new();
|
||||
person
|
||||
.set_context(activitystreams::context())
|
||||
@ -73,7 +71,7 @@ impl ToApub for User_ {
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl FromApub for UserForm {
|
||||
type ApubType = PersonExt;
|
||||
/// Parse an ActivityPub person received from another instance into a Lemmy user.
|
||||
|
||||
async fn from_apub(
|
||||
person: &PersonExt,
|
||||
_context: &LemmyContext,
|
||||
|
Loading…
Reference in New Issue
Block a user