separate table for instance block mod log

This commit is contained in:
Felix Ableitner 2024-11-20 12:03:40 +01:00
parent 2a1d3c9db8
commit f9bb606c62
14 changed files with 191 additions and 77 deletions

View file

@ -2,33 +2,32 @@ use activitypub_federation::config::Data;
use actix_web::web::Json; use actix_web::web::Json;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
site::{AdminBlockInstance, AdminBlockInstanceResponse}, site::{AdminBlockInstanceParams, AdminBlockInstanceResponse},
utils::is_admin, utils::is_admin,
}; };
use lemmy_db_schema::source::federation_blocklist::{FederationBlockList, FederationBlockListForm}; use lemmy_db_schema::source::federation_blocklist::{AdminBlockInstance, AdminBlockInstanceForm};
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn block_instance( pub async fn block_instance(
data: Json<AdminBlockInstance>, data: Json<AdminBlockInstanceParams>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<Json<AdminBlockInstanceResponse>> { ) -> LemmyResult<Json<AdminBlockInstanceResponse>> {
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
let instance_block_form = FederationBlockListForm { let instance_block_form = AdminBlockInstanceForm {
instance_id: data.instance_id, instance_id: data.instance_id,
admin_person_id: Some(local_user_view.person.id), admin_person_id: local_user_view.person.id,
reason: data.reason.clone(), reason: data.reason.clone(),
expires: data.expires, expires: data.expires,
updated: None,
}; };
if data.block { if data.block {
FederationBlockList::block(&mut context.pool(), &instance_block_form).await?; AdminBlockInstance::block(&mut context.pool(), &instance_block_form).await?;
} else { } else {
FederationBlockList::unblock(&mut context.pool(), &instance_block_form).await?; AdminBlockInstance::unblock(&mut context.pool(), data.instance_id).await?;
} }
Ok(Json(AdminBlockInstanceResponse { Ok(Json(AdminBlockInstanceResponse {

View file

@ -1,8 +1,8 @@
pub mod admin_block_instance; pub mod admin_block_instance;
pub mod block;
pub mod federated_instances; pub mod federated_instances;
pub mod leave_admin; pub mod leave_admin;
pub mod list_all_media; pub mod list_all_media;
pub mod mod_log; pub mod mod_log;
pub mod purge; pub mod purge;
pub mod registration_applications; pub mod registration_applications;
pub mod user_block_instance;

View file

@ -7,10 +7,22 @@ use lemmy_api_common::{
use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType}; use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType};
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_moderator::structs::{ use lemmy_db_views_moderator::structs::{
AdminBlockInstanceView, AdminPurgeCommentView, AdminPurgeCommunityView, AdminPurgePersonView, AdminBlockInstanceView,
AdminPurgePostView, ModAddCommunityView, ModAddView, AdminPurgeCommentView,
ModBanFromCommunityView, ModBanView, ModFeaturePostView, ModHideCommunityView, ModLockPostView, AdminPurgeCommunityView,
ModRemoveCommentView, ModRemoveCommunityView, ModRemovePostView, ModTransferCommunityView, AdminPurgePersonView,
AdminPurgePostView,
ModAddCommunityView,
ModAddView,
ModBanFromCommunityView,
ModBanView,
ModFeaturePostView,
ModHideCommunityView,
ModLockPostView,
ModRemoveCommentView,
ModRemoveCommunityView,
ModRemovePostView,
ModTransferCommunityView,
ModlogListParams, ModlogListParams,
}; };
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;

View file

@ -12,7 +12,7 @@ use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn block_instance( pub async fn user_block_instance(
data: Json<BlockInstance>, data: Json<BlockInstance>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
context: Data<LemmyContext>, context: Data<LemmyContext>,

View file

@ -2,7 +2,13 @@ use crate::federate_retry_sleep_duration;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{ newtypes::{
CommentId, CommunityId, InstanceId, LanguageId, PersonId, PostId, RegistrationApplicationId, CommentId,
CommunityId,
InstanceId,
LanguageId,
PersonId,
PostId,
RegistrationApplicationId,
}, },
source::{ source::{
community::Community, community::Community,
@ -14,20 +20,45 @@ use lemmy_db_schema::{
person::Person, person::Person,
tagline::Tagline, tagline::Tagline,
}, },
CommentSortType, FederationMode, ListingType, ModlogActionType, PostListingMode, PostSortType, CommentSortType,
RegistrationMode, SearchType, FederationMode,
ListingType,
ModlogActionType,
PostListingMode,
PostSortType,
RegistrationMode,
SearchType,
}; };
use lemmy_db_views::structs::{ use lemmy_db_views::structs::{
CommentView, LocalUserView, PostView, RegistrationApplicationView, SiteView, CommentView,
LocalUserView,
PostView,
RegistrationApplicationView,
SiteView,
}; };
use lemmy_db_views_actor::structs::{ use lemmy_db_views_actor::structs::{
CommunityFollowerView, CommunityModeratorView, CommunityView, PersonView, CommunityFollowerView,
CommunityModeratorView,
CommunityView,
PersonView,
}; };
use lemmy_db_views_moderator::structs::{ use lemmy_db_views_moderator::structs::{
AdminBlockInstanceView, AdminPurgeCommentView, AdminPurgeCommunityView, AdminPurgePersonView, AdminBlockInstanceView,
AdminPurgePostView, ModAddCommunityView, ModAddView, ModBanFromCommunityView, ModBanView, AdminPurgeCommentView,
ModFeaturePostView, ModHideCommunityView, ModLockPostView, ModRemoveCommentView, AdminPurgeCommunityView,
ModRemoveCommunityView, ModRemovePostView, ModTransferCommunityView, AdminPurgePersonView,
AdminPurgePostView,
ModAddCommunityView,
ModAddView,
ModBanFromCommunityView,
ModBanView,
ModFeaturePostView,
ModHideCommunityView,
ModLockPostView,
ModRemoveCommentView,
ModRemoveCommunityView,
ModRemovePostView,
ModTransferCommunityView,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -635,7 +666,7 @@ pub struct BlockInstanceResponse {
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))] #[cfg_attr(feature = "full", ts(export))]
pub struct AdminBlockInstance { pub struct AdminBlockInstanceParams {
pub instance_id: InstanceId, pub instance_id: InstanceId,
pub block: bool, pub block: bool,
pub reason: Option<String>, pub reason: Option<String>,

View file

@ -1,30 +1,49 @@
use crate::{ use crate::{
schema::federation_blocklist, newtypes::InstanceId,
source::federation_blocklist::{FederationBlockList, FederationBlockListForm}, schema::{admin_block_instance, federation_blocklist},
source::federation_blocklist::{
AdminBlockInstance,
AdminBlockInstanceForm,
FederationBlockListForm,
},
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use diesel::{delete, ExpressionMethods, QueryDsl}; use diesel::{delete, dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
use diesel::{dsl::insert_into, result::Error};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
impl FederationBlockList { impl AdminBlockInstance {
pub async fn block(pool: &mut DbPool<'_>, form: &FederationBlockListForm) -> Result<Self, Error> { pub async fn block(pool: &mut DbPool<'_>, form: &AdminBlockInstanceForm) -> Result<(), Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(federation_blocklist::table) conn
.build_transaction()
.run(|conn| {
Box::pin(async move {
insert_into(admin_block_instance::table)
.values(form) .values(form)
.get_result::<Self>(conn) .execute(conn)
.await?;
let form2 = FederationBlockListForm {
instance_id: form.instance_id,
updated: None,
block_expires: form.expires,
};
insert_into(federation_blocklist::table)
.values(form2)
.execute(conn)
.await?;
Ok(())
})
})
.await .await
} }
pub async fn unblock( pub async fn unblock(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<(), Error> {
pool: &mut DbPool<'_>,
form: &FederationBlockListForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
delete( delete(
federation_blocklist::table federation_blocklist::table.filter(federation_blocklist::dsl::instance_id.eq(instance_id)),
.filter(federation_blocklist::dsl::instance_id.eq(form.instance_id)),
) )
.get_result(conn) .execute(conn)
.await .await?;
Ok(())
} }
} }

View file

@ -42,6 +42,17 @@ pub mod sql_types {
pub struct RegistrationModeEnum; pub struct RegistrationModeEnum;
} }
diesel::table! {
admin_block_instance (id) {
id -> Int4,
instance_id -> Int4,
admin_person_id -> Int4,
reason -> Nullable<Text>,
expires -> Nullable<Timestamptz>,
published -> Timestamptz,
}
}
diesel::table! { diesel::table! {
admin_purge_comment (id) { admin_purge_comment (id) {
id -> Int4, id -> Int4,
@ -282,9 +293,7 @@ diesel::table! {
instance_id -> Int4, instance_id -> Int4,
published -> Timestamptz, published -> Timestamptz,
updated -> Nullable<Timestamptz>, updated -> Nullable<Timestamptz>,
admin_person_id -> Nullable<Int4>, block_expires -> Nullable<Timestamptz>,
reason -> Nullable<Text>,
expires -> Nullable<Timestamptz>,
} }
} }
@ -934,6 +943,8 @@ diesel::table! {
} }
} }
diesel::joinable!(admin_block_instance -> instance (instance_id));
diesel::joinable!(admin_block_instance -> person (admin_person_id));
diesel::joinable!(admin_purge_comment -> person (admin_person_id)); diesel::joinable!(admin_purge_comment -> person (admin_person_id));
diesel::joinable!(admin_purge_comment -> post (post_id)); diesel::joinable!(admin_purge_comment -> post (post_id));
diesel::joinable!(admin_purge_community -> person (admin_person_id)); diesel::joinable!(admin_purge_community -> person (admin_person_id));
@ -958,7 +969,6 @@ diesel::joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
diesel::joinable!(email_verification -> local_user (local_user_id)); diesel::joinable!(email_verification -> local_user (local_user_id));
diesel::joinable!(federation_allowlist -> instance (instance_id)); diesel::joinable!(federation_allowlist -> instance (instance_id));
diesel::joinable!(federation_blocklist -> instance (instance_id)); diesel::joinable!(federation_blocklist -> instance (instance_id));
diesel::joinable!(federation_blocklist -> person (admin_person_id));
diesel::joinable!(federation_queue_state -> instance (instance_id)); diesel::joinable!(federation_queue_state -> instance (instance_id));
diesel::joinable!(instance_actions -> instance (instance_id)); diesel::joinable!(instance_actions -> instance (instance_id));
diesel::joinable!(instance_actions -> person (person_id)); diesel::joinable!(instance_actions -> person (person_id));
@ -1012,6 +1022,7 @@ diesel::joinable!(site_language -> language (language_id));
diesel::joinable!(site_language -> site (site_id)); diesel::joinable!(site_language -> site (site_id));
diesel::allow_tables_to_appear_in_same_query!( diesel::allow_tables_to_appear_in_same_query!(
admin_block_instance,
admin_purge_comment, admin_purge_comment,
admin_purge_community, admin_purge_community,
admin_purge_person, admin_purge_person,

View file

@ -1,6 +1,6 @@
use crate::newtypes::{InstanceId, PersonId}; use crate::newtypes::{InstanceId, PersonId};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use crate::schema::federation_blocklist; use crate::schema::{admin_block_instance, federation_blocklist};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
@ -23,10 +23,7 @@ pub struct FederationBlockList {
pub instance_id: InstanceId, pub instance_id: InstanceId,
pub published: DateTime<Utc>, pub published: DateTime<Utc>,
pub updated: Option<DateTime<Utc>>, pub updated: Option<DateTime<Utc>>,
// TODO: would be good to make this mandatory but value doesnt exist for old entries pub block_expires: Option<DateTime<Utc>>,
pub admin_person_id: Option<PersonId>,
pub reason: Option<String>,
pub expires: Option<DateTime<Utc>>,
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
@ -35,7 +32,37 @@ pub struct FederationBlockList {
pub struct FederationBlockListForm { pub struct FederationBlockListForm {
pub instance_id: InstanceId, pub instance_id: InstanceId,
pub updated: Option<DateTime<Utc>>, pub updated: Option<DateTime<Utc>>,
pub admin_person_id: Option<PersonId>, pub block_expires: Option<DateTime<Utc>>,
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "full",
derive(TS, Queryable, Selectable, Associations, Identifiable)
)]
#[cfg_attr(
feature = "full",
diesel(belongs_to(crate::source::instance::Instance))
)]
#[cfg_attr(feature = "full", diesel(table_name = admin_block_instance))]
#[cfg_attr(feature = "full", diesel(primary_key(instance_id)))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", ts(export))]
pub struct AdminBlockInstance {
pub id: i32,
pub instance_id: InstanceId,
pub admin_person_id: PersonId,
pub reason: Option<String>,
pub expires: Option<DateTime<Utc>>,
pub published: DateTime<Utc>,
}
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = admin_block_instance))]
pub struct AdminBlockInstanceForm {
pub instance_id: InstanceId,
pub admin_person_id: PersonId,
pub reason: Option<String>, pub reason: Option<String>,
pub expires: Option<DateTime<Utc>>, pub expires: Option<DateTime<Utc>>,
} }

View file

@ -11,8 +11,8 @@ use diesel::{
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::PersonId, newtypes::PersonId,
schema::{federation_blocklist, instance, person}, schema::{admin_block_instance, instance, person},
utils::{functions::coalesce, get_conn, limit_and_offset, DbPool}, utils::{get_conn, limit_and_offset, DbPool},
}; };
impl AdminBlockInstanceView { impl AdminBlockInstanceView {
@ -23,34 +23,29 @@ impl AdminBlockInstanceView {
let show_mod_names = !params.hide_modlog_names; let show_mod_names = !params.hide_modlog_names;
let show_mod_names_expr = show_mod_names.as_sql::<diesel::sql_types::Bool>(); let show_mod_names_expr = show_mod_names.as_sql::<diesel::sql_types::Bool>();
let admin_names_join = coalesce(federation_blocklist::admin_person_id, 0) let admin_names_join = admin_block_instance::admin_person_id
.eq(person::id) .eq(person::id)
.and(show_mod_names_expr.or(person::id.eq(admin_person_id_join))); .and(show_mod_names_expr.or(person::id.eq(admin_person_id_join)));
let mut query = federation_blocklist::table let mut query = admin_block_instance::table
.left_join(person::table.on(admin_names_join)) .left_join(person::table.on(admin_names_join))
.inner_join(instance::table) .inner_join(instance::table)
.select(( .select((
federation_blocklist::all_columns, admin_block_instance::all_columns,
instance::all_columns, instance::all_columns,
person::all_columns.nullable(), person::all_columns.nullable(),
)) ))
.into_boxed(); .into_boxed();
if let Some(admin_person_id) = params.mod_person_id { if let Some(admin_person_id) = params.mod_person_id {
query = query.filter(federation_blocklist::admin_person_id.eq(admin_person_id)); query = query.filter(admin_block_instance::admin_person_id.eq(admin_person_id));
}; };
// If a post or comment ID is given, then don't find any results
if params.post_id.is_some() || params.comment_id.is_some() {
return Ok(vec![]);
}
let (limit, offset) = limit_and_offset(params.page, params.limit)?; let (limit, offset) = limit_and_offset(params.page, params.limit)?;
query query
.limit(limit) .limit(limit)
.offset(offset) .offset(offset)
.order_by(federation_blocklist::published.desc()) .order_by(admin_block_instance::published.desc())
.load::<AdminBlockInstanceView>(conn) .load::<AdminBlockInstanceView>(conn)
.await .await
} }

View file

@ -5,7 +5,7 @@ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::Comment,
community::Community, community::Community,
federation_blocklist::FederationBlockList, federation_blocklist::AdminBlockInstance,
instance::Instance, instance::Instance,
moderator::{ moderator::{
AdminPurgeComment, AdminPurgeComment,
@ -242,7 +242,7 @@ pub struct AdminPurgePostView {
#[cfg_attr(feature = "full", ts(export))] #[cfg_attr(feature = "full", ts(export))]
/// When an admin purges a post. /// When an admin purges a post.
pub struct AdminBlockInstanceView { pub struct AdminBlockInstanceView {
pub blocklist_entry: FederationBlockList, pub admin_block_instance: AdminBlockInstance,
pub instance: Instance, pub instance: Instance,
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub admin: Option<Person>, pub admin: Option<Person>,

View file

@ -1,3 +1,5 @@
alter table federation_blocklist drop column reason; ALTER TABLE federation_blocklist
alter table federation_blocklist drop column expires; DROP block_expires;
alter table federation_blocklist drop column admin_person_id;
DROP TABLE admin_block_instance;

View file

@ -1,3 +1,12 @@
alter table federation_blocklist add column admin_person_id int REFERENCES person(id) ON UPDATE CASCADE ON DELETE CASCADE; ALTER TABLE federation_blocklist
alter table federation_blocklist add column reason text; ADD COLUMN block_expires timestamptz;
alter table federation_blocklist add column expires timestamptz;
CREATE TABLE admin_block_instance (
id serial PRIMARY KEY,
instance_id int NOT NULL REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE,
admin_person_id int NOT NULL REFERENCES person (id) ON UPDATE CASCADE ON DELETE CASCADE,
reason text,
expires timestamptz,
published timestamptz NOT NULL DEFAULT now()
);

View file

@ -76,7 +76,6 @@ use lemmy_api::{
resolve::resolve_pm_report, resolve::resolve_pm_report,
}, },
site::{ site::{
block::block_instance,
federated_instances::get_federated_instances, federated_instances::get_federated_instances,
leave_admin::leave_admin, leave_admin::leave_admin,
list_all_media::list_all_media, list_all_media::list_all_media,
@ -93,6 +92,7 @@ use lemmy_api::{
list::list_registration_applications, list::list_registration_applications,
unread_count::get_unread_registration_application_count, unread_count::get_unread_registration_application_count,
}, },
user_block_instance::user_block_instance,
}, },
sitemap::get_sitemap, sitemap::get_sitemap,
}; };
@ -171,7 +171,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
// Admin Actions // Admin Actions
.route("", web::post().to(create_site)) .route("", web::post().to(create_site))
.route("", web::put().to(update_site)) .route("", web::put().to(update_site))
.route("/block", web::post().to(block_instance)), .route("/block", web::post().to(user_block_instance)),
) )
.service( .service(
web::resource("/modlog") web::resource("/modlog")

View file

@ -19,7 +19,16 @@ use lemmy_api_common::{
use lemmy_api_crud::post::create::send_webmention; use lemmy_api_crud::post::create::send_webmention;
use lemmy_db_schema::{ use lemmy_db_schema::{
schema::{ schema::{
captcha_answer, comment, community, community_actions, federation_blocklist, instance, person, post, received_activity, sent_activity captcha_answer,
comment,
community,
community_actions,
federation_blocklist,
instance,
person,
post,
received_activity,
sent_activity,
}, },
source::{ source::{
community::Community, community::Community,
@ -432,9 +441,9 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) {
.inspect_err(|e| error!("Failed to remove community_ban expired rows: {e}")) .inspect_err(|e| error!("Failed to remove community_ban expired rows: {e}"))
.ok(); .ok();
// TODO: need separate table for modlog, otherwise entry will disappear
diesel::delete( diesel::delete(
federation_blocklist::table.filter(federation_blocklist::expires.lt(now().nullable())), federation_blocklist::table
.filter(federation_blocklist::block_expires.lt(now().nullable())),
) )
.execute(&mut conn) .execute(&mut conn)
.await .await