[[package]]
name = "activitypub_federation"
-version = "0.3.5"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59fbd2b7fb0aea9bdd738fc1441d34d3e7b585d60b42ed63deeac289c872f119"
+checksum = "b52228e706f380074b0722dae97f9c8274264026dbd3aa16d20466758995b6f4"
dependencies = [
+ "activitystreams-kinds",
+ "actix-rt",
"actix-web",
"anyhow",
"async-trait",
"background-jobs",
"base64",
+ "bytes",
"chrono",
- "derive_builder 0.11.2",
+ "derive_builder 0.12.0",
+ "displaydoc",
"dyn-clone",
"enum_delegate",
+ "futures-core",
"http",
- "http-signature-normalization-actix",
+ "http-signature-normalization",
"http-signature-normalization-reqwest",
"httpdate",
"itertools",
"once_cell",
"openssl",
+ "pin-project-lite",
+ "regex",
"reqwest",
"reqwest-middleware",
"serde",
[[package]]
name = "bytes"
-version = "1.2.1"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "bytestring"
[[package]]
name = "derive_builder"
-version = "0.11.2"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3"
+checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
dependencies = [
- "derive_builder_macro 0.11.2",
+ "derive_builder_macro 0.12.0",
]
[[package]]
[[package]]
name = "derive_builder_core"
-version = "0.11.2"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4"
+checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
dependencies = [
"darling 0.14.1",
"proc-macro2 1.0.47",
[[package]]
name = "derive_builder_macro"
-version = "0.11.2"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68"
+checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
dependencies = [
- "derive_builder_core 0.11.2",
+ "derive_builder_core 0.12.0",
"syn 1.0.103",
]
"winapi",
]
+[[package]]
+name = "displaydoc"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
+dependencies = [
+ "proc-macro2 1.0.47",
+ "quote 1.0.21",
+ "syn 1.0.103",
+]
+
[[package]]
name = "dlv-list"
version = "0.3.0"
version = "0.17.1"
dependencies = [
"activitypub_federation",
- "activitystreams-kinds",
"actix-rt",
"actix-web",
"anyhow",
name = "lemmy_routes"
version = "0.17.1"
dependencies = [
+ "activitypub_federation",
"actix-web",
"anyhow",
"chrono",
[[package]]
name = "regex"
-version = "1.6.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"aho-corasick",
"memchr",
lemmy_db_views = { version = "=0.17.1", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.17.1", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.17.1", path = "./crates/db_views_moderator" }
-activitypub_federation = "0.3.5"
+activitypub_federation = { version = "0.4.0", default-features = false, features = ["actix-web"] }
diesel = "2.0.2"
diesel_migrations = "2.0.0"
diesel-async = "0.1.1"
use reqwest_middleware::ClientWithMiddleware;
use std::sync::Arc;
+#[derive(Clone)]
pub struct LemmyContext {
pool: DbPool,
chat_server: Arc<ChatServer>,
- client: ClientWithMiddleware,
- settings: Settings,
- secret: Secret,
+ client: Arc<ClientWithMiddleware>,
+ secret: Arc<Secret>,
rate_limit_cell: RateLimitCell,
}
pool: DbPool,
chat_server: Arc<ChatServer>,
client: ClientWithMiddleware,
- settings: Settings,
secret: Secret,
rate_limit_cell: RateLimitCell,
) -> LemmyContext {
LemmyContext {
pool,
chat_server,
- client,
- settings,
- secret,
+ client: Arc::new(client),
+ secret: Arc::new(secret),
rate_limit_cell,
}
}
&self.rate_limit_cell
}
}
-
-impl Clone for LemmyContext {
- fn clone(&self) -> Self {
- LemmyContext {
- pool: self.pool.clone(),
- chat_server: self.chat_server.clone(),
- client: self.client.clone(),
- settings: self.settings.clone(),
- secret: self.secret.clone(),
- rate_limit_cell: self.rate_limit_cell.clone(),
- }
- }
-}
let first_line = html
.trim_start()
.lines()
- .into_iter()
.next()
.ok_or_else(|| LemmyError::from_message("No lines in html"))?
.to_lowercase();
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct ResolveObject {
pub q: String,
- pub auth: Option<Sensitive<String>>,
+ pub auth: Sensitive<String>,
}
#[derive(Debug, Serialize, Deserialize, Default)]
.map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read"))
}
+// TODO: this should simply take LemmyContext as param
#[tracing::instrument(skip_all)]
pub async fn get_local_user_view_from_jwt(
jwt: &str,
use crate::PerformCrud;
-use activitypub_federation::core::signatures::generate_actor_keypair;
+use activitypub_federation::http_signatures::generate_actor_keypair;
use actix_web::web::Data;
use lemmy_api_common::{
community::{CommunityResponse, CreateCommunity},
use crate::{site::check_application_question, PerformCrud};
-use activitypub_federation::core::signatures::generate_actor_keypair;
+use activitypub_federation::http_signatures::generate_actor_keypair;
use actix_web::web::Data;
use lemmy_api_common::{
context::LemmyContext,
use crate::PerformCrud;
-use activitypub_federation::core::signatures::generate_actor_keypair;
+use activitypub_federation::http_signatures::generate_actor_keypair;
use actix_web::web::Data;
use lemmy_api_common::{
context::LemmyContext,
tokio = { workspace = true }
html2md = "0.2.13"
serde_with = "1.14.0"
-activitystreams-kinds = "0.2.1"
http-signature-normalization-actix = { version = "0.6.1", default-features = false, features = ["server", "sha-2"] }
enum_delegate = "0.2.0"
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
- local_instance,
+ insert_activity,
objects::{instance::remote_instance_inboxes, person::ApubPerson},
protocol::activities::block::block_user::BlockUser,
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ kinds::{activity::BlockType, public},
+ protocol::verification::verify_domains_match,
traits::{ActivityHandler, Actor},
- utils::verify_domains_match,
};
-use activitystreams_kinds::{activity::BlockType, public};
use anyhow::anyhow;
use chrono::NaiveDateTime;
use lemmy_api_common::{
remove_data: Option<bool>,
reason: Option<String>,
expires: Option<NaiveDateTime>,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<BlockUser, LemmyError> {
let audience = if let SiteOrCommunity::Community(c) = target {
- Some(ObjectId::new(c.actor_id()))
+ Some(c.id().into())
} else {
None
};
Ok(BlockUser {
- actor: ObjectId::new(mod_.actor_id()),
+ actor: mod_.id().into(),
to: vec![public()],
- object: ObjectId::new(user.actor_id()),
+ object: user.id().into(),
cc: generate_cc(target, context.pool()).await?,
target: target.id(),
kind: BlockType::Block,
remove_data: bool,
reason: Option<String>,
expires: Option<NaiveDateTime>,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let block = BlockUser::new(
target,
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for BlockUser {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- match self
- .target
- .dereference(context, local_instance(context).await, request_counter)
- .await?
- {
+ match self.target.dereference(context).await? {
SiteOrCommunity::Site(site) => {
let domain = self.object.inner().domain().expect("url needs domain");
if context.settings().hostname == domain {
);
}
// site ban can only target a user who is on the same instance as the actor (admin)
- verify_domains_match(&site.actor_id(), self.actor.inner())?;
- verify_domains_match(&site.actor_id(), self.object.inner())?;
+ verify_domains_match(&site.id(), self.actor.inner())?;
+ verify_domains_match(&site.id(), self.object.inner())?;
}
SiteOrCommunity::Community(community) => {
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
- verify_mod_action(
- &self.actor,
- self.object.inner(),
- community.id,
- context,
- request_counter,
- )
- .await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
+ verify_mod_action(&self.actor, self.object.inner(), community.id, context).await?;
}
}
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
let expires = self.expires.map(|u| u.naive_local());
- let mod_person = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let blocked_person = self
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let target = self
- .target
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let mod_person = self.actor.dereference(context).await?;
+ let blocked_person = self.object.dereference(context).await?;
+ let target = self.target.dereference(context).await?;
match target {
SiteOrCommunity::Site(_site) => {
let blocked_person = Person::update(
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
objects::{group::Group, instance::Instance},
},
- ActorType,
SendActivity,
};
-use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ traits::{Actor, Object},
+};
use chrono::NaiveDateTime;
use lemmy_api_common::{
community::{BanFromCommunity, BanFromCommunityResponse},
Group(Group),
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for SiteOrCommunity {
+#[async_trait::async_trait]
+impl Object for SiteOrCommunity {
type DataType = LemmyContext;
- type ApubType = InstanceOrGroup;
- type DbType = ();
+ type Kind = InstanceOrGroup;
type Error = LemmyError;
#[tracing::instrument(skip_all)]
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- data: &Self::DataType,
+ data: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError>
where
Self: Sized,
{
- let site = ApubSite::read_from_apub_id(object_id.clone(), data).await?;
+ let site = ApubSite::read_from_id(object_id.clone(), data).await?;
Ok(match site {
Some(o) => Some(SiteOrCommunity::Site(o)),
- None => ApubCommunity::read_from_apub_id(object_id, data)
+ None => ApubCommunity::read_from_id(object_id, data)
.await?
.map(SiteOrCommunity::Community),
})
}
- async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
+ async fn delete(self, _data: &Data<Self::DataType>) -> Result<(), LemmyError> {
unimplemented!()
}
- async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
+ async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
unimplemented!()
}
#[tracing::instrument(skip_all)]
async fn verify(
- apub: &Self::ApubType,
+ apub: &Self::Kind,
expected_domain: &Url,
- data: &Self::DataType,
- request_counter: &mut i32,
+ data: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
match apub {
- InstanceOrGroup::Instance(i) => {
- ApubSite::verify(i, expected_domain, data, request_counter).await
- }
- InstanceOrGroup::Group(g) => {
- ApubCommunity::verify(g, expected_domain, data, request_counter).await
- }
+ InstanceOrGroup::Instance(i) => ApubSite::verify(i, expected_domain, data).await,
+ InstanceOrGroup::Group(g) => ApubCommunity::verify(g, expected_domain, data).await,
}
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- apub: Self::ApubType,
- data: &Self::DataType,
- request_counter: &mut i32,
- ) -> Result<Self, LemmyError>
+ async fn from_json(apub: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, LemmyError>
where
Self: Sized,
{
Ok(match apub {
- InstanceOrGroup::Instance(p) => {
- SiteOrCommunity::Site(ApubSite::from_apub(p, data, request_counter).await?)
- }
+ InstanceOrGroup::Instance(p) => SiteOrCommunity::Site(ApubSite::from_json(p, data).await?),
InstanceOrGroup::Group(n) => {
- SiteOrCommunity::Community(ApubCommunity::from_apub(n, data, request_counter).await?)
+ SiteOrCommunity::Community(ApubCommunity::from_json(n, data).await?)
}
})
}
impl SiteOrCommunity {
fn id(&self) -> ObjectId<SiteOrCommunity> {
match self {
- SiteOrCommunity::Site(s) => ObjectId::new(s.actor_id.clone()),
- SiteOrCommunity::Community(c) => ObjectId::new(c.actor_id.clone()),
+ SiteOrCommunity::Site(s) => ObjectId::from(s.actor_id.clone()),
+ SiteOrCommunity::Community(c) => ObjectId::from(c.actor_id.clone()),
}
}
}
.into_iter()
.map(|s| s.actor_id.into())
.collect(),
- SiteOrCommunity::Community(c) => vec![c.actor_id()],
+ SiteOrCommunity::Community(c) => vec![c.id()],
})
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for BanPerson {
type Response = BanPersonResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for BanFromCommunity {
type Response = BanFromCommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
verify_is_public,
},
activity_lists::AnnouncableActivities,
- local_instance,
+ insert_activity,
objects::{instance::remote_instance_inboxes, person::ApubPerson},
protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ kinds::{activity::UndoType, public},
+ protocol::verification::verify_domains_match,
traits::{ActivityHandler, Actor},
- utils::verify_domains_match,
};
-use activitystreams_kinds::{activity::UndoType, public};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
source::{
user: &ApubPerson,
mod_: &ApubPerson,
reason: Option<String>,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let block = BlockUser::new(target, user, mod_, None, reason, None, context).await?;
let audience = if let SiteOrCommunity::Community(c) = target {
- Some(ObjectId::new(c.actor_id()))
+ Some(c.id().into())
} else {
None
};
&context.settings().get_protocol_and_hostname(),
)?;
let undo = UndoBlockUser {
- actor: ObjectId::new(mod_.actor_id()),
+ actor: mod_.id().into(),
to: vec![public()],
object: block,
cc: generate_cc(target, context.pool()).await?,
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for UndoBlockUser {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
- self.object.verify(context, request_counter).await?;
+ self.object.verify(context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let instance = local_instance(context).await;
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
let expires = self.object.expires.map(|u| u.naive_local());
- let mod_person = self
- .actor
- .dereference(context, instance, request_counter)
- .await?;
- let blocked_person = self
- .object
- .object
- .dereference(context, instance, request_counter)
- .await?;
- match self
- .object
- .target
- .dereference(context, instance, request_counter)
- .await?
- {
+ let mod_person = self.actor.dereference(context).await?;
+ let blocked_person = self.object.object.dereference(context).await?;
+ match self.object.target.dereference(context).await? {
SiteOrCommunity::Site(_site) => {
let blocked_person = Person::update(
context.pool(),
IdOrNestedObject,
InCommunity,
},
- ActorType,
};
-use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
-use activitystreams_kinds::{activity::AnnounceType, public};
+use activitypub_federation::{
+ config::Data,
+ kinds::{activity::AnnounceType, public},
+ traits::{ActivityHandler, Actor},
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde_json::Value;
-use tracing::debug;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for RawAnnouncableActivities {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- _data: &Data<Self::DataType>,
- _request_counter: &mut i32,
- ) -> Result<(), Self::Error> {
+ async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- data: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), Self::Error> {
+ async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let activity: AnnouncableActivities = self.clone().try_into()?;
// This is only for sending, not receiving so we reject it.
if let AnnouncableActivities::Page(_) = activity {
return Err(LemmyError::from_message("Cant receive page"));
}
- let community = activity.community(data, &mut 0).await?;
- let actor_id = ObjectId::new(activity.actor().clone());
+ let community = activity.community(data).await?;
+ let actor_id = activity.actor().clone().into();
// verify and receive activity
- activity.verify(data, request_counter).await?;
- activity.receive(data, request_counter).await?;
+ activity.verify(data).await?;
+ activity.receive(data).await?;
// send to community followers
if community.local {
- verify_person_in_community(&actor_id, &community, data, &mut 0).await?;
+ verify_person_in_community(&actor_id, &community, data).await?;
AnnounceActivity::send(self, &community, data).await?;
}
Ok(())
pub(crate) fn new(
object: RawAnnouncableActivities,
community: &ApubCommunity,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<AnnounceActivity, LemmyError> {
Ok(AnnounceActivity {
- actor: ObjectId::new(community.actor_id()),
+ actor: community.id().into(),
to: vec![public()],
object: IdOrNestedObject::NestedObject(object),
cc: vec![community.followers_url.clone().into()],
pub async fn send(
object: RawAnnouncableActivities,
community: &ApubCommunity,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let announce = AnnounceActivity::new(object.clone(), community, context)?;
let inboxes = community.get_follower_inboxes(context).await?;
// Hack: need to convert Page into a format which can be sent as activity, which requires
// adding actor field.
let announcable_page = RawAnnouncableActivities {
- id: c.object.id.clone().into_inner(),
+ id: generate_activity_id(
+ AnnounceType::Announce,
+ &context.settings().get_protocol_and_hostname(),
+ )?,
actor: c.actor.clone().into_inner(),
other: serde_json::to_value(c.object)?
.as_object()
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for AnnounceActivity {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- _context: &Data<LemmyContext>,
- _request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let object: AnnouncableActivities = self
- .object
- .object(context, request_counter)
- .await?
- .try_into()?;
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
+ let object: AnnouncableActivities = self.object.object(context).await?.try_into()?;
// This is only for sending, not receiving so we reject it.
if let AnnouncableActivities::Page(_) = object {
return Err(LemmyError::from_message("Cant receive page"));
}
- // we have to verify this here in order to avoid fetching the object twice over http
- object.verify(context, request_counter).await?;
-
- let object_value = serde_json::to_value(&object)?;
- let insert = insert_activity(object.id(), object_value, false, true, context.pool()).await?;
- if !insert {
- debug!(
- "Received duplicate activity in announce {}",
- object.id().to_string()
- );
- return Ok(());
- }
- object.receive(context, request_counter).await
+ // verify here in order to avoid fetching the object twice over http
+ object.verify(context).await?;
+ object.receive(context).await
}
}
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
- local_instance,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{
activities::{
},
InCommunity,
},
- ActorType,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::{activity::AddType, public},
traits::{ActivityHandler, Actor},
};
-use activitystreams_kinds::{activity::AddType, public};
use lemmy_api_common::{
community::{AddModToCommunity, AddModToCommunityResponse},
context::LemmyContext,
community: &ApubCommunity,
added_mod: &ApubPerson,
actor: &ApubPerson,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
AddType::Add,
&context.settings().get_protocol_and_hostname(),
)?;
let add = CollectionAdd {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
to: vec![public()],
- object: added_mod.actor_id(),
+ object: added_mod.id(),
target: generate_moderators_url(&community.actor_id)?.into(),
- cc: vec![community.actor_id()],
+ cc: vec![community.id()],
kind: AddType::Add,
id: id.clone(),
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
};
let activity = AnnouncableActivities::CollectionAdd(add);
community: &ApubCommunity,
featured_post: &ApubPost,
actor: &ApubPerson,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
AddType::Add,
&context.settings().get_protocol_and_hostname(),
)?;
let add = CollectionAdd {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
to: vec![public()],
object: featured_post.ap_id.clone().into(),
target: generate_featured_url(&community.actor_id)?.into(),
- cc: vec![community.actor_id()],
+ cc: vec![community.id()],
kind: AddType::Add,
id: id.clone(),
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
};
let activity = AnnouncableActivities::CollectionAdd(add);
send_activity_in_community(activity, actor, community, vec![], true, context).await
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for CollectionAdd {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
- verify_mod_action(
- &self.actor,
- &self.object,
- community.id,
- context,
- request_counter,
- )
- .await?;
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
+ verify_mod_action(&self.actor, &self.object, community.id, context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
let (community, collection_type) =
Community::get_by_collection_url(context.pool(), &self.target.into()).await?;
match collection_type {
CollectionType::Moderators => {
- let new_mod = ObjectId::<ApubPerson>::new(self.object)
- .dereference(context, local_instance(context).await, request_counter)
+ let new_mod = ObjectId::<ApubPerson>::from(self.object)
+ .dereference(context)
.await?;
// If we had to refetch the community while parsing the activity, then the new mod has already
CommunityModerator::join(context.pool(), &form).await?;
// write mod log
- let actor = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let actor = self.actor.dereference(context).await?;
let form = ModAddCommunityForm {
mod_person_id: actor.id,
other_person_id: new_mod.id,
// TODO: send websocket notification about added mod
}
CollectionType::Featured => {
- let post = ObjectId::<ApubPost>::new(self.object)
- .dereference(context, local_instance(context).await, request_counter)
+ let post = ObjectId::<ApubPost>::from(self.object)
+ .dereference(context)
.await?;
let form = PostUpdateForm::builder()
.featured_community(Some(true))
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for AddModToCommunity {
type Response = AddModToCommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for FeaturePost {
type Response = PostResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
- local_instance,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{activities::community::collection_remove::CollectionRemove, InCommunity},
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::{activity::RemoveType, public},
traits::{ActivityHandler, Actor},
};
-use activitystreams_kinds::{activity::RemoveType, public};
use lemmy_api_common::{
context::LemmyContext,
utils::{generate_featured_url, generate_moderators_url},
community: &ApubCommunity,
removed_mod: &ApubPerson,
actor: &ApubPerson,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
RemoveType::Remove,
&context.settings().get_protocol_and_hostname(),
)?;
let remove = CollectionRemove {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
to: vec![public()],
- object: ObjectId::new(removed_mod.actor_id()),
+ object: removed_mod.id(),
target: generate_moderators_url(&community.actor_id)?.into(),
id: id.clone(),
- cc: vec![community.actor_id()],
+ cc: vec![community.id()],
kind: RemoveType::Remove,
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
};
let activity = AnnouncableActivities::CollectionRemove(remove);
community: &ApubCommunity,
featured_post: &ApubPost,
actor: &ApubPerson,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
RemoveType::Remove,
&context.settings().get_protocol_and_hostname(),
)?;
let remove = CollectionRemove {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
to: vec![public()],
object: featured_post.ap_id.clone().into(),
target: generate_featured_url(&community.actor_id)?.into(),
- cc: vec![community.actor_id()],
+ cc: vec![community.id()],
kind: RemoveType::Remove,
id: id.clone(),
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
};
let activity = AnnouncableActivities::CollectionRemove(remove);
send_activity_in_community(activity, actor, community, vec![], true, context).await
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for CollectionRemove {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
- verify_mod_action(
- &self.actor,
- self.object.inner(),
- community.id,
- context,
- request_counter,
- )
- .await?;
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
+ verify_mod_action(&self.actor, &self.object, community.id, context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
let (community, collection_type) =
Community::get_by_collection_url(context.pool(), &self.target.into()).await?;
match collection_type {
CollectionType::Moderators => {
- let remove_mod = self
- .object
- .dereference(context, local_instance(context).await, request_counter)
+ let remove_mod = ObjectId::<ApubPerson>::from(self.object)
+ .dereference(context)
.await?;
let form = CommunityModeratorForm {
CommunityModerator::leave(context.pool(), &form).await?;
// write mod log
- let actor = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let actor = self.actor.dereference(context).await?;
let form = ModAddCommunityForm {
mod_person_id: actor.id,
other_person_id: remove_mod.id,
// TODO: send websocket notification about removed mod
}
CollectionType::Featured => {
- let post = ObjectId::<ApubPost>::new(self.object)
- .dereference(context, local_instance(context).await, request_counter)
+ let post = ObjectId::<ApubPost>::from(self.object)
+ .dereference(context)
.await?;
let form = PostUpdateForm::builder()
.featured_community(Some(false))
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
- local_instance,
+ insert_activity,
protocol::{
activities::{
community::lock_page::{LockPage, LockType, UndoLockPage},
},
SendActivity,
};
-use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
-use activitystreams_kinds::{activity::UndoType, public};
+use activitypub_federation::{
+ config::Data,
+ kinds::{activity::UndoType, public},
+ traits::ActivityHandler,
+};
use lemmy_api_common::{
context::LemmyContext,
post::{LockPost, PostResponse},
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for LockPage {
type DataType = LemmyContext;
type Error = LemmyError;
self.actor.inner()
}
- async fn verify(
- &self,
- context: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), Self::Error> {
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
verify_is_public(&self.to, &self.cc)?;
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
check_community_deleted_or_removed(&community)?;
- verify_mod_action(
- &self.actor,
- self.object.inner(),
- community.id,
- context,
- request_counter,
- )
- .await?;
+ verify_mod_action(&self.actor, self.object.inner(), community.id, context).await?;
Ok(())
}
- async fn receive(
- self,
- context: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), Self::Error> {
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let form = PostUpdateForm::builder().locked(Some(true)).build();
- let post = self
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let post = self.object.dereference(context).await?;
Post::update(context.pool(), post.id, &form).await?;
Ok(())
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for UndoLockPage {
type DataType = LemmyContext;
type Error = LemmyError;
self.actor.inner()
}
- async fn verify(
- &self,
- context: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), Self::Error> {
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
verify_is_public(&self.to, &self.cc)?;
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
check_community_deleted_or_removed(&community)?;
verify_mod_action(
&self.actor,
self.object.object.inner(),
community.id,
context,
- request_counter,
)
.await?;
Ok(())
}
- async fn receive(
- self,
- context: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), Self::Error> {
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
+ insert_activity(&self.id, &self, false, false, context).await?;
let form = PostUpdateForm::builder().locked(Some(false)).build();
- let post = self
- .object
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let post = self.object.object.dereference(context).await?;
Post::update(context.pool(), post.id, &form).await?;
Ok(())
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for LockPost {
type Response = PostResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
LockType::Lock,
&context.settings().get_protocol_and_hostname(),
)?;
- let community_id: Url = response.post_view.community.actor_id.clone().into();
- let actor = ObjectId::new(local_user_view.person.actor_id.clone());
+ let community_id = response.post_view.community.actor_id.clone();
+ let actor = local_user_view.person.actor_id.clone().into();
let lock = LockPage {
actor,
to: vec![public()],
- object: ObjectId::new(response.post_view.post.ap_id.clone()),
- cc: vec![community_id.clone()],
+ object: response.post_view.post.ap_id.clone().into(),
+ cc: vec![community_id.clone().into()],
kind: LockType::Lock,
id,
- audience: Some(ObjectId::new(community_id)),
+ audience: Some(community_id.into()),
};
let activity = if request.locked {
AnnouncableActivities::LockPost(lock)
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::announce::AnnounceActivity,
};
-use activitypub_federation::traits::Actor;
+use activitypub_federation::{config::Data, traits::Actor};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::person::PersonFollower;
use lemmy_utils::error::LemmyError;
community: &ApubCommunity,
extra_inboxes: Vec<Url>,
is_mod_action: bool,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
- // send to extra_inboxes
- send_lemmy_activity(context, activity.clone(), actor, extra_inboxes, false).await?;
+ // send to any users which are mentioned or affected directly
+ let mut inboxes = extra_inboxes;
+
+ // send to user followers
+ if !is_mod_action {
+ inboxes.append(
+ &mut PersonFollower::list_followers(context.pool(), actor.id)
+ .await?
+ .into_iter()
+ .map(|p| ApubPerson(p).shared_inbox_or_inbox())
+ .collect(),
+ );
+ }
if community.local {
// send directly to community followers
AnnounceActivity::send(activity.clone().try_into()?, community, context).await?;
} else {
// send to the community, which will then forward to followers
- let inbox = vec![community.shared_inbox_or_inbox()];
- send_lemmy_activity(context, activity.clone(), actor, inbox, false).await?;
- }
-
- // send to those who follow `actor`
- if !is_mod_action {
- let inboxes = PersonFollower::list_followers(context.pool(), actor.id)
- .await?
- .into_iter()
- .map(|p| ApubPerson(p).shared_inbox_or_inbox())
- .collect();
- send_lemmy_activity(context, activity, actor, inboxes, false).await?;
+ inboxes.push(community.shared_inbox_or_inbox());
}
+ send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?;
Ok(())
}
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
- local_instance,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::community::report::Report, InCommunity},
- ActorType,
PostOrComment,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::FlagType,
traits::{ActivityHandler, Actor},
};
-use activitystreams_kinds::activity::FlagType;
use lemmy_api_common::{
comment::{CommentReportResponse, CreateCommentReport},
context::LemmyContext,
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for CreatePostReport {
type Response = PostReportResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
Report::send(
- ObjectId::new(response.post_report_view.post.ap_id.clone()),
+ ObjectId::from(response.post_report_view.post.ap_id.clone()),
&local_user_view.person.into(),
- ObjectId::new(response.post_report_view.community.actor_id.clone()),
+ ObjectId::from(response.post_report_view.community.actor_id.clone()),
request.reason.to_string(),
context,
)
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for CreateCommentReport {
type Response = CommentReportResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
Report::send(
- ObjectId::new(response.comment_report_view.comment.ap_id.clone()),
+ ObjectId::from(response.comment_report_view.comment.ap_id.clone()),
&local_user_view.person.into(),
- ObjectId::new(response.comment_report_view.community.actor_id.clone()),
+ ObjectId::from(response.comment_report_view.community.actor_id.clone()),
request.reason.to_string(),
context,
)
actor: &ApubPerson,
community_id: ObjectId<ApubCommunity>,
reason: String,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let community = community_id.dereference_local(context).await?;
let kind = FlagType::Flag;
&context.settings().get_protocol_and_hostname(),
)?;
let report = Report {
- actor: ObjectId::new(actor.actor_id()),
- to: [ObjectId::new(community.actor_id())],
+ actor: actor.id().into(),
+ to: [community.id().into()],
object: object_id,
summary: reason,
kind,
id: id.clone(),
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
};
let inbox = vec![community.shared_inbox_or_inbox()];
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for Report {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let actor = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- match self
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?
- {
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, true, context).await?;
+ let actor = self.actor.dereference(context).await?;
+ match self.object.dereference(context).await? {
PostOrComment::Post(post) => {
let report_form = PostReportForm {
creator_id: actor.id,
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::community::update::UpdateCommunity, InCommunity},
- ActorType,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
- traits::{ActivityHandler, ApubObject},
+ config::Data,
+ kinds::{activity::UpdateType, public},
+ traits::{ActivityHandler, Actor, Object},
};
-use activitystreams_kinds::{activity::UpdateType, public};
use lemmy_api_common::{
community::{CommunityResponse, EditCommunity, HideCommunity},
context::LemmyContext,
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for EditCommunity {
type Response = CommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
pub async fn send(
community: ApubCommunity,
actor: &ApubPerson,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
UpdateType::Update,
&context.settings().get_protocol_and_hostname(),
)?;
let update = UpdateCommunity {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
to: vec![public()],
- object: Box::new(community.clone().into_apub(context).await?),
- cc: vec![community.actor_id()],
+ object: Box::new(community.clone().into_json(context).await?),
+ cc: vec![community.id()],
kind: UpdateType::Update,
id: id.clone(),
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
};
let activity = AnnouncableActivities::UpdateCommunity(update);
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for UpdateCommunity {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
- verify_mod_action(
- &self.actor,
- self.object.id.inner(),
- community.id,
- context,
- request_counter,
- )
- .await?;
- ApubCommunity::verify(
- &self.object,
- &community.actor_id.clone().into(),
- context,
- request_counter,
- )
- .await?;
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
+ verify_mod_action(&self.actor, self.object.id.inner(), community.id, context).await?;
+ ApubCommunity::verify(&self.object, &community.actor_id.clone().into(), context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let community = self.community(context, request_counter).await?;
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
+ let community = self.community(context).await?;
let community_update_form = self.object.into_update_form();
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for HideCommunity {
type Response = CommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
- local_instance,
+ insert_activity,
mentions::MentionOrValue,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
protocol::{
activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
InCommunity,
},
- ActorType,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
- traits::{ActivityHandler, Actor, ApubObject},
- utils::verify_domains_match,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::public,
+ protocol::verification::verify_domains_match,
+ traits::{ActivityHandler, Actor, Object},
};
-use activitystreams_kinds::public;
use lemmy_api_common::{
comment::{CommentResponse, CreateComment, EditComment},
context::LemmyContext,
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for CreateComment {
type Response = CommentResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdateNote::send(
&response.comment_view.comment,
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for EditComment {
type Response = CommentResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdateNote::send(
&response.comment_view.comment,
comment: &Comment,
person_id: PersonId,
kind: CreateOrUpdateType,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
// TODO: might be helpful to add a comment method to retrieve community directly
let post_id = comment.post_id;
kind.clone(),
&context.settings().get_protocol_and_hostname(),
)?;
- let note = ApubComment(comment.clone()).into_apub(context).await?;
+ let note = ApubComment(comment.clone()).into_json(context).await?;
let create_or_update = CreateOrUpdateNote {
- actor: ObjectId::new(person.actor_id()),
+ actor: person.id().into(),
to: vec![public()],
cc: note.cc.clone(),
tag: note.tag.clone(),
object: note,
kind,
id: id.clone(),
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
};
let tagged_users: Vec<ObjectId<ApubPerson>> = create_or_update
}
})
.map(|t| t.href.clone())
- .map(ObjectId::new)
+ .map(ObjectId::from)
.collect();
let mut inboxes = vec![];
for t in tagged_users {
- let person = t
- .dereference(context, local_instance(context).await, &mut 0)
- .await?;
+ let person = t.dereference(context).await?;
inboxes.push(person.shared_inbox_or_inbox());
}
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for CreateOrUpdateNote {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- let post = self.object.get_parents(context, request_counter).await?.0;
- let community = self.community(context, request_counter).await?;
+ let post = self.object.get_parents(context).await?.0;
+ let community = self.community(context).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
check_community_deleted_or_removed(&community)?;
check_post_deleted_or_removed(&post)?;
- ApubComment::verify(&self.object, self.actor.inner(), context, request_counter).await?;
+ ApubComment::verify(&self.object, self.actor.inner(), context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- // Need to do this check here instead of Note::from_apub because we need the person who
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
+ // Need to do this check here instead of Note::from_json because we need the person who
// send the activity, not the comment author.
let existing_comment = self.object.id.dereference_local(context).await.ok();
if let (Some(distinguished), Some(existing_comment)) =
(self.object.distinguished, existing_comment)
{
if distinguished != existing_comment.distinguished {
- let creator = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let (post, _) = self.object.get_parents(context, request_counter).await?;
+ let creator = self.actor.dereference(context).await?;
+ let (post, _) = self.object.get_parents(context).await?;
is_mod_or_admin(context.pool(), creator.id, post.community_id).await?;
}
}
- let comment = ApubComment::from_apub(self.object, context, request_counter).await?;
+ let comment = ApubComment::from_json(self.object, context).await?;
// author likes their own comment by default
let like_form = CommentLikeForm {
CommentLike::like(context.pool(), &like_form).await?;
let do_send_email = self.kind == CreateOrUpdateType::Create;
- let recipients = get_comment_notif_recipients(
- &self.actor,
- &comment,
- do_send_email,
- context,
- request_counter,
- )
- .await?;
+ let recipients =
+ get_comment_notif_recipients(&self.actor, &comment, do_send_email, context).await?;
let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
CreateOrUpdateType::Update => UserOperationCrud::EditComment,
-use crate::{local_instance, objects::person::ApubPerson};
-use activitypub_federation::core::object_id::ObjectId;
+use crate::objects::person::ApubPerson;
+use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
use lemmy_api_common::{context::LemmyContext, websocket::send::send_local_notifs};
use lemmy_db_schema::{
newtypes::LocalUserId,
actor: &ObjectId<ApubPerson>,
comment: &Comment,
do_send_email: bool,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<Vec<LocalUserId>, LemmyError> {
let post_id = comment.post_id;
let post = Post::read(context.pool(), post_id).await?;
- let actor = actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let actor = actor.dereference(context).await?;
// Note:
// Although mentions could be gotten from the post tags (they are included there), or the ccs,
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
InCommunity,
},
- ActorType,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
- traits::{ActivityHandler, ApubObject},
- utils::{verify_domains_match, verify_urls_match},
+ config::Data,
+ kinds::public,
+ protocol::verification::{verify_domains_match, verify_urls_match},
+ traits::{ActivityHandler, Actor, Object},
};
-use activitystreams_kinds::public;
use lemmy_api_common::{
context::LemmyContext,
post::{CreatePost, EditPost, PostResponse},
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for CreatePost {
type Response = PostResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdatePage::send(
&response.post_view.post,
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for EditPost {
type Response = PostResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdatePage::send(
&response.post_view.post,
actor: &ApubPerson,
community: &ApubCommunity,
kind: CreateOrUpdateType,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<CreateOrUpdatePage, LemmyError> {
let id = generate_activity_id(
kind.clone(),
&context.settings().get_protocol_and_hostname(),
)?;
Ok(CreateOrUpdatePage {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
to: vec![public()],
- object: post.into_apub(context).await?,
- cc: vec![community.actor_id()],
+ object: post.into_json(context).await?,
+ cc: vec![community.id()],
kind,
id: id.clone(),
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
})
}
post: &Post,
person_id: PersonId,
kind: CreateOrUpdateType,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let post = ApubPost(post.clone());
let community_id = post.community_id;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for CreateOrUpdatePage {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
check_community_deleted_or_removed(&community)?;
match self.kind {
CreateOrUpdateType::Update => {
let is_mod_action = self.object.is_mod_action(context).await?;
if is_mod_action {
- verify_mod_action(
- &self.actor,
- self.object.id.inner(),
- community.id,
- context,
- request_counter,
- )
- .await?;
+ verify_mod_action(&self.actor, self.object.id.inner(), community.id, context).await?;
} else {
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
verify_urls_match(self.actor.inner(), self.object.creator()?.inner())?;
}
}
}
- ApubPost::verify(&self.object, self.actor.inner(), context, request_counter).await?;
+ ApubPost::verify(&self.object, self.actor.inner(), context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let post = ApubPost::from_apub(self.object, context, request_counter).await?;
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
+ let post = ApubPost::from_json(self.object, context).await?;
// author likes their own post by default
let like_form = PostLikeForm {
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_person},
+ insert_activity,
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::activities::{
create_or_update::chat_message::CreateOrUpdateChatMessage,
CreateOrUpdateType,
},
- ActorType,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
- traits::{ActivityHandler, Actor, ApubObject},
- utils::verify_domains_match,
+ config::Data,
+ protocol::verification::verify_domains_match,
+ traits::{ActivityHandler, Actor, Object},
};
use lemmy_api_common::{
context::LemmyContext,
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for CreatePrivateMessage {
type Response = PrivateMessageResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdateChatMessage::send(
&response.private_message_view.private_message,
.await
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for EditPrivateMessage {
type Response = PrivateMessageResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdateChatMessage::send(
&response.private_message_view.private_message,
private_message: &PrivateMessage,
sender_id: PersonId,
kind: CreateOrUpdateType,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let recipient_id = private_message.recipient_id;
let sender: ApubPerson = Person::read(context.pool(), sender_id).await?.into();
)?;
let create_or_update = CreateOrUpdateChatMessage {
id: id.clone(),
- actor: ObjectId::new(sender.actor_id()),
- to: [ObjectId::new(recipient.actor_id())],
+ actor: sender.id().into(),
+ to: [recipient.id().into()],
object: ApubPrivateMessage(private_message.clone())
- .into_apub(context)
+ .into_json(context)
.await?,
kind,
};
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for CreateOrUpdateChatMessage {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- verify_person(&self.actor, context, request_counter).await?;
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ verify_person(&self.actor, context).await?;
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;
- ApubPrivateMessage::verify(&self.object, self.actor.inner(), context, request_counter).await?;
+ ApubPrivateMessage::verify(&self.object, self.actor.inner(), context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let private_message =
- ApubPrivateMessage::from_apub(self.object, context, request_counter).await?;
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, true, context).await?;
+ let private_message = ApubPrivateMessage::from_json(self.object, context).await?;
let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id,
},
- local_instance,
- objects::{community::ApubCommunity, person::ApubPerson},
+ insert_activity,
+ objects::person::ApubPerson,
protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
};
-use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
-use activitystreams_kinds::activity::DeleteType;
+use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler};
use lemmy_api_common::{
context::LemmyContext,
websocket::{
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for Delete {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ verify_delete_activity(self, self.summary.is_some(), context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
if let Some(reason) = self.summary {
// We set reason to empty string if it doesn't exist, to distinguish between delete and
// remove. Here we change it back to option, so we don't write it to db.
Some(reason)
};
receive_remove_action(
- &self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?,
+ &self.actor.dereference(context).await?,
self.object.id(),
reason,
context,
)
.await
} else {
- receive_delete_action(
- self.object.id(),
- &self.actor,
- true,
- context,
- request_counter,
- )
- .await
+ receive_delete_action(self.object.id(), &self.actor, true, context).await
}
}
}
to: Url,
community: Option<&Community>,
summary: Option<String>,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<Delete, LemmyError> {
let id = generate_activity_id(
DeleteType::Delete,
)?;
let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
Ok(Delete {
- actor: ObjectId::new(actor.actor_id.clone()),
+ actor: actor.actor_id.clone().into(),
to: vec![to],
object: IdOrNestedObject::Id(object.id()),
cc: cc.into_iter().collect(),
kind: DeleteType::Delete,
summary,
id,
- audience: community.map(|c| ObjectId::<ApubCommunity>::new(c.actor_id.clone())),
+ audience: community.map(|c| c.actor_id.clone().into()),
})
}
}
actor: &ApubPerson,
object: &Url,
reason: Option<String>,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
use UserOperationCrud::*;
match DeletableObjects::read_from_db(object, context).await? {
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
- local_instance,
+ insert_activity,
objects::{instance::remote_instance_inboxes, person::ApubPerson},
protocol::activities::deletion::delete_user::DeleteUser,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
- traits::ActivityHandler,
- utils::verify_urls_match,
+ config::Data,
+ kinds::{activity::DeleteType, public},
+ protocol::verification::verify_urls_match,
+ traits::{ActivityHandler, Actor},
};
-use activitystreams_kinds::{activity::DeleteType, public};
use lemmy_api_common::{
context::LemmyContext,
person::{DeleteAccount, DeleteAccountResponse},
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for DeleteAccount {
type Response = DeleteAccountResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
)
.await?;
- let actor_id = ObjectId::new(actor.actor_id.clone());
let id = generate_activity_id(
DeleteType::Delete,
&context.settings().get_protocol_and_hostname(),
)?;
let delete = DeleteUser {
- actor: actor_id.clone(),
+ actor: actor.id().into(),
to: vec![public()],
- object: actor_id,
+ object: actor.id().into(),
kind: DeleteType::Delete,
id: id.clone(),
cc: vec![],
/// This can be separate from Delete activity because it doesn't need to be handled in shared inbox
/// (cause instance actor doesn't have shared inbox).
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for DeleteUser {
type DataType = LemmyContext;
type Error = LemmyError;
self.actor.inner()
}
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &[])?;
- verify_person(&self.actor, context, request_counter).await?;
+ verify_person(&self.actor, context).await?;
verify_urls_match(self.actor.inner(), self.object.inner())?;
Ok(())
}
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let actor = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
+ let actor = self.actor.dereference(context).await?;
delete_user_account(
actor.id,
context.pool(),
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
- local_instance,
objects::{
comment::ApubComment,
community::ApubCommunity,
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
InCommunity,
},
- ActorType,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- traits::{Actor, ApubObject},
- utils::verify_domains_match,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::public,
+ protocol::verification::verify_domains_match,
+ traits::{Actor, Object},
};
-use activitystreams_kinds::public;
use lemmy_api_common::{
comment::{CommentResponse, DeleteComment, RemoveComment},
community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
pub mod delete_user;
pub mod undo_delete;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for DeletePost {
type Response = PostResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for RemovePost {
type Response = PostResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for DeleteComment {
type Response = CommentResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let community_id = response.comment_view.community.id;
let community = Community::read(context.pool(), community_id).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for RemoveComment {
type Response = CommentResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for DeletePrivateMessage {
type Response = PrivateMessageResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for DeleteCommunity {
type Response = CommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for RemoveCommunity {
type Response = CommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
object: DeletableObjects,
reason: Option<String>,
deleted: bool,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let actor = ApubPerson::from(actor);
let is_mod_action = reason.is_some();
actor: &ApubPerson,
pm: PrivateMessage,
deleted: bool,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let recipient_id = pm.recipient_id;
let recipient: ApubPerson = Person::read(context.pool(), recipient_id).await?.into();
let deletable = DeletableObjects::PrivateMessage(pm.into());
let inbox = vec![recipient.shared_inbox_or_inbox()];
if deleted {
- let delete = Delete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
+ let delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?;
send_lemmy_activity(context, delete, actor, inbox, true).await?;
} else {
- let undo = UndoDelete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
+ let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?;
send_lemmy_activity(context, undo, actor, inbox, true).await?;
};
Ok(())
#[tracing::instrument(skip_all)]
pub(crate) async fn read_from_db(
ap_id: &Url,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<DeletableObjects, LemmyError> {
- if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
+ if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Community(c));
}
- if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? {
+ if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Post(p));
}
- if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
+ if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Comment(c));
}
- if let Some(p) = ApubPrivateMessage::read_from_apub_id(ap_id.clone(), context).await? {
+ if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::PrivateMessage(p));
}
Err(diesel::NotFound.into())
pub(crate) fn id(&self) -> Url {
match self {
- DeletableObjects::Community(c) => c.actor_id(),
+ DeletableObjects::Community(c) => c.id(),
DeletableObjects::Comment(c) => c.ap_id.clone().into(),
DeletableObjects::Post(p) => p.ap_id.clone().into(),
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
pub(in crate::activities) async fn verify_delete_activity(
activity: &Delete,
is_mod_action: bool,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
match object {
if community.local {
// can only do this check for local community, in remote case it would try to fetch the
// deleted community (which fails)
- verify_person_in_community(&activity.actor, &community, context, request_counter).await?;
+ verify_person_in_community(&activity.actor, &community, context).await?;
}
// community deletion is always a mod (or admin) action
- verify_mod_action(
- &activity.actor,
- activity.object.id(),
- community.id,
- context,
- request_counter,
- )
- .await?;
+ verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
}
DeletableObjects::Post(p) => {
verify_is_public(&activity.to, &[])?;
verify_delete_post_or_comment(
&activity.actor,
&p.ap_id.clone().into(),
- &activity.community(context, request_counter).await?,
+ &activity.community(context).await?,
is_mod_action,
context,
- request_counter,
)
.await?;
}
verify_delete_post_or_comment(
&activity.actor,
&c.ap_id.clone().into(),
- &activity.community(context, request_counter).await?,
+ &activity.community(context).await?,
is_mod_action,
context,
- request_counter,
)
.await?;
}
DeletableObjects::PrivateMessage(_) => {
- verify_person(&activity.actor, context, request_counter).await?;
+ verify_person(&activity.actor, context).await?;
verify_domains_match(activity.actor.inner(), activity.object.id())?;
}
}
object_id: &Url,
community: &ApubCommunity,
is_mod_action: bool,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
- verify_person_in_community(actor, community, context, request_counter).await?;
+ verify_person_in_community(actor, community, context).await?;
if is_mod_action {
- verify_mod_action(actor, object_id, community.id, context, request_counter).await?;
+ verify_mod_action(actor, object_id, community.id, context).await?;
} else {
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
verify_domains_match(actor.inner(), object_id)?;
object: &Url,
actor: &ObjectId<ApubPerson>,
deleted: bool,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => {
if community.local {
- let mod_: Person = actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?
- .deref()
- .clone();
+ let mod_: Person = actor.dereference(context).await?.deref().clone();
let object = DeletableObjects::Community(community.clone());
let c: Community = community.deref().deref().clone();
send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id,
},
- local_instance,
- objects::{community::ApubCommunity, person::ApubPerson},
+ insert_activity,
+ objects::person::ApubPerson,
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
};
-use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
-use activitystreams_kinds::activity::UndoType;
+use activitypub_federation::{config::Data, kinds::activity::UndoType, traits::ActivityHandler};
use lemmy_api_common::{
context::LemmyContext,
websocket::{
use lemmy_utils::error::LemmyError;
use url::Url;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for UndoDelete {
type DataType = LemmyContext;
type Error = LemmyError;
self.actor.inner()
}
- #[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- self.object.verify(context, request_counter).await?;
- verify_delete_activity(
- &self.object,
- self.object.summary.is_some(),
- context,
- request_counter,
- )
- .await?;
+ async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
+ self.object.verify(data).await?;
+ verify_delete_activity(&self.object, self.object.summary.is_some(), data).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, false, context).await?;
if self.object.summary.is_some() {
UndoDelete::receive_undo_remove_action(
- &self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?,
+ &self.actor.dereference(context).await?,
self.object.object.id(),
context,
)
.await
} else {
- receive_delete_action(
- self.object.object.id(),
- &self.actor,
- false,
- context,
- request_counter,
- )
- .await
+ receive_delete_action(self.object.object.id(), &self.actor, false, context).await
}
}
}
to: Url,
community: Option<&Community>,
summary: Option<String>,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<UndoDelete, LemmyError> {
let object = Delete::new(actor, object, to.clone(), community, summary, context)?;
)?;
let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
Ok(UndoDelete {
- actor: ObjectId::new(actor.actor_id.clone()),
+ actor: actor.actor_id.clone().into(),
to: vec![to],
object,
cc: cc.into_iter().collect(),
kind: UndoType::Undo,
id,
- audience: community.map(|c| ObjectId::<ApubCommunity>::new(c.actor_id.clone())),
+ audience: community.map(|c| c.actor_id.clone().into()),
})
}
pub(in crate::activities) async fn receive_undo_remove_action(
actor: &ApubPerson,
object: &Url,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
use UserOperationCrud::*;
match DeletableObjects::read_from_db(object, context).await? {
use crate::{
activities::{generate_activity_id, send_lemmy_activity},
- local_instance,
+ insert_activity,
protocol::activities::following::{accept::AcceptFollow, follow::Follow},
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ kinds::activity::AcceptType,
+ protocol::verification::verify_urls_match,
traits::{ActivityHandler, Actor},
- utils::verify_urls_match,
};
-use activitystreams_kinds::activity::AcceptType;
use lemmy_api_common::{
community::CommunityResponse,
context::LemmyContext,
impl AcceptFollow {
#[tracing::instrument(skip_all)]
- pub async fn send(
- follow: Follow,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ pub async fn send(follow: Follow, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
let user_or_community = follow.object.dereference_local(context).await?;
- let person = follow
- .actor
- .clone()
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let person = follow.actor.clone().dereference(context).await?;
let accept = AcceptFollow {
- actor: ObjectId::new(user_or_community.actor_id()),
+ actor: user_or_community.id().into(),
object: follow,
kind: AcceptType::Accept,
id: generate_activity_id(
}
/// Handle accepted follows
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for AcceptFollow {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
verify_urls_match(self.actor.inner(), self.object.object.inner())?;
- self.object.verify(context, request_counter).await?;
+ self.object.verify(context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let community = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let person = self
- .object
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, true, context).await?;
+ let community = self.actor.dereference(context).await?;
+ let person = self.object.actor.dereference(context).await?;
// This will throw an error if no follow was requested
let community_id = community.id;
let person_id = person.id;
verify_person_in_community,
},
fetcher::user_or_community::UserOrCommunity,
- local_instance,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::following::{
accept::AcceptFollow,
follow::Follow,
undo_follow::UndoFollow,
},
- ActorType,
SendActivity,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ kinds::activity::FollowType,
traits::{ActivityHandler, Actor},
};
-use activitystreams_kinds::activity::FollowType;
use lemmy_api_common::{
community::{BlockCommunity, BlockCommunityResponse},
context::LemmyContext,
pub(in crate::activities::following) fn new(
actor: &ApubPerson,
community: &ApubCommunity,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<Follow, LemmyError> {
Ok(Follow {
- actor: ObjectId::new(actor.actor_id()),
- object: ObjectId::new(community.actor_id()),
+ actor: actor.id().into(),
+ object: community.id().into(),
kind: FollowType::Follow,
id: generate_activity_id(
FollowType::Follow,
pub async fn send(
actor: &ApubPerson,
community: &ApubCommunity,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for Follow {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- verify_person(&self.actor, context, request_counter).await?;
- let object = self
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ verify_person(&self.actor, context).await?;
+ let object = self.object.dereference(context).await?;
if let UserOrCommunity::Community(c) = object {
- verify_person_in_community(&self.actor, &c, context, request_counter).await?;
+ verify_person_in_community(&self.actor, &c, context).await?;
}
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let actor = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let object = self
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, true, context).await?;
+ let actor = self.actor.dereference(context).await?;
+ let object = self.object.dereference(context).await?;
match object {
UserOrCommunity::User(u) => {
let form = PersonFollowerForm {
}
}
- AcceptFollow::send(self, context, request_counter).await
+ AcceptFollow::send(self, context).await
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for BlockCommunity {
type Response = BlockCommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
SendActivity,
};
+use activitypub_federation::config::Data;
use lemmy_api_common::{
community::{CommunityResponse, FollowCommunity},
context::LemmyContext,
pub mod follow;
pub mod undo_follow;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for FollowCommunity {
type Response = CommunityResponse;
async fn send_activity(
request: &Self,
_response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view =
get_local_user_view_from_jwt(&request.auth, context.pool(), context.secret()).await?;
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_person},
fetcher::user_or_community::UserOrCommunity,
- local_instance,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
+ config::Data,
+ kinds::activity::UndoType,
+ protocol::verification::verify_urls_match,
traits::{ActivityHandler, Actor},
- utils::verify_urls_match,
};
-use activitystreams_kinds::activity::UndoType;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
source::{
pub async fn send(
actor: &ApubPerson,
community: &ApubCommunity,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let object = Follow::new(actor, community, context)?;
let undo = UndoFollow {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
object,
kind: UndoType::Undo,
id: generate_activity_id(
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for UndoFollow {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
- verify_person(&self.actor, context, request_counter).await?;
- self.object.verify(context, request_counter).await?;
+ verify_person(&self.actor, context).await?;
+ self.object.verify(context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let person = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let object = self
- .object
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, true, context).await?;
+ let person = self.actor.dereference(context).await?;
+ let object = self.object.object.dereference(context).await?;
match object {
UserOrCommunity::User(u) => {
use crate::{
insert_activity,
- local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
- ActorType,
CONTEXT,
};
use activitypub_federation::{
- core::{activity_queue::send_activity, object_id::ObjectId},
- deser::context::WithContext,
+ activity_queue::send_activity,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::public,
+ protocol::context::WithContext,
traits::{ActivityHandler, Actor},
};
-use activitystreams_kinds::public;
use anyhow::anyhow;
use lemmy_api_common::context::LemmyContext;
-use lemmy_db_schema::{
- newtypes::CommunityId,
- source::{community::Community, local_site::LocalSite},
-};
+use lemmy_db_schema::{newtypes::CommunityId, source::community::Community};
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
use lemmy_utils::error::LemmyError;
use serde::Serialize;
#[tracing::instrument(skip_all)]
async fn verify_person(
person_id: &ObjectId<ApubPerson>,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
- let person = person_id
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let person = person_id.dereference(context).await?;
if person.banned {
let err = anyhow!("Person {} is banned", person_id);
return Err(LemmyError::from_error_message(err, "banned"));
pub(crate) async fn verify_person_in_community(
person_id: &ObjectId<ApubPerson>,
community: &ApubCommunity,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
- let person = person_id
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let person = person_id.dereference(context).await?;
if person.banned {
return Err(LemmyError::from_message("Person is banned from site"));
}
mod_id: &ObjectId<ApubPerson>,
object_id: &Url,
community_id: CommunityId,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
- let mod_ = mod_id
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let mod_ = mod_id.dereference(context).await?;
let is_mod_or_admin =
CommunityView::is_mod_or_admin(context.pool(), mod_.id, community_id).await?;
#[tracing::instrument(skip_all)]
async fn send_lemmy_activity<Activity, ActorT>(
- context: &LemmyContext,
+ data: &Data<LemmyContext>,
activity: Activity,
actor: &ActorT,
inbox: Vec<Url>,
sensitive: bool,
) -> Result<(), LemmyError>
where
- Activity: ActivityHandler + Serialize,
- ActorT: Actor + ActorType,
+ Activity: ActivityHandler + Serialize + Send + Sync + Clone,
+ ActorT: Actor,
Activity: ActivityHandler<Error = LemmyError>,
{
- let federation_enabled = LocalSite::read(context.pool())
- .await
- .map(|l| l.federation_enabled)
- .unwrap_or(false);
- if !federation_enabled {
- return Ok(());
- }
-
info!("Sending activity {}", activity.id().to_string());
let activity = WithContext::new(activity, CONTEXT.deref().clone());
- let object_value = serde_json::to_value(&activity)?;
- insert_activity(activity.id(), object_value, true, sensitive, context.pool()).await?;
-
- send_activity(
- activity,
- actor.get_public_key(),
- actor.private_key().expect("actor has private key"),
- inbox,
- local_instance(context).await,
- )
- .await?;
+ insert_activity(activity.id(), &activity, true, sensitive, data).await?;
+ send_activity(activity, actor, inbox, data).await?;
Ok(())
}
},
SendActivity,
};
-use activitypub_federation::core::object_id::ObjectId;
+use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
use lemmy_api_common::{
comment::{CommentResponse, CreateCommentLike},
context::LemmyContext,
pub mod undo_vote;
pub mod vote;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for CreatePostLike {
type Response = PostResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
- let object_id = ObjectId::new(response.post_view.post.ap_id.clone());
+ let object_id = ObjectId::from(response.post_view.post.ap_id.clone());
let community_id = response.post_view.community.id;
send_activity(
object_id,
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl SendActivity for CreateCommentLike {
type Response = CommentResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
- let object_id = ObjectId::new(response.comment_view.comment.ap_id.clone());
+ let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone());
let community_id = response.comment_view.community.id;
send_activity(
object_id,
community_id: CommunityId,
score: i16,
jwt: &Sensitive<String>,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let community = Community::read(context.pool(), community_id).await?.into();
let local_user_view = get_local_user_view_from_jwt(jwt, context.pool(), context.secret()).await?;
vote_type: &VoteType,
actor: ApubPerson,
comment: &ApubComment,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let comment_id = comment.id;
let like_form = CommentLikeForm {
vote_type: &VoteType,
actor: ApubPerson,
post: &ApubPost,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let post_id = post.id;
let like_form = PostLikeForm {
async fn undo_vote_comment(
actor: ApubPerson,
comment: &ApubComment,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let comment_id = comment.id;
let person_id = actor.id;
async fn undo_vote_post(
actor: ApubPerson,
post: &ApubPost,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let post_id = post.id;
let person_id = actor.id;
verify_person_in_community,
voting::{undo_vote_comment, undo_vote_post},
},
- local_instance,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{
activities::voting::{undo_vote::UndoVote, vote::Vote},
InCommunity,
},
- ActorType,
PostOrComment,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
- traits::ActivityHandler,
- utils::verify_urls_match,
+ config::Data,
+ kinds::activity::UndoType,
+ protocol::verification::verify_urls_match,
+ traits::{ActivityHandler, Actor},
};
-use activitystreams_kinds::activity::UndoType;
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use url::Url;
vote: Vote,
actor: &ApubPerson,
community: &ApubCommunity,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<Self, LemmyError> {
Ok(UndoVote {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
object: vote,
kind: UndoType::Undo,
id: generate_activity_id(
UndoType::Undo,
&context.settings().get_protocol_and_hostname(),
)?,
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
})
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for UndoVote {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
- self.object.verify(context, request_counter).await?;
+ self.object.verify(context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let actor = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let object = self
- .object
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, true, context).await?;
+ let actor = self.actor.dereference(context).await?;
+ let object = self.object.object.dereference(context).await?;
match object {
PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await,
PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await,
verify_person_in_community,
voting::{vote_comment, vote_post},
},
- local_instance,
+ insert_activity,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{
activities::voting::vote::{Vote, VoteType},
InCommunity,
},
- ActorType,
PostOrComment,
};
-use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ traits::{ActivityHandler, Actor},
+};
use anyhow::anyhow;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::local_site::LocalSite;
actor: &ApubPerson,
community: &ApubCommunity,
kind: VoteType,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<Vote, LemmyError> {
Ok(Vote {
- actor: ObjectId::new(actor.actor_id()),
+ actor: actor.id().into(),
object: object_id,
kind: kind.clone(),
id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
- audience: Some(ObjectId::new(community.actor_id())),
+ audience: Some(community.id().into()),
})
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for Vote {
type DataType = LemmyContext;
type Error = LemmyError;
}
#[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let community = self.community(context, request_counter).await?;
- verify_person_in_community(&self.actor, &community, context, request_counter).await?;
+ async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ let community = self.community(context).await?;
+ verify_person_in_community(&self.actor, &community, context).await?;
let enable_downvotes = LocalSite::read(context.pool())
.await
.map(|l| l.enable_downvotes)
}
#[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let actor = self
- .actor
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let object = self
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
+ insert_activity(&self.id, &self, false, true, context).await?;
+ let actor = self.actor.dereference(context).await?;
+ let object = self.object.dereference(context).await?;
match object {
PostOrComment::Post(p) => vote_post(&self.kind, actor, &p, context).await,
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await,
InCommunity,
},
};
-use activitypub_federation::{data::Data, deser::context::WithContext, traits::ActivityHandler};
+use activitypub_federation::{
+ config::Data,
+ protocol::context::WithContext,
+ traits::ActivityHandler,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
DeleteUser(DeleteUser),
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for AnnouncableActivities {
#[tracing::instrument(skip(self, context))]
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
use AnnouncableActivities::*;
match self {
- CreateOrUpdateComment(a) => a.community(context, request_counter).await,
- CreateOrUpdatePost(a) => a.community(context, request_counter).await,
- Vote(a) => a.community(context, request_counter).await,
- UndoVote(a) => a.community(context, request_counter).await,
- Delete(a) => a.community(context, request_counter).await,
- UndoDelete(a) => a.community(context, request_counter).await,
- UpdateCommunity(a) => a.community(context, request_counter).await,
- BlockUser(a) => a.community(context, request_counter).await,
- UndoBlockUser(a) => a.community(context, request_counter).await,
- CollectionAdd(a) => a.community(context, request_counter).await,
- CollectionRemove(a) => a.community(context, request_counter).await,
- LockPost(a) => a.community(context, request_counter).await,
- UndoLockPost(a) => a.community(context, request_counter).await,
+ CreateOrUpdateComment(a) => a.community(context).await,
+ CreateOrUpdatePost(a) => a.community(context).await,
+ Vote(a) => a.community(context).await,
+ UndoVote(a) => a.community(context).await,
+ Delete(a) => a.community(context).await,
+ UndoDelete(a) => a.community(context).await,
+ UpdateCommunity(a) => a.community(context).await,
+ BlockUser(a) => a.community(context).await,
+ UndoBlockUser(a) => a.community(context).await,
+ CollectionAdd(a) => a.community(context).await,
+ CollectionRemove(a) => a.community(context).await,
+ LockPost(a) => a.community(context).await,
+ UndoLockPost(a) => a.community(context).await,
Page(_) => unimplemented!(),
}
}
fetcher::resolve_actor_identifier,
objects::community::ApubCommunity,
};
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
use lemmy_api_common::{
comment::{GetComments, GetCommentsResponse},
context::LemmyContext,
use lemmy_db_views::comment_view::CommentQuery;
use lemmy_utils::{error::LemmyError, ConnectionId};
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl PerformApub for GetComments {
type Response = GetCommentsResponse;
let listing_type = listing_type_with_site_default(data.type_, &local_site)?;
let community_actor_id = if let Some(name) = &data.community_name {
- resolve_actor_identifier::<ApubCommunity, Community>(name, context, true)
+ resolve_actor_identifier::<ApubCommunity, Community>(name, context, &None, true)
.await
.ok()
- .map(|c| c.actor_id)
+ .map(|c| c.actor_id.clone())
} else {
None
};
fetcher::resolve_actor_identifier,
objects::community::ApubCommunity,
};
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
use lemmy_api_common::{
context::LemmyContext,
post::{GetPosts, GetPostsResponse},
use lemmy_db_views::post_view::PostQuery;
use lemmy_utils::{error::LemmyError, ConnectionId};
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl PerformApub for GetPosts {
type Response = GetPostsResponse;
let limit = data.limit;
let community_id = data.community_id;
let community_actor_id = if let Some(name) = &data.community_name {
- resolve_actor_identifier::<ApubCommunity, Community>(name, context, true)
+ resolve_actor_identifier::<ApubCommunity, Community>(name, context, &None, true)
.await
.ok()
- .map(|c| c.actor_id)
+ .map(|c| c.actor_id.clone())
} else {
None
};
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::{error::LemmyError, ConnectionId};
mod resolve_object;
mod search;
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
pub trait PerformApub {
type Response: serde::ser::Serialize + Send;
fetcher::resolve_actor_identifier,
objects::community::ApubCommunity,
};
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
use lemmy_api_common::{
community::{GetCommunity, GetCommunityResponse},
context::LemmyContext,
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::{error::LemmyError, ConnectionId};
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl PerformApub for GetCommunity {
type Response = GetCommunityResponse;
Some(id) => id,
None => {
let name = data.name.clone().unwrap_or_else(|| "main".to_string());
- resolve_actor_identifier::<ApubCommunity, Community>(&name, context, true)
+ resolve_actor_identifier::<ApubCommunity, Community>(&name, context, &local_user_view, true)
.await
.map_err(|e| e.with_message("couldnt_find_community"))?
.id
use crate::{api::PerformApub, fetcher::resolve_actor_identifier, objects::person::ApubPerson};
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
use lemmy_api_common::{
context::LemmyContext,
person::{GetPersonDetails, GetPersonDetailsResponse},
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView};
use lemmy_utils::{error::LemmyError, ConnectionId};
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl PerformApub for GetPersonDetails {
type Response = GetPersonDetailsResponse;
Some(id) => id,
None => {
if let Some(username) = &data.username {
- resolve_actor_identifier::<ApubPerson, Person>(username, context, true)
+ resolve_actor_identifier::<ApubPerson, Person>(username, context, &local_user_view, true)
.await
.map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?
.id
api::PerformApub,
fetcher::search::{search_query_to_object_id, SearchableObjects},
};
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
use diesel::NotFound;
use lemmy_api_common::{
context::LemmyContext,
site::{ResolveObject, ResolveObjectResponse},
- utils::{check_private_instance, get_local_user_view_from_jwt_opt},
+ utils::{check_private_instance, get_local_user_view_from_jwt},
};
use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool};
use lemmy_db_views::structs::{CommentView, PostView};
use lemmy_db_views_actor::structs::{CommunityView, PersonView};
use lemmy_utils::{error::LemmyError, ConnectionId};
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl PerformApub for ResolveObject {
type Response = ResolveObjectResponse;
_websocket_id: Option<ConnectionId>,
) -> Result<ResolveObjectResponse, LemmyError> {
let local_user_view =
- get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
- .await?;
+ get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?;
let local_site = LocalSite::read(context.pool()).await?;
- check_private_instance(&local_user_view, &local_site)?;
+ let person_id = local_user_view.person.id;
+ check_private_instance(&Some(local_user_view), &local_site)?;
- // In release builds only allow for authenticated users to fetch remote objects
- let local_only = local_user_view.is_none() && cfg!(not(debug_assertions));
- let res = search_query_to_object_id(&self.q, local_only, context)
+ let res = search_query_to_object_id(&self.q, context)
.await
.map_err(|e| e.with_message("couldnt_find_object"))?;
- convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
+ convert_response(res, person_id, context.pool())
.await
.map_err(|e| e.with_message("couldnt_find_object"))
}
async fn convert_response(
object: SearchableObjects,
- user_id: Option<PersonId>,
+ user_id: PersonId,
pool: &DbPool,
) -> Result<ResolveObjectResponse, LemmyError> {
use SearchableObjects::*;
}
Community(c) => {
removed_or_deleted = c.deleted || c.removed;
- res.community = Some(CommunityView::read(pool, c.id, user_id, None).await?)
+ res.community = Some(CommunityView::read(pool, c.id, Some(user_id), None).await?)
}
Post(p) => {
removed_or_deleted = p.deleted || p.removed;
- res.post = Some(PostView::read(pool, p.id, user_id, None).await?)
+ res.post = Some(PostView::read(pool, p.id, Some(user_id), None).await?)
}
Comment(c) => {
removed_or_deleted = c.deleted || c.removed;
- res.comment = Some(CommentView::read(pool, c.id, user_id).await?)
+ res.comment = Some(CommentView::read(pool, c.id, Some(user_id)).await?)
}
};
// if the object was deleted from database, dont return it
fetcher::resolve_actor_identifier,
objects::community::ApubCommunity,
};
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
use lemmy_api_common::{
context::LemmyContext,
site::{Search, SearchResponse},
use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery};
use lemmy_utils::{error::LemmyError, ConnectionId};
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl PerformApub for Search {
type Response = SearchResponse;
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
- let local_user = local_user_view.map(|l| l.local_user);
-
let mut posts = Vec::new();
let mut comments = Vec::new();
let mut communities = Vec::new();
let search_type = data.type_.unwrap_or(SearchType::All);
let community_id = data.community_id;
let community_actor_id = if let Some(name) = &data.community_name {
- resolve_actor_identifier::<ApubCommunity, Community>(name, context, false)
+ resolve_actor_identifier::<ApubCommunity, Community>(name, context, &local_user_view, false)
.await
.ok()
- .map(|c| c.actor_id)
+ .map(|c| c.actor_id.clone())
} else {
None
};
let creator_id = data.creator_id;
+ let local_user = local_user_view.map(|l| l.local_user);
match search_type {
SearchType::Posts => {
posts = PostQuery::builder()
use crate::{
- collections::CommunityContext,
- objects::post::ApubPost,
+ objects::{community::ApubCommunity, post::ApubPost},
protocol::collections::group_featured::GroupFeatured,
};
use activitypub_federation::{
- data::Data,
- traits::{ActivityHandler, ApubObject},
- utils::verify_domains_match,
+ config::Data,
+ kinds::collection::OrderedCollectionType,
+ protocol::verification::verify_domains_match,
+ traits::{ActivityHandler, Collection, Object},
};
-use activitystreams_kinds::collection::OrderedCollectionType;
use futures::future::{join_all, try_join_all};
-use lemmy_api_common::utils::generate_featured_url;
+use lemmy_api_common::{context::LemmyContext, utils::generate_featured_url};
use lemmy_db_schema::{source::post::Post, utils::FETCH_LIMIT_MAX};
use lemmy_utils::error::LemmyError;
use url::Url;
#[derive(Clone, Debug)]
pub(crate) struct ApubCommunityFeatured(Vec<ApubPost>);
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubCommunityFeatured {
- type DataType = CommunityContext;
- type ApubType = GroupFeatured;
- type DbType = ();
+#[async_trait::async_trait]
+impl Collection for ApubCommunityFeatured {
+ type Owner = ApubCommunity;
+ type DataType = LemmyContext;
+ type Kind = GroupFeatured;
type Error = LemmyError;
- async fn read_from_apub_id(
- _object_id: Url,
- data: &Self::DataType,
- ) -> Result<Option<Self>, Self::Error>
- where
- Self: Sized,
- {
- // Only read from database if its a local community, otherwise fetch over http
- if data.0.local {
- let community_id = data.0.id;
- let post_list: Vec<ApubPost> = Post::list_featured_for_community(data.1.pool(), community_id)
+ async fn read_local(
+ owner: &Self::Owner,
+ data: &Data<Self::DataType>,
+ ) -> Result<Self::Kind, Self::Error> {
+ let ordered_items = try_join_all(
+ Post::list_featured_for_community(data.pool(), owner.id)
.await?
.into_iter()
- .map(Into::into)
- .collect();
- Ok(Some(ApubCommunityFeatured(post_list)))
- } else {
- Ok(None)
- }
- }
-
- async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, Self::Error> {
- let ordered_items = try_join_all(self.0.into_iter().map(|p| p.into_apub(&data.1))).await?;
+ .map(ApubPost::from)
+ .map(|p| p.into_json(data)),
+ )
+ .await?;
Ok(GroupFeatured {
r#type: OrderedCollectionType::OrderedCollection,
- id: generate_featured_url(&data.0.actor_id)?.into(),
+ id: generate_featured_url(&owner.actor_id)?.into(),
total_items: ordered_items.len() as i32,
ordered_items,
})
}
async fn verify(
- apub: &Self::ApubType,
+ apub: &Self::Kind,
expected_domain: &Url,
- _data: &Self::DataType,
- _request_counter: &mut i32,
+ _data: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(expected_domain, &apub.id)?;
Ok(())
}
- async fn from_apub(
- apub: Self::ApubType,
- data: &Self::DataType,
- _request_counter: &mut i32,
+ async fn from_json(
+ apub: Self::Kind,
+ _owner: &Self::Owner,
+ data: &Data<Self::DataType>,
) -> Result<Self, Self::Error>
where
Self: Sized,
// We intentionally ignore errors here. This is because the outbox might contain posts from old
// Lemmy versions, or from other software which we cant parse. In that case, we simply skip the
// item and only parse the ones that work.
- let data = Data::new(data.1.clone());
// process items in parallel, to avoid long delay from fetch_site_metadata() and other processing
join_all(posts.into_iter().map(|post| {
async {
// use separate request counter for each item, otherwise there will be problems with
// parallel processing
- let request_counter = &mut 0;
- let verify = post.verify(&data, request_counter).await;
+ let verify = post.verify(data).await;
if verify.is_ok() {
- post.receive(&data, request_counter).await.ok();
+ post.receive(data).await.ok();
}
}
}))
use crate::{
- collections::CommunityContext,
- local_instance,
- objects::person::ApubPerson,
+ objects::{community::ApubCommunity, person::ApubPerson},
protocol::collections::group_moderators::GroupModerators,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- traits::ApubObject,
- utils::verify_domains_match,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::collection::OrderedCollectionType,
+ protocol::verification::verify_domains_match,
+ traits::Collection,
};
-use activitystreams_kinds::collection::OrderedCollectionType;
-use chrono::NaiveDateTime;
-use lemmy_api_common::utils::generate_moderators_url;
+use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url};
use lemmy_db_schema::{
source::community::{CommunityModerator, CommunityModeratorForm},
traits::Joinable,
#[derive(Clone, Debug)]
pub(crate) struct ApubCommunityModerators(pub(crate) Vec<CommunityModeratorView>);
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubCommunityModerators {
- type DataType = CommunityContext;
- type ApubType = GroupModerators;
+#[async_trait::async_trait]
+impl Collection for ApubCommunityModerators {
+ type Owner = ApubCommunity;
+ type DataType = LemmyContext;
+ type Kind = GroupModerators;
type Error = LemmyError;
- fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
- None
- }
-
- #[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
- _object_id: Url,
- data: &Self::DataType,
- ) -> Result<Option<Self>, LemmyError> {
- // Only read from database if its a local community, otherwise fetch over http
- if data.0.local {
- let cid = data.0.id;
- let moderators = CommunityModeratorView::for_community(data.1.pool(), cid).await?;
- Ok(Some(ApubCommunityModerators(moderators)))
- } else {
- Ok(None)
- }
- }
-
#[tracing::instrument(skip_all)]
- async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
- unimplemented!()
- }
-
- #[tracing::instrument(skip_all)]
- async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
- let ordered_items = self
- .0
+ async fn read_local(
+ owner: &Self::Owner,
+ data: &Data<Self::DataType>,
+ ) -> Result<Self::Kind, LemmyError> {
+ let moderators = CommunityModeratorView::for_community(data.pool(), owner.id).await?;
+ let ordered_items = moderators
.into_iter()
- .map(|m| ObjectId::<ApubPerson>::new(m.moderator.actor_id))
+ .map(|m| ObjectId::<ApubPerson>::from(m.moderator.actor_id))
.collect();
Ok(GroupModerators {
r#type: OrderedCollectionType::OrderedCollection,
- id: generate_moderators_url(&data.0.actor_id)?.into(),
+ id: generate_moderators_url(&owner.actor_id)?.into(),
ordered_items,
})
}
async fn verify(
group_moderators: &GroupModerators,
expected_domain: &Url,
- _context: &CommunityContext,
- _request_counter: &mut i32,
+ _data: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
verify_domains_match(&group_moderators.id, expected_domain)?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- apub: Self::ApubType,
- data: &Self::DataType,
- request_counter: &mut i32,
+ async fn from_json(
+ apub: Self::Kind,
+ owner: &Self::Owner,
+ data: &Data<Self::DataType>,
) -> Result<Self, LemmyError> {
- let community_id = data.0.id;
+ let community_id = owner.id;
let current_moderators =
- CommunityModeratorView::for_community(data.1.pool(), community_id).await?;
+ CommunityModeratorView::for_community(data.pool(), community_id).await?;
// Remove old mods from database which arent in the moderators collection anymore
for mod_user in ¤t_moderators {
- let mod_id = ObjectId::new(mod_user.moderator.actor_id.clone());
+ let mod_id = ObjectId::from(mod_user.moderator.actor_id.clone());
if !apub.ordered_items.contains(&mod_id) {
let community_moderator_form = CommunityModeratorForm {
community_id: mod_user.community.id,
person_id: mod_user.moderator.id,
};
- CommunityModerator::leave(data.1.pool(), &community_moderator_form).await?;
+ CommunityModerator::leave(data.pool(), &community_moderator_form).await?;
}
}
// Add new mods to database which have been added to moderators collection
for mod_id in apub.ordered_items {
- let mod_id = ObjectId::new(mod_id);
- let mod_user: ApubPerson = mod_id
- .dereference(&data.1, local_instance(&data.1).await, request_counter)
- .await?;
+ let mod_user: ApubPerson = mod_id.dereference(data).await?;
if !current_moderators
.iter()
.any(|x| x == mod_user.actor_id)
{
let community_moderator_form = CommunityModeratorForm {
- community_id: data.0.id,
+ community_id: owner.id,
person_id: mod_user.id,
};
- CommunityModerator::join(data.1.pool(), &community_moderator_form).await?;
+ CommunityModerator::join(data.pool(), &community_moderator_form).await?;
}
}
// This return value is unused, so just set an empty vec
Ok(ApubCommunityModerators(Vec::new()))
}
-
- type DbType = ();
}
#[cfg(test)]
let json: GroupModerators =
file_to_json_object("assets/lemmy/collections/group_moderators.json").unwrap();
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
- let mut request_counter = 0;
- let community_context = CommunityContext(community, context.clone());
- ApubCommunityModerators::verify(&json, &url, &community_context, &mut request_counter)
+ ApubCommunityModerators::verify(&json, &url, &context)
.await
.unwrap();
- ApubCommunityModerators::from_apub(json, &community_context, &mut request_counter)
+ ApubCommunityModerators::from_json(json, &community, &context)
.await
.unwrap();
- assert_eq!(request_counter, 0);
+ assert_eq!(context.request_count(), 0);
let current_moderators = CommunityModeratorView::for_community(context.pool(), community_id)
.await
Person::delete(context.pool(), old_mod.id).await.unwrap();
Person::delete(context.pool(), new_mod.id).await.unwrap();
- Community::delete(context.pool(), community_context.0.id)
+ Community::delete(context.pool(), community.id)
.await
.unwrap();
Site::delete(context.pool(), site.id).await.unwrap();
use crate::{
activity_lists::AnnouncableActivities,
- collections::CommunityContext,
- objects::post::ApubPost,
+ objects::{community::ApubCommunity, post::ApubPost},
protocol::{
activities::{
community::announce::AnnounceActivity,
},
};
use activitypub_federation::{
- data::Data,
- traits::{ActivityHandler, ApubObject},
- utils::verify_domains_match,
+ config::Data,
+ kinds::collection::OrderedCollectionType,
+ protocol::verification::verify_domains_match,
+ traits::{ActivityHandler, Collection},
};
-use activitystreams_kinds::collection::OrderedCollectionType;
-use chrono::NaiveDateTime;
use futures::future::join_all;
-use lemmy_api_common::utils::generate_outbox_url;
+use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url};
use lemmy_db_schema::{
source::{person::Person, post::Post},
traits::Crud,
#[derive(Clone, Debug)]
pub(crate) struct ApubCommunityOutbox(Vec<ApubPost>);
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubCommunityOutbox {
- type DataType = CommunityContext;
- type ApubType = GroupOutbox;
+#[async_trait::async_trait]
+impl Collection for ApubCommunityOutbox {
+ type Owner = ApubCommunity;
+ type DataType = LemmyContext;
+ type Kind = GroupOutbox;
type Error = LemmyError;
- type DbType = ();
-
- fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
- None
- }
-
- #[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
- _object_id: Url,
- data: &Self::DataType,
- ) -> Result<Option<Self>, LemmyError> {
- // Only read from database if its a local community, otherwise fetch over http
- if data.0.local {
- let community_id = data.0.id;
- let post_list: Vec<ApubPost> = Post::list_for_community(data.1.pool(), community_id)
- .await?
- .into_iter()
- .map(Into::into)
- .collect();
- Ok(Some(ApubCommunityOutbox(post_list)))
- } else {
- Ok(None)
- }
- }
#[tracing::instrument(skip_all)]
- async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
+ async fn read_local(
+ owner: &Self::Owner,
+ data: &Data<Self::DataType>,
+ ) -> Result<Self::Kind, LemmyError> {
+ let post_list: Vec<ApubPost> = Post::list_for_community(data.pool(), owner.id)
+ .await?
+ .into_iter()
+ .map(Into::into)
+ .collect();
let mut ordered_items = vec![];
- for post in self.0 {
- let person = Person::read(data.1.pool(), post.creator_id).await?.into();
+ for post in post_list {
+ let person = Person::read(data.pool(), post.creator_id).await?.into();
let create =
- CreateOrUpdatePage::new(post, &person, &data.0, CreateOrUpdateType::Create, &data.1)
- .await?;
+ CreateOrUpdatePage::new(post, &person, owner, CreateOrUpdateType::Create, data).await?;
let announcable = AnnouncableActivities::CreateOrUpdatePost(create);
- let announce = AnnounceActivity::new(announcable.try_into()?, &data.0, &data.1)?;
+ let announce = AnnounceActivity::new(announcable.try_into()?, owner, data)?;
ordered_items.push(announce);
}
Ok(GroupOutbox {
r#type: OrderedCollectionType::OrderedCollection,
- id: generate_outbox_url(&data.0.actor_id)?.into(),
+ id: generate_outbox_url(&owner.actor_id)?.into(),
total_items: ordered_items.len() as i32,
ordered_items,
})
async fn verify(
group_outbox: &GroupOutbox,
expected_domain: &Url,
- _context: &CommunityContext,
- _request_counter: &mut i32,
+ _data: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
verify_domains_match(expected_domain, &group_outbox.id)?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- apub: Self::ApubType,
- data: &Self::DataType,
- _request_counter: &mut i32,
+ async fn from_json(
+ apub: Self::Kind,
+ _owner: &Self::Owner,
+ data: &Data<Self::DataType>,
) -> Result<Self, LemmyError> {
let mut outbox_activities = apub.ordered_items;
if outbox_activities.len() as i64 > FETCH_LIMIT_MAX {
// We intentionally ignore errors here. This is because the outbox might contain posts from old
// Lemmy versions, or from other software which we cant parse. In that case, we simply skip the
// item and only parse the ones that work.
- let data = Data::new(data.1.clone());
// process items in parallel, to avoid long delay from fetch_site_metadata() and other processing
join_all(outbox_activities.into_iter().map(|activity| {
async {
// use separate request counter for each item, otherwise there will be problems with
// parallel processing
- let request_counter = &mut 0;
- let verify = activity.verify(&data, request_counter).await;
+ let verify = activity.verify(data).await;
if verify.is_ok() {
- activity.receive(&data, request_counter).await.ok();
+ activity.receive(data).await.ok();
}
}
}))
-use crate::objects::community::ApubCommunity;
-use lemmy_api_common::context::LemmyContext;
-
pub(crate) mod community_featured;
pub(crate) mod community_moderators;
pub(crate) mod community_outbox;
-
-/// Put community in the data, so we dont have to read it again from the database.
-pub(crate) struct CommunityContext(pub ApubCommunity, pub LemmyContext);
-use crate::{fetcher::webfinger::webfinger_resolve_actor, ActorType};
-use activitypub_federation::traits::ApubObject;
+use activitypub_federation::{
+ config::Data,
+ fetch::webfinger::webfinger_resolve_actor,
+ traits::{Actor, Object},
+};
+use diesel::NotFound;
use itertools::Itertools;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::traits::ApubActor;
+use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
pub mod post_or_comment;
pub mod search;
pub mod user_or_community;
-pub mod webfinger;
-/// Resolve actor identifier (eg `!news@example.com`) from local database to avoid network requests.
-/// This only works for local actors, and remote actors which were previously fetched (so it doesnt
-/// trigger any new fetch).
+/// Resolve actor identifier like `!news@example.com` to user or community object.
+///
+/// In case the requesting user is logged in and the object was not found locally, it is attempted
+/// to fetch via webfinger from the original instance.
#[tracing::instrument(skip_all)]
-pub async fn resolve_actor_identifier<Actor, DbActor>(
+pub async fn resolve_actor_identifier<ActorType, DbActor>(
identifier: &str,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
+ local_user_view: &Option<LocalUserView>,
include_deleted: bool,
-) -> Result<DbActor, LemmyError>
+) -> Result<ActorType, LemmyError>
where
- Actor: ApubObject<DataType = LemmyContext, Error = LemmyError>
- + ApubObject<DbType = DbActor>
- + ActorType
+ ActorType: Object<DataType = LemmyContext, Error = LemmyError>
+ + Object
+ + Actor
+ + From<DbActor>
+ Send
+ 'static,
- for<'de2> <Actor as ApubObject>::ApubType: serde::Deserialize<'de2>,
+ for<'de2> <ActorType as Object>::Kind: serde::Deserialize<'de2>,
DbActor: ApubActor + Send + 'static,
{
// remote actor
let domain = format!("{}://{}", context.settings().get_protocol_string(), domain);
let actor = DbActor::read_from_name_and_domain(context.pool(), &name, &domain).await;
if actor.is_ok() {
- Ok(actor?)
- } else {
+ Ok(actor?.into())
+ } else if local_user_view.is_some() {
// Fetch the actor from its home instance using webfinger
- let id = webfinger_resolve_actor::<Actor>(identifier, true, context, &mut 0).await?;
- let actor: DbActor = DbActor::read_from_apub_id(context.pool(), &id)
- .await?
- .expect("actor exists as we fetched just before");
+ let actor: ActorType = webfinger_resolve_actor(identifier, context).await?;
Ok(actor)
+ } else {
+ Err(NotFound.into())
}
}
// local actor
else {
let identifier = identifier.to_string();
- Ok(DbActor::read_from_name(context.pool(), &identifier, include_deleted).await?)
+ Ok(
+ DbActor::read_from_name(context.pool(), &identifier, include_deleted)
+ .await?
+ .into(),
+ )
}
}
InCommunity,
},
};
-use activitypub_federation::traits::ApubObject;
+use activitypub_federation::{config::Data, traits::Object};
use chrono::NaiveDateTime;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
Note(Note),
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for PostOrComment {
+#[async_trait::async_trait]
+impl Object for PostOrComment {
type DataType = LemmyContext;
- type ApubType = PageOrNote;
- type DbType = ();
+ type Kind = PageOrNote;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- data: &Self::DataType,
+ data: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
- let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?;
+ let post = ApubPost::read_from_id(object_id.clone(), data).await?;
Ok(match post {
Some(o) => Some(PostOrComment::Post(o)),
- None => ApubComment::read_from_apub_id(object_id, data)
+ None => ApubComment::read_from_id(object_id, data)
.await?
.map(PostOrComment::Comment),
})
}
#[tracing::instrument(skip_all)]
- async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
+ async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
match self {
PostOrComment::Post(p) => p.delete(data).await,
PostOrComment::Comment(c) => c.delete(data).await,
}
}
- async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
+ async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
unimplemented!()
}
#[tracing::instrument(skip_all)]
async fn verify(
- apub: &Self::ApubType,
+ apub: &Self::Kind,
expected_domain: &Url,
- data: &Self::DataType,
- request_counter: &mut i32,
+ data: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
match apub {
- PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data, request_counter).await,
- PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data, request_counter).await,
+ PageOrNote::Page(a) => ApubPost::verify(a, expected_domain, data).await,
+ PageOrNote::Note(a) => ApubComment::verify(a, expected_domain, data).await,
}
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- apub: PageOrNote,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<Self, LemmyError> {
+ async fn from_json(apub: PageOrNote, context: &Data<LemmyContext>) -> Result<Self, LemmyError> {
Ok(match apub {
- PageOrNote::Page(p) => {
- PostOrComment::Post(ApubPost::from_apub(*p, context, request_counter).await?)
- }
- PageOrNote::Note(n) => {
- PostOrComment::Comment(ApubComment::from_apub(n, context, request_counter).await?)
- }
+ PageOrNote::Page(p) => PostOrComment::Post(ApubPost::from_json(*p, context).await?),
+ PageOrNote::Note(n) => PostOrComment::Comment(ApubComment::from_json(n, context).await?),
})
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for PostOrComment {
- async fn community(
- &self,
- context: &LemmyContext,
- _: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
let cid = match self {
PostOrComment::Post(p) => p.community_id,
PostOrComment::Comment(c) => Post::read(context.pool(), c.post_id).await?.community_id,
use crate::{
- fetcher::webfinger::webfinger_resolve_actor,
- local_instance,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
};
-use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
+use activitypub_federation::{
+ config::Data,
+ fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
+ traits::Object,
+};
use chrono::NaiveDateTime;
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
#[tracing::instrument(skip_all)]
pub(crate) async fn search_query_to_object_id(
query: &str,
- local_only: bool,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> Result<SearchableObjects, LemmyError> {
- let request_counter = &mut 0;
- let object_id = match Url::parse(query) {
- // its already an url, just go with it
- Ok(url) => ObjectId::new(url),
+ Ok(match Url::parse(query) {
+ Ok(url) => {
+ // its already an url, just go with it
+ ObjectId::from(url).dereference(context).await?
+ }
Err(_) => {
// not an url, try to resolve via webfinger
let mut chars = query.chars();
let kind = chars.next();
let identifier = chars.as_str();
- let id = match kind {
- Some('@') => {
- webfinger_resolve_actor::<ApubPerson>(identifier, local_only, context, request_counter)
- .await?
- }
- Some('!') => {
- webfinger_resolve_actor::<ApubCommunity>(identifier, local_only, context, request_counter)
- .await?
- }
+ match kind {
+ Some('@') => SearchableObjects::Person(
+ webfinger_resolve_actor::<LemmyContext, ApubPerson>(identifier, context).await?,
+ ),
+ Some('!') => SearchableObjects::Community(
+ webfinger_resolve_actor::<LemmyContext, ApubCommunity>(identifier, context).await?,
+ ),
_ => return Err(LemmyError::from_message("invalid query")),
- };
- ObjectId::new(id)
+ }
}
- };
- if local_only {
- object_id.dereference_local(context).await
- } else {
- object_id
- .dereference(context, local_instance(context).await, request_counter)
- .await
- }
+ })
}
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
#[derive(Deserialize)]
#[serde(untagged)]
-pub(crate) enum SearchableApubTypes {
+pub(crate) enum SearchableKinds {
Group(Group),
Person(Person),
Page(Page),
Note(Note),
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for SearchableObjects {
+#[async_trait::async_trait]
+impl Object for SearchableObjects {
type DataType = LemmyContext;
- type ApubType = SearchableApubTypes;
- type DbType = ();
+ type Kind = SearchableKinds;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
// we could skip this and always return an error, but then it would always fetch objects
// over http, and not be able to mark objects as deleted that were deleted by remote server.
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- context: &LemmyContext,
+ context: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
- let c = ApubCommunity::read_from_apub_id(object_id.clone(), context).await?;
+ let c = ApubCommunity::read_from_id(object_id.clone(), context).await?;
if let Some(c) = c {
return Ok(Some(SearchableObjects::Community(c)));
}
- let p = ApubPerson::read_from_apub_id(object_id.clone(), context).await?;
+ let p = ApubPerson::read_from_id(object_id.clone(), context).await?;
if let Some(p) = p {
return Ok(Some(SearchableObjects::Person(p)));
}
- let p = ApubPost::read_from_apub_id(object_id.clone(), context).await?;
+ let p = ApubPost::read_from_id(object_id.clone(), context).await?;
if let Some(p) = p {
return Ok(Some(SearchableObjects::Post(p)));
}
- let c = ApubComment::read_from_apub_id(object_id, context).await?;
+ let c = ApubComment::read_from_id(object_id, context).await?;
if let Some(c) = c {
return Ok(Some(SearchableObjects::Comment(c)));
}
}
#[tracing::instrument(skip_all)]
- async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
+ async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
match self {
SearchableObjects::Person(p) => p.delete(data).await,
SearchableObjects::Community(c) => c.delete(data).await,
}
}
- async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
+ async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
unimplemented!()
}
#[tracing::instrument(skip_all)]
async fn verify(
- apub: &Self::ApubType,
+ apub: &Self::Kind,
expected_domain: &Url,
- data: &Self::DataType,
- request_counter: &mut i32,
+ data: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
match apub {
- SearchableApubTypes::Group(a) => {
- ApubCommunity::verify(a, expected_domain, data, request_counter).await
- }
- SearchableApubTypes::Person(a) => {
- ApubPerson::verify(a, expected_domain, data, request_counter).await
- }
- SearchableApubTypes::Page(a) => {
- ApubPost::verify(a, expected_domain, data, request_counter).await
- }
- SearchableApubTypes::Note(a) => {
- ApubComment::verify(a, expected_domain, data, request_counter).await
- }
+ SearchableKinds::Group(a) => ApubCommunity::verify(a, expected_domain, data).await,
+ SearchableKinds::Person(a) => ApubPerson::verify(a, expected_domain, data).await,
+ SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await,
+ SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await,
}
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- apub: Self::ApubType,
- context: &LemmyContext,
- rc: &mut i32,
- ) -> Result<Self, LemmyError> {
- use SearchableApubTypes as SAT;
+ async fn from_json(apub: Self::Kind, context: &Data<LemmyContext>) -> Result<Self, LemmyError> {
+ use SearchableKinds as SAT;
use SearchableObjects as SO;
Ok(match apub {
- SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, rc).await?),
- SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, rc).await?),
- SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, rc).await?),
- SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, rc).await?),
+ SAT::Group(g) => SO::Community(ApubCommunity::from_json(g, context).await?),
+ SAT::Person(p) => SO::Person(ApubPerson::from_json(p, context).await?),
+ SAT::Page(p) => SO::Post(ApubPost::from_json(p, context).await?),
+ SAT::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?),
})
}
}
use crate::{
objects::{community::ApubCommunity, person::ApubPerson},
protocol::objects::{group::Group, person::Person},
- ActorType,
};
-use activitypub_federation::traits::{Actor, ApubObject};
+use activitypub_federation::{
+ config::Data,
+ traits::{Actor, Object},
+};
use chrono::NaiveDateTime;
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
Group,
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for UserOrCommunity {
+#[async_trait::async_trait]
+impl Object for UserOrCommunity {
type DataType = LemmyContext;
- type ApubType = PersonOrGroup;
- type DbType = ();
+ type Kind = PersonOrGroup;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- data: &Self::DataType,
+ data: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
- let person = ApubPerson::read_from_apub_id(object_id.clone(), data).await?;
+ let person = ApubPerson::read_from_id(object_id.clone(), data).await?;
Ok(match person {
Some(o) => Some(UserOrCommunity::User(o)),
- None => ApubCommunity::read_from_apub_id(object_id, data)
+ None => ApubCommunity::read_from_id(object_id, data)
.await?
.map(UserOrCommunity::Community),
})
}
#[tracing::instrument(skip_all)]
- async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
+ async fn delete(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
match self {
UserOrCommunity::User(p) => p.delete(data).await,
UserOrCommunity::Community(p) => p.delete(data).await,
}
}
- async fn into_apub(self, _data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
+ async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
unimplemented!()
}
#[tracing::instrument(skip_all)]
async fn verify(
- apub: &Self::ApubType,
+ apub: &Self::Kind,
expected_domain: &Url,
- data: &Self::DataType,
- request_counter: &mut i32,
+ data: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
match apub {
- PersonOrGroup::Person(a) => {
- ApubPerson::verify(a, expected_domain, data, request_counter).await
- }
- PersonOrGroup::Group(a) => {
- ApubCommunity::verify(a, expected_domain, data, request_counter).await
- }
+ PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await,
+ PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await,
}
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- apub: Self::ApubType,
- data: &Self::DataType,
- request_counter: &mut i32,
- ) -> Result<Self, LemmyError> {
+ async fn from_json(apub: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, LemmyError> {
Ok(match apub {
- PersonOrGroup::Person(p) => {
- UserOrCommunity::User(ApubPerson::from_apub(p, data, request_counter).await?)
- }
+ PersonOrGroup::Person(p) => UserOrCommunity::User(ApubPerson::from_json(p, data).await?),
PersonOrGroup::Group(p) => {
- UserOrCommunity::Community(ApubCommunity::from_apub(p, data, request_counter).await?)
+ UserOrCommunity::Community(ApubCommunity::from_json(p, data).await?)
}
})
}
}
impl Actor for UserOrCommunity {
- fn public_key(&self) -> &str {
+ fn id(&self) -> Url {
match self {
- UserOrCommunity::User(p) => p.public_key(),
- UserOrCommunity::Community(p) => p.public_key(),
+ UserOrCommunity::User(u) => u.id(),
+ UserOrCommunity::Community(c) => c.id(),
}
}
- fn inbox(&self) -> Url {
- unimplemented!()
- }
-}
-
-impl ActorType for UserOrCommunity {
- fn actor_id(&self) -> Url {
+ fn public_key_pem(&self) -> &str {
match self {
- UserOrCommunity::User(u) => u.actor_id(),
- UserOrCommunity::Community(c) => c.actor_id(),
+ UserOrCommunity::User(p) => p.public_key_pem(),
+ UserOrCommunity::Community(p) => p.public_key_pem(),
}
}
- fn private_key(&self) -> Option<String> {
+ fn private_key_pem(&self) -> Option<String> {
match self {
- UserOrCommunity::User(u) => u.private_key(),
- UserOrCommunity::Community(c) => c.private_key(),
+ UserOrCommunity::User(p) => p.private_key_pem(),
+ UserOrCommunity::Community(p) => p.private_key_pem(),
}
}
+
+ fn inbox(&self) -> Url {
+ unimplemented!()
+ }
}
+++ /dev/null
-use crate::{local_instance, ActorType, FEDERATION_HTTP_FETCH_LIMIT};
-use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
-use anyhow::anyhow;
-use itertools::Itertools;
-use lemmy_api_common::context::LemmyContext;
-use lemmy_db_schema::newtypes::DbUrl;
-use lemmy_utils::{error::LemmyError, WebfingerResponse};
-use tracing::debug;
-use url::Url;
-
-/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
-/// using webfinger.
-pub(crate) async fn webfinger_resolve_actor<Kind>(
- identifier: &str,
- local_only: bool,
- context: &LemmyContext,
- request_counter: &mut i32,
-) -> Result<DbUrl, LemmyError>
-where
- Kind: ApubObject<DataType = LemmyContext, Error = LemmyError> + ActorType + Send + 'static,
- for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
-{
- let protocol = context.settings().get_protocol_string();
- let (_, domain) = identifier
- .splitn(2, '@')
- .collect_tuple()
- .ok_or_else(|| LemmyError::from_message("Invalid webfinger query, missing domain"))?;
- let fetch_url = format!("{protocol}://{domain}/.well-known/webfinger?resource=acct:{identifier}");
- debug!("Fetching webfinger url: {}", &fetch_url);
-
- *request_counter += 1;
- if *request_counter > FEDERATION_HTTP_FETCH_LIMIT {
- return Err(LemmyError::from_message("Request retry limit reached"));
- }
-
- let response = context.client().get(&fetch_url).send().await?;
-
- let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?;
-
- let links: Vec<Url> = res
- .links
- .iter()
- .filter(|link| {
- if let Some(type_) = &link.kind {
- type_.starts_with("application/")
- } else {
- false
- }
- })
- .filter_map(|l| l.href.clone())
- .collect();
- for l in links {
- let object_id = ObjectId::<Kind>::new(l);
- let object = if local_only {
- object_id.dereference_local(context).await
- } else {
- object_id
- .dereference(context, local_instance(context).await, request_counter)
- .await
- };
- if object.is_ok() {
- return object.map(|o| o.actor_id().into());
- }
- }
- let err = anyhow!("Failed to resolve actor for {}", identifier);
- Err(LemmyError::from_error_message(err, "failed_to_resolve"))
-}
http::{create_apub_response, create_apub_tombstone_response, err_object_not_local},
objects::comment::ApubComment,
};
-use activitypub_federation::traits::ApubObject;
-use actix_web::{web, web::Path, HttpResponse};
+use activitypub_federation::{config::Data, traits::Object};
+use actix_web::{web::Path, HttpResponse};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud};
use lemmy_utils::error::LemmyError;
#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_comment(
info: Path<CommentQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let id = CommentId(info.comment_id.parse::<i32>()?);
let comment: ApubComment = Comment::read(context.pool(), id).await?.into();
}
if !comment.deleted && !comment.removed {
- Ok(create_apub_response(&comment.into_apub(&context).await?))
+ Ok(create_apub_response(&comment.into_json(&context).await?))
} else {
Ok(create_apub_tombstone_response(comment.ap_id.clone()))
}
community_featured::ApubCommunityFeatured,
community_moderators::ApubCommunityModerators,
community_outbox::ApubCommunityOutbox,
- CommunityContext,
},
- http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity},
- local_instance,
+ http::{create_apub_response, create_apub_tombstone_response},
objects::{community::ApubCommunity, person::ApubPerson},
protocol::collections::group_followers::GroupFollowers,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- deser::context::WithContext,
- traits::ApubObject,
-};
-use actix_web::{web, HttpRequest, HttpResponse};
-use lemmy_api_common::{
- context::LemmyContext,
- utils::{generate_featured_url, generate_outbox_url},
+ actix_web::inbox::receive_activity,
+ config::Data,
+ protocol::context::WithContext,
+ traits::{Collection, Object},
};
+use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
+use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{source::community::Community, traits::ApubActor};
use lemmy_utils::error::LemmyError;
use serde::Deserialize;
#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_community_http(
info: web::Path<CommunityQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let community: ApubCommunity =
Community::read_from_name(context.pool(), &info.community_name, true)
.into();
if !community.deleted && !community.removed {
- let apub = community.into_apub(&context).await?;
+ let apub = community.into_json(&context).await?;
Ok(create_apub_response(&apub))
} else {
#[tracing::instrument(skip_all)]
pub async fn community_inbox(
request: HttpRequest,
- payload: String,
- context: web::Data<LemmyContext>,
+ body: Bytes,
+ data: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- receive_lemmy_activity::<WithContext<GroupInboxActivities>, ApubPerson>(request, payload, context)
- .await
+ receive_activity::<WithContext<GroupInboxActivities>, ApubPerson, LemmyContext>(
+ request, body, &data,
+ )
+ .await
}
/// Returns an empty followers collection, only populating the size (for privacy).
pub(crate) async fn get_apub_community_followers(
info: web::Path<CommunityQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let community = Community::read_from_name(context.pool(), &info.community_name, false).await?;
let followers = GroupFollowers::new(community, &context).await?;
/// activites like votes or comments).
pub(crate) async fn get_apub_community_outbox(
info: web::Path<CommunityQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- let community = Community::read_from_name(context.pool(), &info.community_name, false).await?;
+ let community: ApubCommunity =
+ Community::read_from_name(context.pool(), &info.community_name, false)
+ .await?
+ .into();
if community.deleted || community.removed {
return Err(LemmyError::from_message("deleted"));
}
- let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
- let outbox_data = CommunityContext(community.into(), context.get_ref().clone());
- let outbox: ApubCommunityOutbox = id
- .dereference(&outbox_data, local_instance(&context).await, &mut 0)
- .await?;
- Ok(create_apub_response(&outbox.into_apub(&outbox_data).await?))
+ let outbox = ApubCommunityOutbox::read_local(&community, &context).await?;
+ Ok(create_apub_response(&outbox))
}
#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_community_moderators(
info: web::Path<CommunityQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let community: ApubCommunity =
Community::read_from_name(context.pool(), &info.community_name, false)
if community.deleted || community.removed {
return Err(LemmyError::from_message("deleted"));
}
- let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
- let outbox_data = CommunityContext(community, context.get_ref().clone());
- let moderators: ApubCommunityModerators = id
- .dereference(&outbox_data, local_instance(&context).await, &mut 0)
- .await?;
- Ok(create_apub_response(
- &moderators.into_apub(&outbox_data).await?,
- ))
+ let moderators = ApubCommunityModerators::read_local(&community, &context).await?;
+ Ok(create_apub_response(&moderators))
}
/// Returns collection of featured (stickied) posts.
pub(crate) async fn get_apub_community_featured(
info: web::Path<CommunityQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- let community = Community::read_from_name(context.pool(), &info.community_name, false).await?;
+ let community: ApubCommunity =
+ Community::read_from_name(context.pool(), &info.community_name, false)
+ .await?
+ .into();
if community.deleted || community.removed {
return Err(LemmyError::from_message("deleted"));
}
- let id = ObjectId::new(generate_featured_url(&community.actor_id)?);
- let data = CommunityContext(community.into(), context.get_ref().clone());
- let featured: ApubCommunityFeatured = id
- .dereference(&data, local_instance(&context).await, &mut 0)
- .await?;
- Ok(create_apub_response(&featured.into_apub(&data).await?))
+ let featured = ApubCommunityFeatured::read_local(&community, &context).await?;
+ Ok(create_apub_response(&featured))
}
use crate::{
activity_lists::SharedInboxActivities,
fetcher::user_or_community::UserOrCommunity,
- insert_activity,
- local_instance,
protocol::objects::tombstone::Tombstone,
CONTEXT,
};
use activitypub_federation::{
- core::inbox::receive_activity,
- data::Data,
- deser::context::WithContext,
- traits::{ActivityHandler, Actor, ApubObject},
- APUB_JSON_CONTENT_TYPE,
+ actix_web::inbox::receive_activity,
+ config::Data,
+ protocol::context::WithContext,
+ FEDERATION_CONTENT_TYPE,
};
-use actix_web::{web, HttpRequest, HttpResponse};
+use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
use http::StatusCode;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::activity::Activity;
use lemmy_utils::error::LemmyError;
-use once_cell::sync::OnceCell;
-use serde::{de::DeserializeOwned, Deserialize, Serialize};
-use serde_json::Value;
+use serde::{Deserialize, Serialize};
use std::ops::Deref;
-use tracing::{debug, log::info};
use url::Url;
mod comment;
pub async fn shared_inbox(
request: HttpRequest,
- payload: String,
- context: web::Data<LemmyContext>,
+ body: Bytes,
+ data: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- receive_lemmy_activity::<SharedInboxActivities, UserOrCommunity>(request, payload, context).await
-}
-
-pub async fn receive_lemmy_activity<Activity, ActorT>(
- request: HttpRequest,
- payload: String,
- context: web::Data<LemmyContext>,
-) -> Result<HttpResponse, LemmyError>
-where
- Activity: ActivityHandler<DataType = LemmyContext, Error = LemmyError>
- + DeserializeOwned
- + Send
- + 'static,
- ActorT: ApubObject<DataType = LemmyContext, Error = LemmyError> + Actor + Send + 'static,
- for<'de2> <ActorT as ApubObject>::ApubType: serde::Deserialize<'de2>,
-{
- static DATA: OnceCell<Data<LemmyContext>> = OnceCell::new();
- let activity_value: Value = serde_json::from_str(&payload)?;
- debug!("Parsing activity {}", payload);
- let activity: Activity = serde_json::from_value(activity_value.clone())?;
- // Log the activity, so we avoid receiving and parsing it twice.
- let insert = insert_activity(activity.id(), activity_value, false, true, context.pool()).await?;
- if !insert {
- debug!("Received duplicate activity {}", activity.id().to_string());
- return Ok(HttpResponse::BadRequest().finish());
- }
- info!("Received activity {}", payload);
-
- let data = DATA.get_or_init(|| Data::new(context.get_ref().clone()));
- receive_activity::<Activity, ActorT, LemmyContext>(
- request,
- activity,
- local_instance(&context).await,
- data,
- )
- .await
+ receive_activity::<SharedInboxActivities, UserOrCommunity, LemmyContext>(request, body, &data)
+ .await
}
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
T: Serialize,
{
HttpResponse::Ok()
- .content_type(APUB_JSON_CONTENT_TYPE)
+ .content_type(FEDERATION_CONTENT_TYPE)
.json(WithContext::new(data, CONTEXT.deref().clone()))
}
fn create_json_apub_response(data: serde_json::Value) -> HttpResponse {
HttpResponse::Ok()
- .content_type(APUB_JSON_CONTENT_TYPE)
+ .content_type(FEDERATION_CONTENT_TYPE)
.json(data)
}
fn create_apub_tombstone_response<T: Into<Url>>(id: T) -> HttpResponse {
let tombstone = Tombstone::new(id.into());
HttpResponse::Gone()
- .content_type(APUB_JSON_CONTENT_TYPE)
+ .content_type(FEDERATION_CONTENT_TYPE)
.status(StatusCode::GONE)
.json(WithContext::new(tombstone, CONTEXT.deref().clone()))
}
use crate::{
activity_lists::PersonInboxActivitiesWithAnnouncable,
fetcher::user_or_community::UserOrCommunity,
- http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity},
+ http::{create_apub_response, create_apub_tombstone_response},
objects::person::ApubPerson,
protocol::collections::empty_outbox::EmptyOutbox,
};
-use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
-use actix_web::{web, HttpRequest, HttpResponse};
+use activitypub_federation::{
+ actix_web::inbox::receive_activity,
+ config::Data,
+ protocol::context::WithContext,
+ traits::Object,
+};
+use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
use lemmy_api_common::{context::LemmyContext, utils::generate_outbox_url};
use lemmy_db_schema::{source::person::Person, traits::ApubActor};
use lemmy_utils::error::LemmyError;
#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_person_http(
info: web::Path<PersonQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let user_name = info.into_inner().user_name;
// TODO: this needs to be able to read deleted persons, so that it can send tombstones
.into();
if !person.deleted {
- let apub = person.into_apub(&context).await?;
+ let apub = person.into_json(&context).await?;
Ok(create_apub_response(&apub))
} else {
#[tracing::instrument(skip_all)]
pub async fn person_inbox(
request: HttpRequest,
- payload: String,
- context: web::Data<LemmyContext>,
+ body: Bytes,
+ data: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- receive_lemmy_activity::<WithContext<PersonInboxActivitiesWithAnnouncable>, UserOrCommunity>(
- request, payload, context,
+ receive_activity::<WithContext<PersonInboxActivitiesWithAnnouncable>, UserOrCommunity, LemmyContext>(
+ request, body, &data,
)
.await
}
#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_person_outbox(
info: web::Path<PersonQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let person = Person::read_from_name(context.pool(), &info.user_name, false).await?;
let outbox_id = generate_outbox_url(&person.actor_id)?.into();
http::{create_apub_response, create_apub_tombstone_response, err_object_not_local},
objects::post::ApubPost,
};
-use activitypub_federation::traits::ApubObject;
+use activitypub_federation::{config::Data, traits::Object};
use actix_web::{web, HttpResponse};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud};
#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_post(
info: web::Path<PostQuery>,
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let id = PostId(info.post_id.parse::<i32>()?);
let post: ApubPost = Post::read(context.pool(), id).await?.into();
}
if !post.deleted && !post.removed {
- Ok(create_apub_response(&post.into_apub(&context).await?))
+ Ok(create_apub_response(&post.into_json(&context).await?))
} else {
Ok(create_apub_tombstone_response(post.ap_id.clone()))
}
use crate::{
activity_lists::SiteInboxActivities,
- http::{create_apub_response, receive_lemmy_activity},
+ http::create_apub_response,
objects::{instance::ApubSite, person::ApubPerson},
protocol::collections::empty_outbox::EmptyOutbox,
};
-use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
-use actix_web::{web, HttpRequest, HttpResponse};
+use activitypub_federation::{
+ actix_web::inbox::receive_activity,
+ config::Data,
+ protocol::context::WithContext,
+ traits::Object,
+};
+use actix_web::{web::Bytes, HttpRequest, HttpResponse};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_views::structs::SiteView;
use lemmy_utils::error::LemmyError;
use url::Url;
pub(crate) async fn get_apub_site_http(
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let site: ApubSite = SiteView::read_local(context.pool()).await?.site.into();
- let apub = site.into_apub(&context).await?;
+ let apub = site.into_json(&context).await?;
Ok(create_apub_response(&apub))
}
#[tracing::instrument(skip_all)]
pub(crate) async fn get_apub_site_outbox(
- context: web::Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
let outbox_id = format!(
"{}/site_outbox",
#[tracing::instrument(skip_all)]
pub async fn get_apub_site_inbox(
request: HttpRequest,
- payload: String,
- context: web::Data<LemmyContext>,
+ body: Bytes,
+ data: Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- receive_lemmy_activity::<WithContext<SiteInboxActivities>, ApubPerson>(request, payload, context)
- .await
+ receive_activity::<WithContext<SiteInboxActivities>, ApubPerson, LemmyContext>(
+ request, body, &data,
+ )
+ .await
}
use crate::fetcher::post_or_comment::PostOrComment;
-use activitypub_federation::{
- core::signatures::PublicKey,
- traits::{Actor, ApubObject},
- InstanceSettings,
- LocalInstance,
- UrlVerifier,
-};
+use activitypub_federation::config::{Data, UrlVerifier};
use async_trait::async_trait;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
- source::{activity::Activity, instance::Instance, local_site::LocalSite},
+ source::{
+ activity::{Activity, ActivityInsertForm},
+ instance::Instance,
+ local_site::LocalSite,
+ },
+ traits::Crud,
utils::DbPool,
};
use lemmy_utils::{error::LemmyError, settings::structs::Settings};
use once_cell::sync::Lazy;
-use tokio::sync::OnceCell;
+use serde::Serialize;
use url::Url;
pub mod activities;
pub mod objects;
pub mod protocol;
-const FEDERATION_HTTP_FETCH_LIMIT: i32 = 25;
+pub const FEDERATION_HTTP_FETCH_LIMIT: u32 = 50;
static CONTEXT: Lazy<Vec<serde_json::Value>> = Lazy::new(|| {
serde_json::from_str(include_str!("../assets/lemmy/context.json")).expect("parse context")
});
-// TODO: store this in context? but its only used in this crate, no need to expose it elsewhere
-// TODO this singleton needs to be redone to account for live data.
-async fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
- static LOCAL_INSTANCE: OnceCell<LocalInstance> = OnceCell::const_new();
- LOCAL_INSTANCE
- .get_or_init(|| async {
- // Local site may be missing
- let local_site = &LocalSite::read(context.pool()).await;
- let worker_count = local_site
- .as_ref()
- .map(|l| l.federation_worker_count)
- .unwrap_or(64) as u64;
-
- let settings = InstanceSettings::builder()
- .http_fetch_retry_limit(FEDERATION_HTTP_FETCH_LIMIT)
- .worker_count(worker_count)
- .debug(cfg!(debug_assertions))
- .http_signature_compat(true)
- .url_verifier(Box::new(VerifyUrlData(context.clone())))
- .build()
- .expect("configure federation");
- LocalInstance::new(
- context.settings().hostname.clone(),
- context.client().clone(),
- settings,
- )
- })
- .await
-}
-
#[derive(Clone)]
-struct VerifyUrlData(LemmyContext);
+pub struct VerifyUrlData(pub DbPool);
#[async_trait]
impl UrlVerifier for VerifyUrlData {
async fn verify(&self, url: &Url) -> Result<(), &'static str> {
- let local_site_data = fetch_local_site_data(self.0.pool())
+ let local_site_data = fetch_local_site_data(&self.0)
.await
.expect("read local site data");
- check_apub_id_valid(url, &local_site_data, self.0.settings())
+ check_apub_id_valid(url, &local_site_data)?;
+ Ok(())
}
}
///
/// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
/// post/comment in a local community.
-#[tracing::instrument(skip(settings, local_site_data))]
-fn check_apub_id_valid(
- apub_id: &Url,
- local_site_data: &LocalSiteData,
- settings: &Settings,
-) -> Result<(), &'static str> {
+#[tracing::instrument(skip(local_site_data))]
+fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> Result<(), &'static str> {
let domain = apub_id.domain().expect("apud id has domain").to_string();
- let local_instance = settings
- .get_hostname_without_port()
- .expect("local hostname is valid");
- if domain == local_instance {
- return Ok(());
- }
if !local_site_data
.local_site
return Err("Federation disabled");
}
- if apub_id.scheme() != settings.get_protocol_string() {
- return Err("Invalid protocol scheme");
- }
-
if let Some(blocked) = local_site_data.blocked_instances.as_ref() {
if blocked.iter().any(|i| domain.eq(&i.domain)) {
return Err("Domain is blocked");
local_site_data: &LocalSiteData,
settings: &Settings,
) -> Result<(), LemmyError> {
- check_apub_id_valid(apub_id, local_site_data, settings).map_err(LemmyError::from_message)?;
let domain = apub_id.domain().expect("apud id has domain").to_string();
let local_instance = settings
.get_hostname_without_port()
if domain == local_instance {
return Ok(());
}
+ check_apub_id_valid(apub_id, local_site_data).map_err(LemmyError::from_message)?;
if let Some(allowed) = local_site_data.allowed_instances.as_ref() {
// Only check allowlist if this is a community
.iter()
.map(|i| i.domain.clone())
.collect::<Vec<String>>();
+ let local_instance = settings
+ .get_hostname_without_port()
+ .expect("local hostname is valid");
allowed_and_local.push(local_instance);
+ let domain = apub_id.domain().expect("apud id has domain").to_string();
if !allowed_and_local.contains(&domain) {
return Err(LemmyError::from_message(
"Federation forbidden by strict allowlist",
Ok(())
}
-/// Store a sent or received activity in the database, for logging purposes. These records are not
-/// persistent.
-#[tracing::instrument(skip(pool))]
-async fn insert_activity(
+/// Store a sent or received activity in the database.
+///
+/// Stored activities are served over the HTTP endpoint `GET /activities/{type_}/{id}`. This also
+/// ensures that the same activity cannot be received more than once.
+#[tracing::instrument(skip(data, activity))]
+async fn insert_activity<T>(
ap_id: &Url,
- activity: serde_json::Value,
+ activity: &T,
local: bool,
sensitive: bool,
- pool: &DbPool,
-) -> Result<bool, LemmyError> {
+ data: &Data<LemmyContext>,
+) -> Result<(), LemmyError>
+where
+ T: Serialize,
+{
let ap_id = ap_id.clone().into();
- Ok(Activity::insert(pool, ap_id, activity, local, Some(sensitive)).await?)
-}
-
-/// Common methods provided by ActivityPub actors (community and person). Not all methods are
-/// implemented by all actors.
-pub trait ActorType: Actor + ApubObject {
- fn actor_id(&self) -> Url;
-
- fn private_key(&self) -> Option<String>;
-
- fn get_public_key(&self) -> PublicKey {
- PublicKey::new_main_key(self.actor_id(), self.public_key().to_string())
- }
+ let form = ActivityInsertForm {
+ ap_id,
+ data: serde_json::to_value(activity)?,
+ local: Some(local),
+ sensitive: Some(sensitive),
+ updated: None,
+ };
+ Activity::create(data.pool(), &form).await?;
+ Ok(())
}
-#[async_trait::async_trait(?Send)]
-pub trait SendActivity {
- type Response;
+#[async_trait::async_trait]
+pub trait SendActivity: Sync {
+ type Response: Sync + Send;
async fn send_activity(
_request: &Self,
_response: &Self::Response,
- _context: &LemmyContext,
+ _context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
Ok(())
}
-use crate::{
- fetcher::webfinger::webfinger_resolve_actor,
- objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
- ActorType,
+use crate::objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson};
+use activitypub_federation::{
+ config::Data,
+ fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
+ kinds::link::MentionType,
+ traits::Actor,
};
-use activitypub_federation::core::object_id::ObjectId;
-use activitystreams_kinds::link::MentionType;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
source::{comment::Comment, person::Person, post::Post},
pub async fn collect_non_local_mentions(
comment: &ApubComment,
community_id: ObjectId<ApubCommunity>,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<MentionsAndAddresses, LemmyError> {
let parent_creator = get_comment_parent_creator(context.pool(), comment).await?;
- let mut addressed_ccs: Vec<Url> = vec![community_id.into(), parent_creator.actor_id()];
+ let mut addressed_ccs: Vec<Url> = vec![community_id.into(), parent_creator.id()];
// Add the mention tag
let parent_creator_tag = Mention {
name: Some(format!(
"@{}@{}",
&parent_creator.name,
- &parent_creator.actor_id().domain().expect("has domain")
+ &parent_creator.id().domain().expect("has domain")
)),
kind: MentionType::Mention,
};
for mention in &mentions {
let identifier = format!("{}@{}", mention.name, mention.domain);
- let actor_id =
- webfinger_resolve_actor::<ApubPerson>(&identifier, true, context, request_counter).await;
- if let Ok(actor_id) = actor_id {
- let actor_id: ObjectId<ApubPerson> = ObjectId::new(actor_id);
- addressed_ccs.push(actor_id.to_string().parse()?);
+ let person = webfinger_resolve_actor::<LemmyContext, ApubPerson>(&identifier, context).await;
+ if let Ok(person) = person {
+ addressed_ccs.push(person.actor_id.to_string().parse()?);
let mention_tag = Mention {
- href: actor_id.into(),
+ href: person.id(),
name: Some(mention.full_name()),
kind: MentionType::Mention,
};
activities::{verify_is_public, verify_person_in_community},
check_apub_id_valid_with_strictness,
fetch_local_site_data,
- local_instance,
mentions::collect_non_local_mentions,
objects::{read_from_string_or_source, verify_is_remote_object},
protocol::{
InCommunity,
Source,
},
- PostOrComment,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- deser::values::MediaTypeMarkdownOrHtml,
- traits::ApubObject,
- utils::verify_domains_match,
+ config::Data,
+ kinds::{object::NoteType, public},
+ protocol::{values::MediaTypeMarkdownOrHtml, verification::verify_domains_match},
+ traits::Object,
};
-use activitystreams_kinds::{object::NoteType, public};
use chrono::NaiveDateTime;
use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
use lemmy_db_schema::{
}
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubComment {
+#[async_trait::async_trait]
+impl Object for ApubComment {
type DataType = LemmyContext;
- type ApubType = Note;
- type DbType = Comment;
+ type Kind = Note;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- context: &LemmyContext,
+ context: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
Ok(
Comment::read_from_apub_id(context.pool(), object_id)
}
#[tracing::instrument(skip_all)]
- async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
if !self.deleted {
let form = CommentUpdateForm::builder().deleted(Some(true)).build();
Comment::update(context.pool(), self.id, &form).await?;
}
#[tracing::instrument(skip_all)]
- async fn into_apub(self, context: &LemmyContext) -> Result<Note, LemmyError> {
+ async fn into_json(self, context: &Data<Self::DataType>) -> Result<Note, LemmyError> {
let creator_id = self.creator_id;
let creator = Person::read(context.pool(), creator_id).await?;
let in_reply_to = if let Some(comment_id) = self.parent_comment_id() {
let parent_comment = Comment::read(context.pool(), comment_id).await?;
- ObjectId::<PostOrComment>::new(parent_comment.ap_id)
+ parent_comment.ap_id.into()
} else {
- ObjectId::<PostOrComment>::new(post.ap_id)
+ post.ap_id.into()
};
let language = LanguageTag::new_single(self.language_id, context.pool()).await?;
- let maa = collect_non_local_mentions(
- &self,
- ObjectId::new(community.actor_id.clone()),
- context,
- &mut 0,
- )
- .await?;
+ let maa = collect_non_local_mentions(&self, community.actor_id.clone().into(), context).await?;
let note = Note {
r#type: NoteType::Note,
- id: ObjectId::new(self.ap_id.clone()),
- attributed_to: ObjectId::new(creator.actor_id),
+ id: self.ap_id.clone().into(),
+ attributed_to: creator.actor_id.into(),
to: vec![public()],
cc: maa.ccs,
content: markdown_to_html(&self.content),
tag: maa.tags,
distinguished: Some(self.distinguished),
language,
- audience: Some(ObjectId::new(community.actor_id)),
+ audience: Some(community.actor_id.into()),
};
Ok(note)
async fn verify(
note: &Note,
expected_domain: &Url,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
verify_domains_match(note.id.inner(), expected_domain)?;
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
verify_is_public(¬e.to, ¬e.cc)?;
- let community = note.community(context, request_counter).await?;
+ let community = note.community(context).await?;
let local_site_data = fetch_local_site_data(context.pool()).await?;
check_apub_id_valid_with_strictness(
context.settings(),
)?;
verify_is_remote_object(note.id.inner(), context.settings())?;
- verify_person_in_community(¬e.attributed_to, &community, context, request_counter).await?;
- let (post, _) = note.get_parents(context, request_counter).await?;
+ verify_person_in_community(¬e.attributed_to, &community, context).await?;
+ let (post, _) = note.get_parents(context).await?;
if post.locked {
return Err(LemmyError::from_message("Post is locked"));
}
///
/// If the parent community, post and comment(s) are not known locally, these are also fetched.
#[tracing::instrument(skip_all)]
- async fn from_apub(
- note: Note,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubComment, LemmyError> {
- let creator = note
- .attributed_to
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let (post, parent_comment) = note.get_parents(context, request_counter).await?;
+ async fn from_json(note: Note, context: &Data<LemmyContext>) -> Result<ApubComment, LemmyError> {
+ let creator = note.attributed_to.dereference(context).await?;
+ let (post, parent_comment) = note.get_parents(context).await?;
let content = read_from_string_or_source(¬e.content, ¬e.media_type, ¬e.source);
async fn prepare_comment_test(
url: &Url,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> (ApubPerson, ApubCommunity, ApubPost, ApubSite) {
- let (person, site) = parse_lemmy_person(context).await;
- let community = parse_lemmy_community(context).await;
+ // use separate counter so this doesnt affect tests
+ let context2 = context.reset_request_count();
+ let (person, site) = parse_lemmy_person(&context2).await;
+ let community = parse_lemmy_community(&context2).await;
let post_json = file_to_json_object("assets/lemmy/objects/page.json").unwrap();
- ApubPost::verify(&post_json, url, context, &mut 0)
- .await
- .unwrap();
- let post = ApubPost::from_apub(post_json, context, &mut 0)
- .await
- .unwrap();
+ ApubPost::verify(&post_json, url, &context2).await.unwrap();
+ let post = ApubPost::from_json(post_json, &context2).await.unwrap();
(person, community, post, site)
}
let data = prepare_comment_test(&url, &context).await;
let json: Note = file_to_json_object("assets/lemmy/objects/note.json").unwrap();
- let mut request_counter = 0;
- ApubComment::verify(&json, &url, &context, &mut request_counter)
- .await
- .unwrap();
- let comment = ApubComment::from_apub(json.clone(), &context, &mut request_counter)
+ ApubComment::verify(&json, &url, &context).await.unwrap();
+ let comment = ApubComment::from_json(json.clone(), &context)
.await
.unwrap();
assert_eq!(comment.ap_id, url.into());
assert_eq!(comment.content.len(), 14);
assert!(!comment.local);
- assert_eq!(request_counter, 0);
+ assert_eq!(context.request_count(), 0);
let comment_id = comment.id;
- let to_apub = comment.into_apub(&context).await.unwrap();
+ let to_apub = comment.into_json(&context).await.unwrap();
assert_json_include!(actual: json, expected: to_apub);
Comment::delete(context.pool(), comment_id).await.unwrap();
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")
.unwrap();
let person_json = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
- ApubPerson::verify(&person_json, &pleroma_url, &context, &mut 0)
- .await
- .unwrap();
- ApubPerson::from_apub(person_json, &context, &mut 0)
+ ApubPerson::verify(&person_json, &pleroma_url, &context)
.await
.unwrap();
+ ApubPerson::from_json(person_json, &context).await.unwrap();
let json = file_to_json_object("assets/pleroma/objects/note.json").unwrap();
- let mut request_counter = 0;
- ApubComment::verify(&json, &pleroma_url, &context, &mut request_counter)
- .await
- .unwrap();
- let comment = ApubComment::from_apub(json, &context, &mut request_counter)
+ ApubComment::verify(&json, &pleroma_url, &context)
.await
.unwrap();
+ let comment = ApubComment::from_json(json, &context).await.unwrap();
assert_eq!(comment.ap_id, pleroma_url.into());
assert_eq!(comment.content.len(), 64);
assert!(!comment.local);
- assert_eq!(request_counter, 0);
+ assert_eq!(context.request_count(), 1);
Comment::delete(context.pool(), comment.id).await.unwrap();
cleanup(data, &context).await;
use crate::{
check_apub_id_valid_with_strictness,
- collections::CommunityContext,
fetch_local_site_data,
- local_instance,
objects::instance::fetch_instance_actor_for_object,
protocol::{
objects::{group::Group, Endpoints, LanguageTag},
ImageObject,
Source,
},
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- traits::{Actor, ApubObject},
+ config::Data,
+ kinds::actor::GroupType,
+ traits::{Actor, Object},
};
-use activitystreams_kinds::actor::GroupType;
use chrono::NaiveDateTime;
use itertools::Itertools;
use lemmy_api_common::{
}
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubCommunity {
+#[async_trait::async_trait]
+impl Object for ApubCommunity {
type DataType = LemmyContext;
- type ApubType = Group;
- type DbType = Community;
+ type Kind = Group;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- context: &LemmyContext,
+ context: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
Ok(
Community::read_from_apub_id(context.pool(), &object_id.into())
}
#[tracing::instrument(skip_all)]
- async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
let form = CommunityUpdateForm::builder().deleted(Some(true)).build();
Community::update(context.pool(), self.id, &form).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn into_apub(self, data: &LemmyContext) -> Result<Group, LemmyError> {
+ async fn into_json(self, data: &Data<Self::DataType>) -> Result<Group, LemmyError> {
let community_id = self.id;
let langs = CommunityLanguage::read(data.pool(), community_id).await?;
let language = LanguageTag::new_multiple(langs, data.pool()).await?;
let group = Group {
kind: GroupType::Group,
- id: ObjectId::new(self.actor_id()),
+ id: self.id().into(),
preferred_username: self.name.clone(),
name: Some(self.title.clone()),
summary: self.description.as_ref().map(|b| markdown_to_html(b)),
moderators: Some(generate_moderators_url(&self.actor_id)?.into()),
featured: Some(generate_featured_url(&self.actor_id)?.into()),
inbox: self.inbox_url.clone().into(),
- outbox: ObjectId::new(generate_outbox_url(&self.actor_id)?),
+ outbox: generate_outbox_url(&self.actor_id)?.into(),
followers: self.followers_url.clone().into(),
endpoints: self.shared_inbox_url.clone().map(|s| Endpoints {
shared_inbox: s.into(),
}),
- public_key: self.get_public_key(),
+ public_key: self.public_key(),
language,
published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
async fn verify(
group: &Group,
expected_domain: &Url,
- context: &LemmyContext,
- _request_counter: &mut i32,
+ context: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
group.verify(expected_domain, context).await
}
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
#[tracing::instrument(skip_all)]
- async fn from_apub(
+ async fn from_json(
group: Group,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<Self::DataType>,
) -> Result<ApubCommunity, LemmyError> {
- let instance_id = fetch_instance_actor_for_object(&group.id, context, request_counter).await?;
+ let instance_id = fetch_instance_actor_for_object(&group.id, context).await?;
let form = Group::into_insert_form(group.clone(), instance_id);
let languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?;
CommunityLanguage::update(context.pool(), languages, community.id).await?;
let community: ApubCommunity = community.into();
- let outbox_data = CommunityContext(community.clone(), context.clone());
// Fetching mods and outbox is not necessary for Lemmy to work, so ignore errors. Besides,
// we need to ignore these errors so that tests can work entirely offline.
group
.outbox
- .dereference(&outbox_data, local_instance(context).await, request_counter)
+ .dereference(&community, context)
.await
.map_err(|e| debug!("{}", e))
.ok();
if let Some(moderators) = group.attributed_to.or(group.moderators) {
moderators
- .dereference(&outbox_data, local_instance(context).await, request_counter)
+ .dereference(&community, context)
.await
.map_err(|e| debug!("{}", e))
.ok();
}
impl Actor for ApubCommunity {
- fn public_key(&self) -> &str {
+ fn id(&self) -> Url {
+ self.actor_id.inner().clone()
+ }
+
+ fn public_key_pem(&self) -> &str {
&self.public_key
}
+ fn private_key_pem(&self) -> Option<String> {
+ self.private_key.clone()
+ }
+
fn inbox(&self) -> Url {
self.inbox_url.clone().into()
}
}
}
-impl ActorType for ApubCommunity {
- fn actor_id(&self) -> Url {
- self.actor_id.clone().into()
- }
- fn private_key(&self) -> Option<String> {
- self.private_key.clone()
- }
-}
-
impl ApubCommunity {
/// For a given community, returns the inboxes of all followers.
#[tracing::instrument(skip_all)]
objects::{instance::tests::parse_lemmy_instance, tests::init_context},
protocol::tests::file_to_json_object,
};
+ use activitypub_federation::fetch::collection_id::CollectionId;
use lemmy_db_schema::{source::site::Site, traits::Crud};
use serial_test::serial;
- pub(crate) async fn parse_lemmy_community(context: &LemmyContext) -> ApubCommunity {
+ pub(crate) async fn parse_lemmy_community(context: &Data<LemmyContext>) -> ApubCommunity {
+ // use separate counter so this doesnt affect tests
+ let context2 = context.reset_request_count();
let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json").unwrap();
// change these links so they dont fetch over the network
json.moderators = None;
json.attributed_to = None;
json.outbox =
- ObjectId::new(Url::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap());
+ CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap();
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
- let mut request_counter = 0;
- ApubCommunity::verify(&json, &url, context, &mut request_counter)
- .await
- .unwrap();
- let community = ApubCommunity::from_apub(json, context, &mut request_counter)
- .await
- .unwrap();
+ ApubCommunity::verify(&json, &url, &context2).await.unwrap();
+ let community = ApubCommunity::from_json(json, &context2).await.unwrap();
// this makes one requests to the (intentionally broken) outbox collection
- assert_eq!(request_counter, 1);
+ assert_eq!(context2.request_count(), 1);
community
}
use crate::{
check_apub_id_valid_with_strictness,
fetch_local_site_data,
- local_instance,
objects::read_from_string_or_source_opt,
protocol::{
objects::{instance::Instance, LanguageTag},
ImageObject,
Source,
},
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- deser::values::MediaTypeHtml,
- traits::{Actor, ApubObject},
- utils::verify_domains_match,
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::actor::ApplicationType,
+ protocol::{values::MediaTypeHtml, verification::verify_domains_match},
+ traits::{Actor, Object},
};
-use activitystreams_kinds::actor::ApplicationType;
use chrono::NaiveDateTime;
use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
use lemmy_db_schema::{
}
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubSite {
+#[async_trait::async_trait]
+impl Object for ApubSite {
type DataType = LemmyContext;
- type ApubType = Instance;
- type DbType = Site;
+ type Kind = Instance;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- data: &Self::DataType,
+ data: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
Ok(
Site::read_from_apub_id(data.pool(), object_id)
)
}
- async fn delete(self, _data: &Self::DataType) -> Result<(), LemmyError> {
+ async fn delete(self, _data: &Data<Self::DataType>) -> Result<(), LemmyError> {
unimplemented!()
}
#[tracing::instrument(skip_all)]
- async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
+ async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, LemmyError> {
let site_id = self.id;
let langs = SiteLanguage::read(data.pool(), site_id).await?;
let language = LanguageTag::new_multiple(langs, data.pool()).await?;
let instance = Instance {
kind: ApplicationType::Application,
- id: ObjectId::new(self.actor_id()),
+ id: self.id().into(),
name: self.name.clone(),
content: self.sidebar.as_ref().map(|d| markdown_to_html(d)),
source: self.sidebar.clone().map(Source::new),
image: self.banner.clone().map(ImageObject::new),
inbox: self.inbox_url.clone().into(),
outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?,
- public_key: self.get_public_key(),
+ public_key: self.public_key(),
language,
published: convert_datetime(self.published),
updated: self.updated.map(convert_datetime),
#[tracing::instrument(skip_all)]
async fn verify(
- apub: &Self::ApubType,
+ apub: &Self::Kind,
expected_domain: &Url,
- data: &Self::DataType,
- _request_counter: &mut i32,
+ data: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
let local_site_data = fetch_local_site_data(data.pool()).await?;
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- apub: Self::ApubType,
- data: &Self::DataType,
- _request_counter: &mut i32,
- ) -> Result<Self, LemmyError> {
+ async fn from_json(apub: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, LemmyError> {
let domain = apub.id.inner().domain().expect("group id has domain");
let instance = DbInstance::read_or_create(data.pool(), domain.to_string()).await?;
}
}
-impl ActorType for ApubSite {
- fn actor_id(&self) -> Url {
- self.actor_id.clone().into()
- }
- fn private_key(&self) -> Option<String> {
- self.private_key.clone()
+impl Actor for ApubSite {
+ fn id(&self) -> Url {
+ self.actor_id.inner().clone()
}
-}
-impl Actor for ApubSite {
- fn public_key(&self) -> &str {
+ fn public_key_pem(&self) -> &str {
&self.public_key
}
+ fn private_key_pem(&self) -> Option<String> {
+ self.private_key.clone()
+ }
+
fn inbox(&self) -> Url {
self.inbox_url.clone().into()
}
/// Try to fetch the instance actor (to make things like instance rules available).
pub(in crate::objects) async fn fetch_instance_actor_for_object<T: Into<Url> + Clone>(
object_id: &T,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<InstanceId, LemmyError> {
let object_id: Url = object_id.clone().into();
let instance_id = Site::instance_actor_id_from_url(object_id);
- let site = ObjectId::<ApubSite>::new(instance_id.clone())
- .dereference(context, local_instance(context).await, request_counter)
+ let site = ObjectId::<ApubSite>::from(instance_id.clone())
+ .dereference(context)
.await;
match site {
Ok(s) => Ok(s.instance_id),
use lemmy_db_schema::traits::Crud;
use serial_test::serial;
- pub(crate) async fn parse_lemmy_instance(context: &LemmyContext) -> ApubSite {
+ pub(crate) async fn parse_lemmy_instance(context: &Data<LemmyContext>) -> ApubSite {
let json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
let id = Url::parse("https://enterprise.lemmy.ml/").unwrap();
- let mut request_counter = 0;
- ApubSite::verify(&json, &id, context, &mut request_counter)
- .await
- .unwrap();
- let site = ApubSite::from_apub(json, context, &mut request_counter)
- .await
- .unwrap();
- assert_eq!(request_counter, 0);
+ ApubSite::verify(&json, &id, context).await.unwrap();
+ let site = ApubSite::from_json(json, context).await.unwrap();
+ assert_eq!(context.request_count(), 0);
site
}
use crate::protocol::Source;
-use activitypub_federation::deser::values::MediaTypeMarkdownOrHtml;
+use activitypub_federation::protocol::values::MediaTypeMarkdownOrHtml;
use anyhow::anyhow;
use html2md::parse_html;
use lemmy_utils::{error::LemmyError, settings::structs::Settings};
#[cfg(test)]
pub(crate) mod tests {
+ use activitypub_federation::config::{Data, FederationConfig};
use anyhow::anyhow;
use lemmy_api_common::{
context::LemmyContext,
}
// TODO: would be nice if we didnt have to use a full context for tests.
- pub(crate) async fn init_context() -> LemmyContext {
+ pub(crate) async fn init_context() -> Data<LemmyContext> {
async fn x() -> Result<String, LemmyError> {
Ok(String::new())
}
let rate_limit_cell = RateLimitCell::new(rate_limit_config).await;
let chat_server = Arc::new(ChatServer::startup());
- LemmyContext::create(
- pool,
- chat_server,
- client,
- settings,
- secret,
- rate_limit_cell.clone(),
- )
+ let context = LemmyContext::create(pool, chat_server, client, secret, rate_limit_cell.clone());
+ let config = FederationConfig::builder()
+ .domain("example.com")
+ .app_data(context)
+ .build()
+ .unwrap();
+ config.to_request_data()
}
}
ImageObject,
Source,
},
- ActorType,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- traits::{Actor, ApubObject},
- utils::verify_domains_match,
+ config::Data,
+ protocol::verification::verify_domains_match,
+ traits::{Actor, Object},
};
use chrono::NaiveDateTime;
use lemmy_api_common::{
}
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubPerson {
+#[async_trait::async_trait]
+impl Object for ApubPerson {
type DataType = LemmyContext;
- type ApubType = Person;
- type DbType = DbPerson;
+ type Kind = Person;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- context: &LemmyContext,
+ context: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
Ok(
DbPerson::read_from_apub_id(context.pool(), &object_id.into())
}
#[tracing::instrument(skip_all)]
- async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
let form = PersonUpdateForm::builder().deleted(Some(true)).build();
DbPerson::update(context.pool(), self.id, &form).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
- async fn into_apub(self, _pool: &LemmyContext) -> Result<Person, LemmyError> {
+ async fn into_json(self, _context: &Data<Self::DataType>) -> Result<Person, LemmyError> {
let kind = if self.bot_account {
UserTypes::Service
} else {
let person = Person {
kind,
- id: ObjectId::new(self.actor_id.clone()),
+ id: self.actor_id.clone().into(),
preferred_username: self.name.clone(),
name: self.display_name.clone(),
summary: self.bio.as_ref().map(|b| markdown_to_html(b)),
endpoints: self.shared_inbox_url.clone().map(|s| Endpoints {
shared_inbox: s.into(),
}),
- public_key: self.get_public_key(),
+ public_key: self.public_key(),
updated: self.updated.map(convert_datetime),
inbox: self.inbox_url.clone().into(),
};
async fn verify(
person: &Person,
expected_domain: &Url,
- context: &LemmyContext,
- _request_counter: &mut i32,
+ context: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
let local_site_data = fetch_local_site_data(context.pool()).await?;
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
+ async fn from_json(
person: Person,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<Self::DataType>,
) -> Result<ApubPerson, LemmyError> {
- let instance_id = fetch_instance_actor_for_object(&person.id, context, request_counter).await?;
+ let instance_id = fetch_instance_actor_for_object(&person.id, context).await?;
let person_form = PersonInsertForm {
name: person.preferred_username,
}
}
-impl ActorType for ApubPerson {
- fn actor_id(&self) -> Url {
- self.actor_id.clone().into()
+impl Actor for ApubPerson {
+ fn id(&self) -> Url {
+ self.actor_id.inner().clone()
}
- fn private_key(&self) -> Option<String> {
- self.private_key.clone()
+ fn public_key_pem(&self) -> &str {
+ &self.public_key
}
-}
-impl Actor for ApubPerson {
- fn public_key(&self) -> &str {
- &self.public_key
+ fn private_key_pem(&self) -> Option<String> {
+ self.private_key.clone()
}
fn inbox(&self) -> Url {
},
protocol::{objects::instance::Instance, tests::file_to_json_object},
};
+ use activitypub_federation::fetch::object_id::ObjectId;
use lemmy_db_schema::{source::site::Site, traits::Crud};
use serial_test::serial;
- pub(crate) async fn parse_lemmy_person(context: &LemmyContext) -> (ApubPerson, ApubSite) {
+ pub(crate) async fn parse_lemmy_person(context: &Data<LemmyContext>) -> (ApubPerson, ApubSite) {
let site = parse_lemmy_instance(context).await;
let json = file_to_json_object("assets/lemmy/objects/person.json").unwrap();
let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap();
- let mut request_counter = 0;
- ApubPerson::verify(&json, &url, context, &mut request_counter)
- .await
- .unwrap();
- let person = ApubPerson::from_apub(json, context, &mut request_counter)
- .await
- .unwrap();
- assert_eq!(request_counter, 0);
+ ApubPerson::verify(&json, &url, context).await.unwrap();
+ let person = ApubPerson::from_json(json, context).await.unwrap();
+ assert_eq!(context.request_count(), 0);
(person, site)
}
// create and parse a fake pleroma instance actor, to avoid network request during test
let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
- let id = Url::parse("https://queer.hacktivis.me/").unwrap();
- json.id = ObjectId::new(id);
- let mut request_counter = 0;
- let site = ApubSite::from_apub(json, &context, &mut request_counter)
- .await
- .unwrap();
+ json.id = ObjectId::parse("https://queer.hacktivis.me/").unwrap();
+ let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
+ ApubSite::verify(&json, &url, &context).await.unwrap();
+ let site = ApubSite::from_json(json, &context).await.unwrap();
let json = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
- let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
- let mut request_counter = 0;
- ApubPerson::verify(&json, &url, &context, &mut request_counter)
- .await
- .unwrap();
- let person = ApubPerson::from_apub(json, &context, &mut request_counter)
- .await
- .unwrap();
+ ApubPerson::verify(&json, &url, &context).await.unwrap();
+ let person = ApubPerson::from_json(json, &context).await.unwrap();
assert_eq!(person.actor_id, url.into());
assert_eq!(person.name, "lanodan");
assert!(!person.local);
- assert_eq!(request_counter, 0);
+ assert_eq!(context.request_count(), 0);
assert_eq!(person.bio.as_ref().unwrap().len(), 873);
cleanup((person, site), &context).await;
activities::{verify_is_public, verify_person_in_community},
check_apub_id_valid_with_strictness,
fetch_local_site_data,
- local_instance,
objects::{read_from_string_or_source_opt, verify_is_remote_object},
protocol::{
objects::{
},
};
use activitypub_federation::{
- core::object_id::ObjectId,
- deser::values::MediaTypeMarkdownOrHtml,
- traits::ApubObject,
- utils::verify_domains_match,
+ config::Data,
+ kinds::public,
+ protocol::{values::MediaTypeMarkdownOrHtml, verification::verify_domains_match},
+ traits::Object,
};
-use activitystreams_kinds::public;
use anyhow::anyhow;
use chrono::NaiveDateTime;
use html2md::parse_html;
}
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubPost {
+#[async_trait::async_trait]
+impl Object for ApubPost {
type DataType = LemmyContext;
- type ApubType = Page;
- type DbType = Post;
+ type Kind = Page;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- context: &LemmyContext,
+ context: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
Ok(
Post::read_from_apub_id(context.pool(), object_id)
}
#[tracing::instrument(skip_all)]
- async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
if !self.deleted {
let form = PostUpdateForm::builder().deleted(Some(true)).build();
Post::update(context.pool(), self.id, &form).await?;
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
#[tracing::instrument(skip_all)]
- async fn into_apub(self, context: &LemmyContext) -> Result<Page, LemmyError> {
+ async fn into_json(self, context: &Data<Self::DataType>) -> Result<Page, LemmyError> {
let creator_id = self.creator_id;
let creator = Person::read(context.pool(), creator_id).await?;
let community_id = self.community_id;
let page = Page {
kind: PageType::Page,
- id: ObjectId::new(self.ap_id.clone()),
- attributed_to: AttributedTo::Lemmy(ObjectId::new(creator.actor_id)),
+ id: self.ap_id.clone().into(),
+ attributed_to: AttributedTo::Lemmy(creator.actor_id.into()),
to: vec![community.actor_id.clone().into(), public()],
cc: vec![],
name: Some(self.name.clone()),
language,
published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
- audience: Some(ObjectId::new(community.actor_id)),
+ audience: Some(community.actor_id.into()),
in_reply_to: None,
};
Ok(page)
async fn verify(
page: &Page,
expected_domain: &Url,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
// We can't verify the domain in case of mod action, because the mod may be on a different
// instance from the post author.
let local_site_data = fetch_local_site_data(context.pool()).await?;
- let community = page.community(context, request_counter).await?;
+ let community = page.community(context).await?;
check_apub_id_valid_with_strictness(
page.id.inner(),
community.local,
&local_site_data,
context.settings(),
)?;
- verify_person_in_community(&page.creator()?, &community, context, request_counter).await?;
+ verify_person_in_community(&page.creator()?, &community, context).await?;
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
check_slurs_opt(&page.name, slur_regex)?;
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
- page: Page,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubPost, LemmyError> {
- let creator = page
- .creator()?
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let community = page.community(context, request_counter).await?;
+ async fn from_json(page: Page, context: &Data<Self::DataType>) -> Result<ApubPost, LemmyError> {
+ let creator = page.creator()?.dereference(context).await?;
+ let community = page.community(context).await?;
if community.posting_restricted_to_mods {
is_mod_or_admin(context.pool(), creator.id, community.id).await?;
}
.build()
};
// read existing, local post if any (for generating mod log)
- let old_post = ObjectId::<ApubPost>::new(page.id.clone())
- .dereference_local(context)
- .await;
+ let old_post = page.id.dereference_local(context).await;
let post = Post::create(context.pool(), &form).await?;
let json = file_to_json_object("assets/lemmy/objects/page.json").unwrap();
let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap();
- let mut request_counter = 0;
- ApubPost::verify(&json, &url, &context, &mut request_counter)
- .await
- .unwrap();
- let post = ApubPost::from_apub(json, &context, &mut request_counter)
- .await
- .unwrap();
+ ApubPost::verify(&json, &url, &context).await.unwrap();
+ let post = ApubPost::from_json(json, &context).await.unwrap();
assert_eq!(post.ap_id, url.into());
assert_eq!(post.name, "Post title");
assert_eq!(post.body.as_ref().unwrap().len(), 45);
assert!(!post.locked);
assert!(post.featured_community);
- assert_eq!(request_counter, 0);
+ assert_eq!(context.request_count(), 0);
Post::delete(context.pool(), post.id).await.unwrap();
Person::delete(context.pool(), person.id).await.unwrap();
use crate::{
check_apub_id_valid_with_strictness,
fetch_local_site_data,
- local_instance,
objects::read_from_string_or_source,
protocol::{
objects::chat_message::{ChatMessage, ChatMessageType},
},
};
use activitypub_federation::{
- core::object_id::ObjectId,
- deser::values::MediaTypeHtml,
- traits::ApubObject,
- utils::verify_domains_match,
+ config::Data,
+ protocol::{values::MediaTypeHtml, verification::verify_domains_match},
+ traits::Object,
};
use chrono::NaiveDateTime;
use lemmy_api_common::{context::LemmyContext, utils::check_person_block};
}
}
-#[async_trait::async_trait(?Send)]
-impl ApubObject for ApubPrivateMessage {
+#[async_trait::async_trait]
+impl Object for ApubPrivateMessage {
type DataType = LemmyContext;
- type ApubType = ChatMessage;
- type DbType = PrivateMessage;
+ type Kind = ChatMessage;
type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
}
#[tracing::instrument(skip_all)]
- async fn read_from_apub_id(
+ async fn read_from_id(
object_id: Url,
- context: &LemmyContext,
+ context: &Data<Self::DataType>,
) -> Result<Option<Self>, LemmyError> {
Ok(
PrivateMessage::read_from_apub_id(context.pool(), object_id)
)
}
- async fn delete(self, _context: &LemmyContext) -> Result<(), LemmyError> {
+ async fn delete(self, _context: &Data<Self::DataType>) -> Result<(), LemmyError> {
// do nothing, because pm can't be fetched over http
unimplemented!()
}
#[tracing::instrument(skip_all)]
- async fn into_apub(self, context: &LemmyContext) -> Result<ChatMessage, LemmyError> {
+ async fn into_json(self, context: &Data<Self::DataType>) -> Result<ChatMessage, LemmyError> {
let creator_id = self.creator_id;
let creator = Person::read(context.pool(), creator_id).await?;
let note = ChatMessage {
r#type: ChatMessageType::ChatMessage,
- id: ObjectId::new(self.ap_id.clone()),
- attributed_to: ObjectId::new(creator.actor_id),
- to: [ObjectId::new(recipient.actor_id)],
+ id: self.ap_id.clone().into(),
+ attributed_to: creator.actor_id.into(),
+ to: [recipient.actor_id.into()],
content: markdown_to_html(&self.content),
media_type: Some(MediaTypeHtml::Html),
source: Some(Source::new(self.content.clone())),
async fn verify(
note: &ChatMessage,
expected_domain: &Url,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<Self::DataType>,
) -> Result<(), LemmyError> {
verify_domains_match(note.id.inner(), expected_domain)?;
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
&local_site_data,
context.settings(),
)?;
- let person = note
- .attributed_to
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let person = note.attributed_to.dereference(context).await?;
if person.banned {
return Err(LemmyError::from_message("Person is banned from site"));
}
}
#[tracing::instrument(skip_all)]
- async fn from_apub(
+ async fn from_json(
note: ChatMessage,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<Self::DataType>,
) -> Result<ApubPrivateMessage, LemmyError> {
- let creator = note
- .attributed_to
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
- let recipient = note.to[0]
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ let creator = note.attributed_to.dereference(context).await?;
+ let recipient = note.to[0].dereference(context).await?;
check_person_block(creator.id, recipient.id, context.pool()).await?;
let form = PrivateMessageInsertForm {
async fn prepare_comment_test(
url: &Url,
- context: &LemmyContext,
+ context: &Data<LemmyContext>,
) -> (ApubPerson, ApubPerson, ApubSite) {
+ let context2 = context.reset_request_count();
let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json").unwrap();
- let site = parse_lemmy_instance(context).await;
- ApubPerson::verify(&lemmy_person, url, context, &mut 0)
+ let site = parse_lemmy_instance(&context2).await;
+ ApubPerson::verify(&lemmy_person, url, &context2)
.await
.unwrap();
- let person1 = ApubPerson::from_apub(lemmy_person, context, &mut 0)
+ let person1 = ApubPerson::from_json(lemmy_person, &context2)
.await
.unwrap();
let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
- ApubPerson::verify(&pleroma_person, &pleroma_url, context, &mut 0)
+ ApubPerson::verify(&pleroma_person, &pleroma_url, &context2)
.await
.unwrap();
- let person2 = ApubPerson::from_apub(pleroma_person, context, &mut 0)
+ let person2 = ApubPerson::from_json(pleroma_person, &context2)
.await
.unwrap();
(person1, person2, site)
}
- async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &LemmyContext) {
+ async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &Data<LemmyContext>) {
Person::delete(context.pool(), data.0.id).await.unwrap();
Person::delete(context.pool(), data.1.id).await.unwrap();
Site::delete(context.pool(), data.2.id).await.unwrap();
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap();
let data = prepare_comment_test(&url, &context).await;
let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json").unwrap();
- let mut request_counter = 0;
- ApubPrivateMessage::verify(&json, &url, &context, &mut request_counter)
+ ApubPrivateMessage::verify(&json, &url, &context)
.await
.unwrap();
- let pm = ApubPrivateMessage::from_apub(json.clone(), &context, &mut request_counter)
+ let pm = ApubPrivateMessage::from_json(json.clone(), &context)
.await
.unwrap();
assert_eq!(pm.ap_id.clone(), url.into());
assert_eq!(pm.content.len(), 20);
- assert_eq!(request_counter, 0);
+ assert_eq!(context.request_count(), 0);
let pm_id = pm.id;
- let to_apub = pm.into_apub(&context).await.unwrap();
+ let to_apub = pm.into_json(&context).await.unwrap();
assert_json_include!(actual: json, expected: to_apub);
PrivateMessage::delete(context.pool(), pm_id).await.unwrap();
let data = prepare_comment_test(&url, &context).await;
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap();
let json = file_to_json_object("assets/pleroma/objects/chat_message.json").unwrap();
- let mut request_counter = 0;
- ApubPrivateMessage::verify(&json, &pleroma_url, &context, &mut request_counter)
- .await
- .unwrap();
- let pm = ApubPrivateMessage::from_apub(json, &context, &mut request_counter)
+ ApubPrivateMessage::verify(&json, &pleroma_url, &context)
.await
.unwrap();
+ let pm = ApubPrivateMessage::from_json(json, &context).await.unwrap();
assert_eq!(pm.ap_id, pleroma_url.into());
assert_eq!(pm.content.len(), 3);
- assert_eq!(request_counter, 0);
+ assert_eq!(context.request_count(), 0);
PrivateMessage::delete(context.pool(), pm.id).await.unwrap();
cleanup(data, &context).await;
use crate::{
activities::{block::SiteOrCommunity, verify_community_matches},
- local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::InCommunity,
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::BlockType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::BlockType,
+ protocol::helpers::deserialize_one_or_many,
+};
use anyhow::anyhow;
use chrono::{DateTime, FixedOffset};
use lemmy_api_common::context::LemmyContext;
pub(crate) expires: Option<DateTime<FixedOffset>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for BlockUser {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let target = self
- .target
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let target = self.target.dereference(context).await?;
let community = match target {
SiteOrCommunity::Community(c) => c,
SiteOrCommunity::Site(_) => return Err(anyhow!("activity is not in community").into()),
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::block::block_user::BlockUser, InCommunity},
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::UndoType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::UndoType,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for UndoBlockUser {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let community = self.object.community(context, request_counter).await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let community = self.object.community(context).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
use crate::{objects::community::ApubCommunity, protocol::IdOrNestedObject};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::AnnounceType;
+use activitypub_federation::{
+ fetch::object_id::ObjectId,
+ kinds::activity::AnnounceType,
+ protocol::helpers::deserialize_one_or_many,
+};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use url::Url;
objects::{community::ApubCommunity, person::ApubPerson},
protocol::InCommunity,
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::AddType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::AddType,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::community::Community;
use lemmy_utils::error::LemmyError;
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for CollectionAdd {
- async fn community(
- &self,
- context: &LemmyContext,
- _request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
let (community, _) =
Community::get_by_collection_url(context.pool(), &self.clone().target.into()).await?;
if let Some(audience) = &self.audience {
objects::{community::ApubCommunity, person::ApubPerson},
protocol::InCommunity,
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::RemoveType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::RemoveType,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::community::Community;
use lemmy_utils::error::LemmyError;
pub(crate) actor: ObjectId<ApubPerson>,
#[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
- pub(crate) object: ObjectId<ApubPerson>,
+ pub(crate) object: Url,
#[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for CollectionRemove {
- async fn community(
- &self,
- context: &LemmyContext,
- _request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
let (community, _) =
Community::get_by_collection_url(context.pool(), &self.clone().target.into()).await?;
if let Some(audience) = &self.audience {
use crate::{
activities::verify_community_matches,
- local_instance,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::InCommunity,
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::UndoType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::UndoType,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{source::community::Community, traits::Crud};
use lemmy_utils::error::LemmyError;
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for LockPage {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let post = self
- .object
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let post = self.object.dereference(context).await?;
let community = Community::read(context.pool(), post.community_id).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for UndoLockPage {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let community = self.object.community(context, request_counter).await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let community = self.object.community(context).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
use crate::{
activities::verify_community_matches,
fetcher::post_or_comment::PostOrComment,
- local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::InCommunity,
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
-use activitystreams_kinds::activity::FlagType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::FlagType,
+ protocol::helpers::deserialize_one,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for Report {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let community = self.to[0]
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let community = self.to[0].dereference(context).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
use crate::{
activities::verify_community_matches,
- local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{objects::group::Group, InCommunity},
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::UpdateType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::UpdateType,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for UpdateCommunity {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let community: ApubCommunity = ObjectId::new(self.object.id.clone())
- .dereference(context, local_instance(context).await, request_counter)
- .await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let community: ApubCommunity = self.object.id.clone().dereference(context).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
objects::person::ApubPerson,
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage},
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
+use activitypub_federation::{fetch::object_id::ObjectId, protocol::helpers::deserialize_one};
use serde::{Deserialize, Serialize};
use url::Url;
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::CreateOrUpdateType, objects::note::Note, InCommunity},
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{source::community::Community, traits::Crud};
use lemmy_utils::error::LemmyError;
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for CreateOrUpdateNote {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let post = self.object.get_parents(context, request_counter).await?.0;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let post = self.object.get_parents(context).await?.0;
let community = Community::read(context.pool(), post.community_id).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::CreateOrUpdateType, objects::page::Page, InCommunity},
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for CreateOrUpdatePage {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let community = self.object.community(context, request_counter).await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let community = self.object.community(context).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{objects::tombstone::Tombstone, IdOrNestedObject, InCommunity},
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::DeleteType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::DeleteType,
+ protocol::helpers::deserialize_one_or_many,
+};
use anyhow::anyhow;
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
pub(crate) summary: Option<String>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for Delete {
- async fn community(
- &self,
- context: &LemmyContext,
- _request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? {
DeletableObjects::Community(c) => c.id,
DeletableObjects::Comment(c) => {
use crate::objects::person::ApubPerson;
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::DeleteType;
+use activitypub_federation::{
+ fetch::object_id::ObjectId,
+ kinds::activity::DeleteType,
+ protocol::helpers::deserialize_one_or_many,
+};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use url::Url;
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::deletion::delete::Delete, InCommunity},
};
-use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
-use activitystreams_kinds::activity::UndoType;
+use activitypub_federation::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::activity::UndoType,
+ protocol::helpers::deserialize_one_or_many,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
pub(crate) cc: Vec<Url>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for UndoDelete {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let community = self.object.community(context, request_counter).await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let community = self.object.community(context).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
use crate::{objects::community::ApubCommunity, protocol::activities::following::follow::Follow};
-use activitypub_federation::core::object_id::ObjectId;
-use activitystreams_kinds::activity::AcceptType;
+use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::AcceptType};
use serde::{Deserialize, Serialize};
use url::Url;
use crate::{fetcher::user_or_community::UserOrCommunity, objects::person::ApubPerson};
-use activitypub_federation::core::object_id::ObjectId;
-use activitystreams_kinds::activity::FollowType;
+use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::FollowType};
use serde::{Deserialize, Serialize};
use url::Url;
use crate::{objects::person::ApubPerson, protocol::activities::following::follow::Follow};
-use activitypub_federation::core::object_id::ObjectId;
-use activitystreams_kinds::activity::UndoType;
+use activitypub_federation::{fetch::object_id::ObjectId, kinds::activity::UndoType};
use serde::{Deserialize, Serialize};
use url::Url;
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::voting::vote::Vote, InCommunity},
};
-use activitypub_federation::core::object_id::ObjectId;
-use activitystreams_kinds::activity::UndoType;
+use activitypub_federation::{config::Data, fetch::object_id::ObjectId, kinds::activity::UndoType};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for UndoVote {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let community = self.object.community(context, request_counter).await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let community = self.object.community(context).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
}
use crate::{
activities::verify_community_matches,
fetcher::post_or_comment::PostOrComment,
- local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::InCommunity,
};
-use activitypub_federation::core::object_id::ObjectId;
+use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for Vote {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let local_instance = local_instance(context).await;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
let community = self
.object
- .dereference(context, local_instance, request_counter)
+ .dereference(context)
.await?
- .community(context, request_counter)
+ .community(context)
.await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
-use activitystreams_kinds::collection::OrderedCollectionType;
+use activitypub_federation::kinds::collection::OrderedCollectionType;
use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::protocol::objects::page::Page;
-use activitystreams_kinds::collection::OrderedCollectionType;
+use activitypub_federation::kinds::collection::OrderedCollectionType;
use serde::{Deserialize, Serialize};
use url::Url;
-use activitystreams_kinds::collection::CollectionType;
+use activitypub_federation::kinds::collection::CollectionType;
use lemmy_api_common::{context::LemmyContext, utils::generate_followers_url};
use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::structs::CommunityFollowerView;
use crate::objects::person::ApubPerson;
-use activitypub_federation::core::object_id::ObjectId;
-use activitystreams_kinds::collection::OrderedCollectionType;
+use activitypub_federation::{
+ fetch::object_id::ObjectId,
+ kinds::collection::OrderedCollectionType,
+};
use serde::{Deserialize, Serialize};
use url::Url;
use crate::protocol::activities::community::announce::AnnounceActivity;
-use activitystreams_kinds::collection::OrderedCollectionType;
+use activitypub_federation::kinds::collection::OrderedCollectionType;
use serde::{Deserialize, Serialize};
use url::Url;
-use crate::{local_instance, objects::community::ApubCommunity};
-use activitypub_federation::{deser::values::MediaTypeMarkdown, utils::fetch_object_http};
-use activitystreams_kinds::object::ImageType;
+use crate::objects::community::ApubCommunity;
+use activitypub_federation::{
+ config::Data,
+ fetch::fetch_object_http,
+ kinds::object::ImageType,
+ protocol::values::MediaTypeMarkdown,
+};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::newtypes::DbUrl;
use lemmy_utils::error::LemmyError;
NestedObject(Kind),
}
-impl<Kind: Id + DeserializeOwned> IdOrNestedObject<Kind> {
+impl<Kind: Id + DeserializeOwned + Send> IdOrNestedObject<Kind> {
pub(crate) fn id(&self) -> &Url {
match self {
IdOrNestedObject::Id(i) => i,
IdOrNestedObject::NestedObject(n) => n.object_id(),
}
}
- pub(crate) async fn object(
- self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<Kind, LemmyError> {
+ pub(crate) async fn object(self, context: &Data<LemmyContext>) -> Result<Kind, LemmyError> {
match self {
- IdOrNestedObject::Id(i) => {
- Ok(fetch_object_http(&i, local_instance(context).await, request_counter).await?)
- }
+ // TODO: move IdOrNestedObject struct to library and make fetch_object_http private
+ IdOrNestedObject::Id(i) => Ok(fetch_object_http(&i, context).await?),
IdOrNestedObject::NestedObject(o) => Ok(o),
}
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
pub trait InCommunity {
// TODO: after we use audience field and remove backwards compat, it should be possible to change
// this to simply `fn community(&self) -> Result<ObjectId<ApubCommunity>, LemmyError>`
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError>;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError>;
}
#[cfg(test)]
pub(crate) mod tests {
- use activitypub_federation::deser::context::WithContext;
+ use activitypub_federation::protocol::context::WithContext;
use assert_json_diff::assert_json_include;
use lemmy_utils::error::LemmyError;
use serde::{de::DeserializeOwned, Serialize};
protocol::Source,
};
use activitypub_federation::{
- core::object_id::ObjectId,
- deser::{
+ fetch::object_id::ObjectId,
+ protocol::{
helpers::{deserialize_one, deserialize_skip_error},
values::MediaTypeHtml,
},
},
};
use activitypub_federation::{
- core::{object_id::ObjectId, signatures::PublicKey},
- deser::helpers::deserialize_skip_error,
- utils::verify_domains_match,
+ fetch::{collection_id::CollectionId, object_id::ObjectId},
+ kinds::actor::GroupType,
+ protocol::{
+ helpers::deserialize_skip_error,
+ public_key::PublicKey,
+ verification::verify_domains_match,
+ },
};
-use activitystreams_kinds::actor::GroupType;
use chrono::{DateTime, FixedOffset};
use lemmy_api_common::{context::LemmyContext, utils::local_site_opt_to_slur_regex};
use lemmy_db_schema::{
// lemmy extension
pub(crate) sensitive: Option<bool>,
// deprecated, use attributed_to instead
- pub(crate) moderators: Option<ObjectId<ApubCommunityModerators>>,
+ pub(crate) moderators: Option<CollectionId<ApubCommunityModerators>>,
#[serde(deserialize_with = "deserialize_skip_error", default)]
- pub(crate) attributed_to: Option<ObjectId<ApubCommunityModerators>>,
+ pub(crate) attributed_to: Option<CollectionId<ApubCommunityModerators>>,
// lemmy extension
pub(crate) posting_restricted_to_mods: Option<bool>,
- pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
+ pub(crate) outbox: CollectionId<ApubCommunityOutbox>,
pub(crate) endpoints: Option<Endpoints>,
- pub(crate) featured: Option<ObjectId<ApubCommunityFeatured>>,
+ pub(crate) featured: Option<CollectionId<ApubCommunityFeatured>>,
#[serde(default)]
pub(crate) language: Vec<LanguageTag>,
pub(crate) published: Option<DateTime<FixedOffset>>,
protocol::{objects::LanguageTag, ImageObject, Source},
};
use activitypub_federation::{
- core::{object_id::ObjectId, signatures::PublicKey},
- deser::{helpers::deserialize_skip_error, values::MediaTypeHtml},
+ fetch::object_id::ObjectId,
+ kinds::actor::ApplicationType,
+ protocol::{helpers::deserialize_skip_error, public_key::PublicKey, values::MediaTypeHtml},
};
-use activitystreams_kinds::actor::ApplicationType;
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use crate::{
activities::verify_community_matches,
fetcher::post_or_comment::PostOrComment,
- local_instance,
mentions::MentionOrValue,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{objects::LanguageTag, InCommunity, Source},
};
use activitypub_federation::{
- core::object_id::ObjectId,
- deser::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::object::NoteType,
+ protocol::{
helpers::{deserialize_one_or_many, deserialize_skip_error},
values::MediaTypeMarkdownOrHtml,
},
};
-use activitystreams_kinds::object::NoteType;
use chrono::{DateTime, FixedOffset};
use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
impl Note {
pub(crate) async fn get_parents(
&self,
- context: &LemmyContext,
- request_counter: &mut i32,
+ context: &Data<LemmyContext>,
) -> Result<(ApubPost, Option<ApubComment>), LemmyError> {
// Fetch parent comment chain in a box, otherwise it can cause a stack overflow.
- let parent = Box::pin(
- self
- .in_reply_to
- .dereference(context, local_instance(context).await, request_counter)
- .await?,
- );
+ let parent = Box::pin(self.in_reply_to.dereference(context).await?);
match parent.deref() {
PostOrComment::Post(p) => Ok((p.clone(), None)),
PostOrComment::Comment(c) => {
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for Note {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let (post, _) = self.get_parents(context, request_counter).await?;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
+ let (post, _) = self.get_parents(context).await?;
let community = Community::read(context.pool(), post.community_id).await?;
if let Some(audience) = &self.audience {
verify_community_matches(audience, community.actor_id.clone())?;
use crate::{
activities::verify_community_matches,
fetcher::user_or_community::{PersonOrGroupType, UserOrCommunity},
- local_instance,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{objects::LanguageTag, ImageObject, InCommunity, Source},
};
use activitypub_federation::{
- core::object_id::ObjectId,
- data::Data,
- deser::{
+ config::Data,
+ fetch::object_id::ObjectId,
+ kinds::{
+ link::LinkType,
+ object::{DocumentType, ImageType},
+ },
+ protocol::{
helpers::{deserialize_one_or_many, deserialize_skip_error},
values::MediaTypeMarkdownOrHtml,
},
- traits::{ActivityHandler, ApubObject},
-};
-use activitystreams_kinds::{
- link::LinkType,
- object::{DocumentType, ImageType},
+ traits::{ActivityHandler, Object},
};
use chrono::{DateTime, FixedOffset};
use itertools::Itertools;
/// the current value, it is a mod action and needs to be verified as such.
///
/// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]].
- pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result<bool, LemmyError> {
- let old_post = ObjectId::<ApubPost>::new(self.id.clone())
- .dereference_local(context)
- .await;
+ pub(crate) async fn is_mod_action(
+ &self,
+ context: &Data<LemmyContext>,
+ ) -> Result<bool, LemmyError> {
+ let old_post = self.id.clone().dereference_local(context).await;
let featured_changed = Page::is_featured_changed(&old_post, &self.stickied);
let locked_changed = Page::is_locked_changed(&old_post, &self.comments_enabled);
AttributedTo::Peertube(p) => p
.iter()
.find(|a| a.kind == PersonOrGroupType::Person)
- .map(|a| ObjectId::<ApubPerson>::new(a.id.clone().into_inner()))
+ .map(|a| ObjectId::<ApubPerson>::from(a.id.clone().into_inner()))
.ok_or_else(|| LemmyError::from_message("page does not specify creator person")),
}
}
}
// Used for community outbox, so that it can be compatible with Pleroma/Mastodon.
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl ActivityHandler for Page {
type DataType = LemmyContext;
type Error = LemmyError;
fn actor(&self) -> &Url {
unimplemented!()
}
- async fn verify(
- &self,
- data: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- ApubPost::verify(self, self.id.inner(), data, request_counter).await
+ async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ ApubPost::verify(self, self.id.inner(), data).await
}
- async fn receive(
- self,
- data: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- ApubPost::from_apub(self, data, request_counter).await?;
+ async fn receive(self, data: &Data<Self::DataType>) -> Result<(), LemmyError> {
+ ApubPost::from_json(self, data).await?;
Ok(())
}
}
-#[async_trait::async_trait(?Send)]
+#[async_trait::async_trait]
impl InCommunity for Page {
- async fn community(
- &self,
- context: &LemmyContext,
- request_counter: &mut i32,
- ) -> Result<ApubCommunity, LemmyError> {
- let instance = local_instance(context).await;
+ async fn community(&self, context: &Data<LemmyContext>) -> Result<ApubCommunity, LemmyError> {
let community = match &self.attributed_to {
AttributedTo::Lemmy(_) => {
let mut iter = self.to.iter().merge(self.cc.iter());
loop {
if let Some(cid) = iter.next() {
- let cid = ObjectId::new(cid.clone());
- if let Ok(c) = cid.dereference(context, instance, request_counter).await {
+ let cid = ObjectId::from(cid.clone());
+ if let Ok(c) = cid.dereference(context).await {
break c;
}
} else {
AttributedTo::Peertube(p) => {
p.iter()
.find(|a| a.kind == PersonOrGroupType::Group)
- .map(|a| ObjectId::<ApubCommunity>::new(a.id.clone().into_inner()))
+ .map(|a| ObjectId::<ApubCommunity>::from(a.id.clone().into_inner()))
.ok_or_else(|| LemmyError::from_message("page does not specify group"))?
- .dereference(context, instance, request_counter)
+ .dereference(context)
.await?
}
};
protocol::{objects::Endpoints, ImageObject, Source},
};
use activitypub_federation::{
- core::{object_id::ObjectId, signatures::PublicKey},
- deser::helpers::deserialize_skip_error,
+ fetch::object_id::ObjectId,
+ protocol::{helpers::deserialize_skip_error, public_key::PublicKey},
};
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};
use crate::protocol::Id;
-use activitystreams_kinds::object::TombstoneType;
+use activitypub_federation::kinds::object::TombstoneType;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use url::Url;
traits::Crud,
utils::{get_conn, DbPool},
};
-use diesel::{
- dsl::insert_into,
- result::{DatabaseErrorKind, Error},
- ExpressionMethods,
- QueryDsl,
-};
+use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
-use serde_json::Value;
#[async_trait]
impl Crud for Activity {
}
impl Activity {
- /// Returns true if the insert was successful
- // TODO this should probably just be changed to an upsert on_conflict, rather than an error
- pub async fn insert(
- pool: &DbPool,
- ap_id_: DbUrl,
- data_: Value,
- local_: bool,
- sensitive_: Option<bool>,
- ) -> Result<bool, Error> {
- let activity_form = ActivityInsertForm {
- ap_id: ap_id_,
- data: data_,
- local: Some(local_),
- sensitive: sensitive_,
- updated: None,
- };
- match Activity::create(pool, &activity_form).await {
- Ok(_) => Ok(true),
- Err(e) => {
- if let Error::DatabaseError(DatabaseErrorKind::UniqueViolation, _) = e {
- return Ok(false);
- }
- Err(e)
- }
- }
- }
-
pub async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Activity, Error> {
let conn = &mut get_conn(pool).await?;
activity
#[cfg(feature = "full")]
-use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
+use activitypub_federation::{
+ fetch::collection_id::CollectionId,
+ fetch::object_id::ObjectId,
+ traits::Collection,
+ traits::Object,
+};
#[cfg(feature = "full")]
use diesel_ltree::Ltree;
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "full", diesel(sql_type = diesel::sql_types::Text))]
pub struct DbUrl(pub(crate) Box<Url>);
+impl DbUrl {
+ pub fn inner(&self) -> &Url {
+ &self.0
+ }
+}
+
#[cfg(feature = "full")]
#[derive(Serialize, Deserialize)]
#[serde(remote = "Ltree")]
*self.0
}
}
+
#[cfg(feature = "full")]
impl<T> From<DbUrl> for ObjectId<T>
where
- T: ApubObject + Send,
- for<'de2> <T as ApubObject>::ApubType: Deserialize<'de2>,
+ T: Object + Send + 'static,
+ for<'de2> <T as Object>::Kind: Deserialize<'de2>,
{
fn from(value: DbUrl) -> Self {
- ObjectId::new(value)
+ let url: Url = value.into();
+ ObjectId::from(url)
+ }
+}
+
+#[cfg(feature = "full")]
+impl<T> From<DbUrl> for CollectionId<T>
+where
+ T: Collection + Send + 'static,
+ for<'de2> <T as Collection>::Kind: Deserialize<'de2>,
+{
+ fn from(value: DbUrl) -> Self {
+ let url: Url = value.into();
+ CollectionId::from(url)
+ }
+}
+
+#[cfg(feature = "full")]
+impl<T> From<CollectionId<T>> for DbUrl
+where
+ T: Collection,
+ for<'de2> <T as Collection>::Kind: Deserialize<'de2>,
+{
+ fn from(value: CollectionId<T>) -> Self {
+ let url: Url = value.into();
+ url.into()
}
}
#[async_trait]
pub trait ApubActor {
- // TODO: this should be in a trait ApubObject (and implemented for Post, Comment, PrivateMessage as well)
async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Option<Self>, Error>
where
Self: Sized;
CommentSortType,
SortType,
};
-use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
+use activitypub_federation::{fetch::object_id::ObjectId, traits::Object};
use chrono::NaiveDateTime;
use deadpool::Runtime;
use diesel::{
impl<Kind> From<ObjectId<Kind>> for DbUrl
where
- Kind: ApubObject + Send + 'static,
- for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
+ Kind: Object + Send + 'static,
+ for<'de2> <Kind as Object>::Kind: serde::Deserialize<'de2>,
{
fn from(id: ObjectId<Kind>) -> Self {
DbUrl(Box::new(id.into()))
lemmy_db_views_actor = { workspace = true }
lemmy_db_schema = { workspace = true }
lemmy_api_common = { workspace = true, features = ["full"] }
+activitypub_federation = { workspace = true }
diesel = { workspace = true }
actix-web = { workspace = true }
anyhow = { workspace = true }
+use activitypub_federation::fetch::webfinger::{Webfinger, WebfingerLink};
use actix_web::{web, web::Query, HttpResponse};
use anyhow::Context;
use lemmy_api_common::context::LemmyContext;
source::{community::Community, person::Person},
traits::ApubActor,
};
-use lemmy_utils::{error::LemmyError, location_info, WebfingerLink, WebfingerResponse};
+use lemmy_utils::{error::LemmyError, location_info};
use serde::Deserialize;
use std::collections::HashMap;
use url::Url;
.flatten()
.collect();
- let json = WebfingerResponse {
+ let json = Webfinger {
subject: info.resource.clone(),
links,
+ ..Default::default()
};
Ok(HttpResponse::Ok().json(json))
if let Some(url) = url {
let mut properties = HashMap::new();
properties.insert(
- "https://www.w3.org/ns/activitystreams#type".to_string(),
+ "https://www.w3.org/ns/activitystreams#type"
+ .parse()
+ .expect("parse url"),
kind.to_string(),
);
vec![
rel: Some("http://webfinger.net/rel/profile-page".to_string()),
kind: Some("text/html".to_string()),
href: Some(url.clone()),
- properties: Default::default(),
+ ..Default::default()
},
WebfingerLink {
rel: Some("self".to_string()),
pub mod utils;
pub mod version;
-use serde::{Deserialize, Serialize};
-use std::{collections::HashMap, fmt, time::Duration};
-use url::Url;
+use std::{fmt, time::Duration};
pub type ConnectionId = usize;
}
}
-#[derive(Serialize, Deserialize, Debug)]
-pub struct WebfingerLink {
- pub rel: Option<String>,
- #[serde(rename = "type")]
- pub kind: Option<String>,
- pub href: Option<Url>,
- #[serde(default)]
- pub properties: HashMap<String, String>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct WebfingerResponse {
- pub subject: String,
- pub links: Vec<WebfingerLink>,
-}
-
#[macro_export]
macro_rules! location_info {
() => {
)
};
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use reqwest::Client;
-
- #[tokio::test]
- #[ignore]
- async fn test_webfinger() {
- let client = Client::default();
- let fetch_url =
- "https://kino.schuerz.at/.well-known/webfinger?resource=acct:h0e@kino.schuerz.at";
-
- let response = client.get(fetch_url).send().await.unwrap();
-
- let res = response.json::<WebfingerResponse>().await;
- assert!(res.is_ok());
- }
-}
COPY . .
# Build the project
-RUN echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/version.rs"
# Debug mode build
RUN --mount=type=cache,target=/app/target \
if [ "$RUST_RELEASE_MODE" = "debug" ] ; then \
- cargo build --target ${CARGO_BUILD_TARGET} \
+ echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/version.rs" \
+ && cargo build --target ${CARGO_BUILD_TARGET} \
&& cp ./target/$CARGO_BUILD_TARGET/$RUST_RELEASE_MODE/lemmy_server /app/lemmy_server; \
fi
#!/bin/sh
-
-# This script uses a docker file that builds with musl, and runs on linux alpine
-# Its a bit slower for development than the volume mount.
-
set -e
mkdir -p volumes/pictrs
async fn perform<'a, Data>(
data: Data,
context: web::Data<LemmyContext>,
+ apub_data: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: Perform
+ 'static,
{
let res = data.perform(&context, None).await?;
- SendActivity::send_activity(&data, &res, &context).await?;
+ SendActivity::send_activity(&data, &res, &apub_data).await?;
Ok(HttpResponse::Ok().json(res))
}
async fn route_get<'a, Data>(
data: web::Query<Data>,
context: web::Data<LemmyContext>,
+ apub_data: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: Perform
+ Send
+ 'static,
{
- perform::<Data>(data.0, context).await
+ perform::<Data>(data.0, context, apub_data).await
}
async fn route_get_apub<'a, Data>(
data: web::Query<Data>,
- context: web::Data<LemmyContext>,
+ context: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: PerformApub
async fn route_post<'a, Data>(
data: web::Json<Data>,
context: web::Data<LemmyContext>,
+ apub_data: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: Perform
+ Send
+ 'static,
{
- perform::<Data>(data.0, context).await
+ perform::<Data>(data.0, context, apub_data).await
}
async fn perform_crud<'a, Data>(
data: Data,
context: web::Data<LemmyContext>,
+ apub_data: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: PerformCrud
+ 'static,
{
let res = data.perform(&context, None).await?;
- SendActivity::send_activity(&data, &res, &context).await?;
+ SendActivity::send_activity(&data, &res, &apub_data).await?;
Ok(HttpResponse::Ok().json(res))
}
async fn route_get_crud<'a, Data>(
data: web::Query<Data>,
context: web::Data<LemmyContext>,
+ apub_data: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: PerformCrud
+ Send
+ 'static,
{
- perform_crud::<Data>(data.0, context).await
+ perform_crud::<Data>(data.0, context, apub_data).await
}
async fn route_post_crud<'a, Data>(
data: web::Json<Data>,
context: web::Data<LemmyContext>,
+ apub_data: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: PerformCrud
+ Send
+ 'static,
{
- perform_crud::<Data>(data.0, context).await
+ perform_crud::<Data>(data.0, context, apub_data).await
}
+use activitypub_federation::config::Data as ContextData;
use actix_web::{web, Error, HttpRequest, HttpResponse};
use actix_web_actors::ws;
use actix_ws::{MessageStream, Session};
use serde::Deserialize;
use serde_json::Value;
use std::{
+ ops::Deref,
result,
str::FromStr,
sync::{Arc, Mutex},
body: web::Payload,
context: web::Data<LemmyContext>,
rate_limiter: web::Data<RateLimitCell>,
+ apub_data: ContextData<LemmyContext>,
) -> Result<HttpResponse, Error> {
let (response, session, stream) = actix_ws::handle(&req, body)?;
connection_id,
alive,
rate_limiter,
- context,
+ apub_data,
));
Ok(response)
connection_id: ConnectionId,
alive: Arc<Mutex<Instant>>,
rate_limiter: web::Data<RateLimitCell>,
- context: web::Data<LemmyContext>,
+ context: ContextData<LemmyContext>,
) -> Result<(), LemmyError> {
while let Some(Ok(msg)) = stream.next().await {
match msg {
client_ip.clone(),
connection_id,
rate_limiter.get_ref(),
- context.get_ref().clone(),
+ context.reset_request_count(),
)
.await;
ip: IpAddr,
connection_id: ConnectionId,
rate_limiter: &RateLimitCell,
- context: LemmyContext,
+ context: ContextData<LemmyContext>,
) -> Result<String, LemmyError> {
let json: Value = serde_json::from_str(&msg)?;
let data = json
}
pub async fn match_websocket_operation_crud(
- context: LemmyContext,
+ context: ContextData<LemmyContext>,
id: ConnectionId,
op: UserOperationCrud,
data: Value,
}
async fn do_websocket_operation_crud<'a, 'b, Data>(
- context: LemmyContext,
+ context: ContextData<LemmyContext>,
id: ConnectionId,
op: UserOperationCrud,
data: Value,
) -> result::Result<String, LemmyError>
where
- Data: PerformCrud + SendActivity<Response = <Data as PerformCrud>::Response>,
+ Data: PerformCrud + SendActivity<Response = <Data as PerformCrud>::Response> + Send,
for<'de> Data: Deserialize<'de>,
{
let parsed_data: Data = serde_json::from_value(data)?;
let res = parsed_data
- .perform(&web::Data::new(context.clone()), Some(id))
+ .perform(&web::Data::new(context.deref().clone()), Some(id))
.await?;
SendActivity::send_activity(&parsed_data, &res, &context).await?;
serialize_websocket_message(&op, &res)
}
pub async fn match_websocket_operation_apub(
- context: LemmyContext,
+ context: ContextData<LemmyContext>,
id: ConnectionId,
op: UserOperationApub,
data: Value,
}
async fn do_websocket_operation_apub<'a, 'b, Data>(
- context: LemmyContext,
+ context: ContextData<LemmyContext>,
id: ConnectionId,
op: UserOperationApub,
data: Value,
) -> result::Result<String, LemmyError>
where
- Data: PerformApub + SendActivity<Response = <Data as PerformApub>::Response>,
+ Data: PerformApub + SendActivity<Response = <Data as PerformApub>::Response> + Send,
for<'de> Data: Deserialize<'de>,
{
let parsed_data: Data = serde_json::from_value(data)?;
- let res = parsed_data
- .perform(&web::Data::new(context.clone()), Some(id))
- .await?;
+ let res = parsed_data.perform(&context, Some(id)).await?;
SendActivity::send_activity(&parsed_data, &res, &context).await?;
serialize_websocket_message(&op, &res)
}
pub async fn match_websocket_operation(
- context: LemmyContext,
+ context: ContextData<LemmyContext>,
id: ConnectionId,
op: UserOperation,
data: Value,
}
async fn do_websocket_operation<'a, 'b, Data>(
- context: LemmyContext,
+ context: ContextData<LemmyContext>,
id: ConnectionId,
op: UserOperation,
data: Value,
) -> result::Result<String, LemmyError>
where
- Data: Perform + SendActivity<Response = <Data as Perform>::Response>,
+ Data: Perform + SendActivity<Response = <Data as Perform>::Response> + Send,
for<'de> Data: Deserialize<'de>,
{
let parsed_data: Data = serde_json::from_value(data)?;
let res = parsed_data
- .perform(&web::Data::new(context.clone()), Some(id))
+ .perform(&web::Data::new(context.deref().clone()), Some(id))
.await?;
SendActivity::send_activity(&parsed_data, &res, &context).await?;
serialize_websocket_message(&op, &res)
// This is for db migrations that require code
-use activitypub_federation::core::signatures::generate_actor_keypair;
+use activitypub_federation::http_signatures::generate_actor_keypair;
use diesel::{
sql_types::{Nullable, Text},
ExpressionMethods,
pub mod telemetry;
use crate::{code_migrations::run_advanced_migrations, root_span_builder::QuieterRootSpanBuilder};
+use activitypub_federation::config::{FederationConfig, FederationMiddleware};
use actix_web::{middleware, web::Data, App, HttpServer, Result};
use doku::json::{AutoComments, CommentsStyle, Formatting, ObjectsStyle};
use lemmy_api_common::{
},
websocket::chat_server::ChatServer,
};
+use lemmy_apub::{VerifyUrlData, FEDERATION_HTTP_FETCH_LIMIT};
use lemmy_db_schema::{
source::secret::Secret,
utils::{build_db_pool, get_database_url, run_migrations},
pool.clone(),
chat_server.clone(),
client.clone(),
- settings.clone(),
secret.clone(),
rate_limit_cell.clone(),
);
+
+ let federation_config = FederationConfig::builder()
+ .domain(settings.hostname.clone())
+ .app_data(context.clone())
+ .client(client.clone())
+ .http_fetch_limit(FEDERATION_HTTP_FETCH_LIMIT)
+ .worker_count(local_site.federation_worker_count as u64)
+ .debug(cfg!(debug_assertions))
+ .http_signature_compat(true)
+ .url_verifier(Box::new(VerifyUrlData(context.pool().clone())))
+ .build()
+ .expect("configure federation");
+
App::new()
.wrap(middleware::Logger::default())
.wrap(TracingLogger::<QuieterRootSpanBuilder>::new())
.app_data(Data::new(context))
.app_data(Data::new(rate_limit_cell.clone()))
+ .wrap(FederationMiddleware::new(federation_config))
// The routes
.configure(|cfg| api_routes_http::config(cfg, rate_limit_cell))
.configure(|cfg| {