mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-24 02:45:59 +00:00
Nutomic
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
224 lines
6.6 KiB
Rust
224 lines
6.6 KiB
Rust
use crate::{
|
|
activities::{
|
|
block::{generate_cc, SiteOrCommunity},
|
|
community::send_activity_in_community,
|
|
generate_activity_id,
|
|
send_lemmy_activity,
|
|
verify_is_public,
|
|
verify_mod_action,
|
|
verify_person_in_community,
|
|
},
|
|
activity_lists::AnnouncableActivities,
|
|
insert_received_activity,
|
|
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
|
protocol::activities::block::block_user::BlockUser,
|
|
};
|
|
use activitypub_federation::{
|
|
config::Data,
|
|
kinds::{activity::BlockType, public},
|
|
protocol::verification::verify_domains_match,
|
|
traits::{ActivityHandler, Actor},
|
|
};
|
|
use anyhow::anyhow;
|
|
use chrono::NaiveDateTime;
|
|
use lemmy_api_common::{
|
|
context::LemmyContext,
|
|
utils::{remove_user_data, remove_user_data_in_community},
|
|
};
|
|
use lemmy_db_schema::{
|
|
source::{
|
|
community::{
|
|
CommunityFollower,
|
|
CommunityFollowerForm,
|
|
CommunityPersonBan,
|
|
CommunityPersonBanForm,
|
|
},
|
|
moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
|
|
person::{Person, PersonUpdateForm},
|
|
},
|
|
traits::{Bannable, Crud, Followable},
|
|
};
|
|
use lemmy_utils::{error::LemmyError, utils::time::convert_datetime};
|
|
use url::Url;
|
|
|
|
impl BlockUser {
|
|
pub(in crate::activities::block) async fn new(
|
|
target: &SiteOrCommunity,
|
|
user: &ApubPerson,
|
|
mod_: &ApubPerson,
|
|
remove_data: Option<bool>,
|
|
reason: Option<String>,
|
|
expires: Option<NaiveDateTime>,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<BlockUser, LemmyError> {
|
|
let audience = if let SiteOrCommunity::Community(c) = target {
|
|
Some(c.id().into())
|
|
} else {
|
|
None
|
|
};
|
|
Ok(BlockUser {
|
|
actor: mod_.id().into(),
|
|
to: vec![public()],
|
|
object: user.id().into(),
|
|
cc: generate_cc(target, &mut context.pool()).await?,
|
|
target: target.id(),
|
|
kind: BlockType::Block,
|
|
remove_data,
|
|
summary: reason,
|
|
id: generate_activity_id(
|
|
BlockType::Block,
|
|
&context.settings().get_protocol_and_hostname(),
|
|
)?,
|
|
audience,
|
|
expires: expires.map(convert_datetime),
|
|
})
|
|
}
|
|
|
|
#[tracing::instrument(skip_all)]
|
|
pub async fn send(
|
|
target: &SiteOrCommunity,
|
|
user: &ApubPerson,
|
|
mod_: &ApubPerson,
|
|
remove_data: bool,
|
|
reason: Option<String>,
|
|
expires: Option<NaiveDateTime>,
|
|
context: &Data<LemmyContext>,
|
|
) -> Result<(), LemmyError> {
|
|
let block = BlockUser::new(
|
|
target,
|
|
user,
|
|
mod_,
|
|
Some(remove_data),
|
|
reason,
|
|
expires,
|
|
context,
|
|
)
|
|
.await?;
|
|
|
|
match target {
|
|
SiteOrCommunity::Site(_) => {
|
|
let inboxes = remote_instance_inboxes(&mut context.pool()).await?;
|
|
send_lemmy_activity(context, block, mod_, inboxes, false).await
|
|
}
|
|
SiteOrCommunity::Community(c) => {
|
|
let activity = AnnouncableActivities::BlockUser(block);
|
|
let inboxes = vec![user.shared_inbox_or_inbox()];
|
|
send_activity_in_community(activity, mod_, c, inboxes, true, context).await
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl ActivityHandler for BlockUser {
|
|
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)?;
|
|
match self.target.dereference(context).await? {
|
|
SiteOrCommunity::Site(site) => {
|
|
let domain = self.object.inner().domain().expect("url needs domain");
|
|
if context.settings().hostname == domain {
|
|
return Err(
|
|
anyhow!("Site bans from remote instance can't affect user's home instance").into(),
|
|
);
|
|
}
|
|
// site ban can only target a user who is on the same instance as the actor (admin)
|
|
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).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>) -> Result<(), LemmyError> {
|
|
let expires = self.expires.map(|u| u.naive_local());
|
|
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(
|
|
&mut context.pool(),
|
|
blocked_person.id,
|
|
&PersonUpdateForm::builder()
|
|
.banned(Some(true))
|
|
.ban_expires(Some(expires))
|
|
.build(),
|
|
)
|
|
.await?;
|
|
if self.remove_data.unwrap_or(false) {
|
|
remove_user_data(
|
|
blocked_person.id,
|
|
&mut context.pool(),
|
|
context.settings(),
|
|
context.client(),
|
|
)
|
|
.await?;
|
|
}
|
|
|
|
// write mod log
|
|
let form = ModBanForm {
|
|
mod_person_id: mod_person.id,
|
|
other_person_id: blocked_person.id,
|
|
reason: self.summary,
|
|
banned: Some(true),
|
|
expires,
|
|
};
|
|
ModBan::create(&mut context.pool(), &form).await?;
|
|
}
|
|
SiteOrCommunity::Community(community) => {
|
|
let community_user_ban_form = CommunityPersonBanForm {
|
|
community_id: community.id,
|
|
person_id: blocked_person.id,
|
|
expires: Some(expires),
|
|
};
|
|
CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form).await?;
|
|
|
|
// Also unsubscribe them from the community, if they are subscribed
|
|
let community_follower_form = CommunityFollowerForm {
|
|
community_id: community.id,
|
|
person_id: blocked_person.id,
|
|
pending: false,
|
|
};
|
|
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
|
|
.await
|
|
.ok();
|
|
|
|
if self.remove_data.unwrap_or(false) {
|
|
remove_user_data_in_community(community.id, blocked_person.id, &mut context.pool())
|
|
.await?;
|
|
}
|
|
|
|
// write to mod log
|
|
let form = ModBanFromCommunityForm {
|
|
mod_person_id: mod_person.id,
|
|
other_person_id: blocked_person.id,
|
|
community_id: community.id,
|
|
reason: self.summary,
|
|
banned: Some(true),
|
|
expires,
|
|
};
|
|
ModBanFromCommunity::create(&mut context.pool(), &form).await?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|