mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-06-19 22:00:33 +00:00
e9e76549a8
* Split activity table into sent and received parts (fixes #3103) The received activities are only stored in order to avoid processing the same incoming activity multiple times. For this purpose it is completely unnecessary to store the data. So we can split the table into sent_activity and received_activity parts, where only sent_activity table needs to store activity data. This should reduce storage use significantly. Also reduces activity storage duration to three months, we can reduce this further if necessary. Additionally the id columns of activity tables are removed because they are completely unused and risk overflowing (fixes #3560). * address review * move insert_received_activity() methods to verify handlers * remove unnecessary conflict line * clippy * use on conflict, add tests
200 lines
5.8 KiB
Rust
200 lines
5.8 KiB
Rust
use crate::{
|
|
activities::{
|
|
check_community_deleted_or_removed,
|
|
community::send_activity_in_community,
|
|
generate_activity_id,
|
|
verify_is_public,
|
|
verify_mod_action,
|
|
verify_person_in_community,
|
|
},
|
|
activity_lists::AnnouncableActivities,
|
|
insert_received_activity,
|
|
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
|
protocol::{
|
|
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
|
|
InCommunity,
|
|
},
|
|
SendActivity,
|
|
};
|
|
use activitypub_federation::{
|
|
config::Data,
|
|
kinds::public,
|
|
protocol::verification::{verify_domains_match, verify_urls_match},
|
|
traits::{ActivityHandler, Actor, Object},
|
|
};
|
|
use lemmy_api_common::{
|
|
context::LemmyContext,
|
|
post::{CreatePost, EditPost, PostResponse},
|
|
};
|
|
use lemmy_db_schema::{
|
|
aggregates::structs::PostAggregates,
|
|
newtypes::PersonId,
|
|
source::{
|
|
community::Community,
|
|
person::Person,
|
|
post::{Post, PostLike, PostLikeForm},
|
|
},
|
|
traits::{Crud, Likeable},
|
|
};
|
|
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
|
use url::Url;
|
|
|
|
#[async_trait::async_trait]
|
|
impl SendActivity for CreatePost {
|
|
type Response = PostResponse;
|
|
|
|
async fn send_activity(
|
|
_request: &Self,
|
|
response: &Self::Response,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
CreateOrUpdatePage::send(
|
|
&response.post_view.post,
|
|
response.post_view.creator.id,
|
|
CreateOrUpdateType::Create,
|
|
context,
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl SendActivity for EditPost {
|
|
type Response = PostResponse;
|
|
|
|
async fn send_activity(
|
|
_request: &Self,
|
|
response: &Self::Response,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
CreateOrUpdatePage::send(
|
|
&response.post_view.post,
|
|
response.post_view.creator.id,
|
|
CreateOrUpdateType::Update,
|
|
context,
|
|
)
|
|
.await
|
|
}
|
|
}
|
|
|
|
impl CreateOrUpdatePage {
|
|
pub(crate) async fn new(
|
|
post: ApubPost,
|
|
actor: &ApubPerson,
|
|
community: &ApubCommunity,
|
|
kind: CreateOrUpdateType,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<CreateOrUpdatePage, LemmyError> {
|
|
let id = generate_activity_id(
|
|
kind.clone(),
|
|
&context.settings().get_protocol_and_hostname(),
|
|
)?;
|
|
Ok(CreateOrUpdatePage {
|
|
actor: actor.id().into(),
|
|
to: vec![public()],
|
|
object: post.into_json(context).await?,
|
|
cc: vec![community.id()],
|
|
kind,
|
|
id: id.clone(),
|
|
audience: Some(community.id().into()),
|
|
})
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
pub(crate) async fn send(
|
|
post: &Post,
|
|
person_id: PersonId,
|
|
kind: CreateOrUpdateType,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
let post = ApubPost(post.clone());
|
|
let community_id = post.community_id;
|
|
let person: ApubPerson = Person::read(&mut context.pool(), person_id).await?.into();
|
|
let community: ApubCommunity = Community::read(&mut context.pool(), community_id)
|
|
.await?
|
|
.into();
|
|
|
|
let create_or_update =
|
|
CreateOrUpdatePage::new(post, &person, &community, kind, context).await?;
|
|
let is_mod_action = create_or_update.object.is_mod_action(context).await?;
|
|
let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
|
|
send_activity_in_community(
|
|
activity,
|
|
&person,
|
|
&community,
|
|
vec![],
|
|
is_mod_action,
|
|
context,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl ActivityHandler for CreateOrUpdatePage {
|
|
type DataType = LemmyContext;
|
|
type Error = LemmyError;
|
|
|
|
fn id(&self) -> &Url {
|
|
&self.id
|
|
}
|
|
|
|
fn actor(&self) -> &Url {
|
|
self.actor.inner()
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
|
insert_received_activity(&self.id, context).await?;
|
|
verify_is_public(&self.to, &self.cc)?;
|
|
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::Create => {
|
|
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
|
verify_urls_match(self.actor.inner(), self.object.creator()?.inner())?;
|
|
// Check that the post isnt locked, as that isnt possible for newly created posts.
|
|
// However, when fetching a remote post we generate a new create activity with the current
|
|
// locked value, so this check may fail. So only check if its a local community,
|
|
// because then we will definitely receive all create and update activities separately.
|
|
let is_locked = self.object.comments_enabled == Some(false);
|
|
if community.local && is_locked {
|
|
return Err(LemmyErrorType::NewPostCannotBeLocked)?;
|
|
}
|
|
}
|
|
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).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).await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
|
|
let post = ApubPost::from_json(self.object, context).await?;
|
|
|
|
// author likes their own post by default
|
|
let like_form = PostLikeForm {
|
|
post_id: post.id,
|
|
person_id: post.creator_id,
|
|
score: 1,
|
|
};
|
|
PostLike::like(&mut context.pool(), &like_form).await?;
|
|
|
|
// Calculate initial hot_rank for post
|
|
PostAggregates::update_hot_rank(&mut context.pool(), post.id).await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|