Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ pub async fn match_websocket_operation(
UserOperation::SaveSiteConfig => {
do_websocket_operation::<SaveSiteConfig>(context, id, op, data).await
}
UserOperation::PurgePerson => {
do_websocket_operation::<PurgePerson>(context, id, op, data).await
}
UserOperation::PurgeCommunity => {
do_websocket_operation::<PurgeCommunity>(context, id, op, data).await
}
UserOperation::PurgePost => do_websocket_operation::<PurgePost>(context, id, op, data).await,
UserOperation::PurgeComment => {
do_websocket_operation::<PurgeComment>(context, id, op, data).await
}
UserOperation::Search => do_websocket_operation::<Search>(context, id, op, data).await,
UserOperation::ResolveObject => {
do_websocket_operation::<ResolveObject>(context, id, op, data).await
Expand Down
8 changes: 7 additions & 1 deletion crates/api/src/local_user/ban_person.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ impl Perform for BanPerson {
// Remove their data if that's desired
let remove_data = data.remove_data.unwrap_or(false);
if remove_data {
remove_user_data(person.id, context.pool()).await?;
remove_user_data(
person.id,
context.pool(),
&context.settings(),
context.client(),
)
.await?;
}

// Mod tables
Expand Down
1 change: 1 addition & 0 deletions crates/api/src/site/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod config;
mod leave_admin;
mod mod_log;
mod purge;
mod registration_applications;
mod resolve_object;
mod search;
32 changes: 30 additions & 2 deletions crates/api/src/site/mod_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use lemmy_api_common::{
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
};
use lemmy_db_views_moderator::structs::{
AdminPurgeCommentView,
AdminPurgeCommunityView,
AdminPurgePersonView,
AdminPurgePostView,
ModAddCommunityView,
ModAddView,
ModBanFromCommunityView,
Expand Down Expand Up @@ -83,17 +87,37 @@ impl Perform for GetModlog {
.await??;

// These arrays are only for the full modlog, when a community isn't given
let (removed_communities, banned, added) = if data.community_id.is_none() {
let (
removed_communities,
banned,
added,
admin_purged_persons,
admin_purged_communities,
admin_purged_posts,
admin_purged_comments,
) = if data.community_id.is_none() {
blocking(context.pool(), move |conn| {
Ok((
ModRemoveCommunityView::list(conn, mod_person_id, page, limit)?,
ModBanView::list(conn, mod_person_id, page, limit)?,
ModAddView::list(conn, mod_person_id, page, limit)?,
AdminPurgePersonView::list(conn, mod_person_id, page, limit)?,
AdminPurgeCommunityView::list(conn, mod_person_id, page, limit)?,
AdminPurgePostView::list(conn, mod_person_id, page, limit)?,
AdminPurgeCommentView::list(conn, mod_person_id, page, limit)?,
)) as Result<_, LemmyError>
})
.await??
} else {
(Vec::new(), Vec::new(), Vec::new())
(
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should work with simply Default::Default()

};

// Return the jwt
Expand All @@ -108,6 +132,10 @@ impl Perform for GetModlog {
added_to_community,
added,
transferred_to_community,
admin_purged_persons,
admin_purged_communities,
admin_purged_posts,
admin_purged_comments,
hidden_communities,
})
}
Expand Down
63 changes: 63 additions & 0 deletions crates/api/src/site/purge/comment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
site::{PurgeComment, PurgeItemResponse},
utils::{blocking, get_local_user_view_from_jwt, is_admin},
};
use lemmy_db_schema::{
source::{
comment::Comment,
moderator::{AdminPurgeComment, AdminPurgeCommentForm},
},
traits::Crud,
};
use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;

#[async_trait::async_trait(?Send)]
impl Perform for PurgeComment {
type Response = PurgeItemResponse;

#[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<Self::Response, LemmyError> {
let data: &Self = self;
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;

// Only let admins purge an item
is_admin(&local_user_view)?;

let comment_id = data.comment_id;

// Read the comment to get the post_id
let comment = blocking(context.pool(), move |conn| Comment::read(conn, comment_id)).await??;

let post_id = comment.post_id;

// TODO read comments for pictrs images and purge them
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about inline images in comments, right? And similarly for inline images in post body, community/user profile. Those are generally tricky to manage. Easiest option would be not to permit inline images, otherwise we will have to add some functionality to retrieve them sooner or later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. We could have something that scans through every comment, post body, bio, community sidebar... ( there's probably a few others I'm not thinking of), does a regex search for pictrs URLs, and tries to remove them. IMO that's a little excessive, but it might be necessary later on. I'll just leave that TODO in there to remind us.


blocking(context.pool(), move |conn| {
Comment::delete(conn, comment_id)
})
.await??;

// Mod tables
let reason = data.reason.to_owned();
let form = AdminPurgeCommentForm {
admin_person_id: local_user_view.person.id,
reason,
post_id,
};

blocking(context.pool(), move |conn| {
AdminPurgeComment::create(conn, &form)
})
.await??;

Ok(PurgeItemResponse { success: true })
}
}
82 changes: 82 additions & 0 deletions crates/api/src/site/purge/community.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
request::purge_image_from_pictrs,
site::{PurgeCommunity, PurgeItemResponse},
utils::{blocking, get_local_user_view_from_jwt, is_admin, purge_image_posts_for_community},
};
use lemmy_db_schema::{
source::{
community::Community,
moderator::{AdminPurgeCommunity, AdminPurgeCommunityForm},
},
traits::Crud,
};
use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;

#[async_trait::async_trait(?Send)]
impl Perform for PurgeCommunity {
type Response = PurgeItemResponse;

#[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<Self::Response, LemmyError> {
let data: &Self = self;
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;

// Only let admins purge an item
is_admin(&local_user_view)?;

let community_id = data.community_id;

// Read the community to get its images
let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
.await??;

if let Some(banner) = community.banner {
purge_image_from_pictrs(context.client(), &context.settings(), &banner)
.await
.ok();
}

if let Some(icon) = community.icon {
purge_image_from_pictrs(context.client(), &context.settings(), &icon)
.await
.ok();
}

purge_image_posts_for_community(
community_id,
context.pool(),
&context.settings(),
context.client(),
)
.await?;

blocking(context.pool(), move |conn| {
Community::delete(conn, community_id)
})
.await??;

// Mod tables
let reason = data.reason.to_owned();
let form = AdminPurgeCommunityForm {
admin_person_id: local_user_view.person.id,
reason,
};

blocking(context.pool(), move |conn| {
AdminPurgeCommunity::create(conn, &form)
})
.await??;

Ok(PurgeItemResponse { success: true })
}
}
4 changes: 4 additions & 0 deletions crates/api/src/site/purge/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod comment;
mod community;
mod person;
mod post;
75 changes: 75 additions & 0 deletions crates/api/src/site/purge/person.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
request::purge_image_from_pictrs,
site::{PurgeItemResponse, PurgePerson},
utils::{blocking, get_local_user_view_from_jwt, is_admin, purge_image_posts_for_person},
};
use lemmy_db_schema::{
source::{
moderator::{AdminPurgePerson, AdminPurgePersonForm},
person::Person,
},
traits::Crud,
};
use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;

#[async_trait::async_trait(?Send)]
impl Perform for PurgePerson {
type Response = PurgeItemResponse;

#[tracing::instrument(skip(context, _websocket_id))]
async fn perform(
&self,
context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>,
) -> Result<Self::Response, LemmyError> {
let data: &Self = self;
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;

// Only let admins purge an item
is_admin(&local_user_view)?;

// Read the person to get their images
let person_id = data.person_id;
let person = blocking(context.pool(), move |conn| Person::read(conn, person_id)).await??;

if let Some(banner) = person.banner {
purge_image_from_pictrs(context.client(), &context.settings(), &banner)
.await
.ok();
}

if let Some(avatar) = person.avatar {
purge_image_from_pictrs(context.client(), &context.settings(), &avatar)
.await
.ok();
}

purge_image_posts_for_person(
person_id,
context.pool(),
&context.settings(),
context.client(),
)
.await?;

blocking(context.pool(), move |conn| Person::delete(conn, person_id)).await??;

// Mod tables
let reason = data.reason.to_owned();
let form = AdminPurgePersonForm {
admin_person_id: local_user_view.person.id,
reason,
};

blocking(context.pool(), move |conn| {
AdminPurgePerson::create(conn, &form)
})
.await??;

Ok(PurgeItemResponse { success: true })
}
}
Loading