diff --git a/crates/api/src/site/admin_block_instance.rs b/crates/api/src/site/admin_block_instance.rs index d68d28519..81cfa62e6 100644 --- a/crates/api/src/site/admin_block_instance.rs +++ b/crates/api/src/site/admin_block_instance.rs @@ -2,33 +2,32 @@ use activitypub_federation::config::Data; use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, - site::{AdminBlockInstance, AdminBlockInstanceResponse}, + site::{AdminBlockInstanceParams, AdminBlockInstanceResponse}, 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_utils::error::LemmyResult; #[tracing::instrument(skip(context))] pub async fn block_instance( - data: Json, + data: Json, local_user_view: LocalUserView, context: Data, ) -> LemmyResult> { is_admin(&local_user_view)?; - let instance_block_form = FederationBlockListForm { + let instance_block_form = AdminBlockInstanceForm { 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(), expires: data.expires, - updated: None, }; if data.block { - FederationBlockList::block(&mut context.pool(), &instance_block_form).await?; + AdminBlockInstance::block(&mut context.pool(), &instance_block_form).await?; } else { - FederationBlockList::unblock(&mut context.pool(), &instance_block_form).await?; + AdminBlockInstance::unblock(&mut context.pool(), data.instance_id).await?; } Ok(Json(AdminBlockInstanceResponse { diff --git a/crates/api/src/site/mod.rs b/crates/api/src/site/mod.rs index 7c5b51cfb..9b8d8e3e9 100644 --- a/crates/api/src/site/mod.rs +++ b/crates/api/src/site/mod.rs @@ -1,8 +1,8 @@ pub mod admin_block_instance; -pub mod block; pub mod federated_instances; pub mod leave_admin; pub mod list_all_media; pub mod mod_log; pub mod purge; pub mod registration_applications; +pub mod user_block_instance; diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index d6aee8255..41a7e148b 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -7,10 +7,22 @@ use lemmy_api_common::{ use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType}; use lemmy_db_views::structs::LocalUserView; use lemmy_db_views_moderator::structs::{ - AdminBlockInstanceView, AdminPurgeCommentView, AdminPurgeCommunityView, AdminPurgePersonView, - AdminPurgePostView, ModAddCommunityView, ModAddView, - ModBanFromCommunityView, ModBanView, ModFeaturePostView, ModHideCommunityView, ModLockPostView, - ModRemoveCommentView, ModRemoveCommunityView, ModRemovePostView, ModTransferCommunityView, + AdminBlockInstanceView, + AdminPurgeCommentView, + AdminPurgeCommunityView, + AdminPurgePersonView, + AdminPurgePostView, + ModAddCommunityView, + ModAddView, + ModBanFromCommunityView, + ModBanView, + ModFeaturePostView, + ModHideCommunityView, + ModLockPostView, + ModRemoveCommentView, + ModRemoveCommunityView, + ModRemovePostView, + ModTransferCommunityView, ModlogListParams, }; use lemmy_utils::error::LemmyResult; diff --git a/crates/api/src/site/block.rs b/crates/api/src/site/user_block_instance.rs similarity index 97% rename from crates/api/src/site/block.rs rename to crates/api/src/site/user_block_instance.rs index 823dda612..00722d460 100644 --- a/crates/api/src/site/block.rs +++ b/crates/api/src/site/user_block_instance.rs @@ -12,7 +12,7 @@ use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] -pub async fn block_instance( +pub async fn user_block_instance( data: Json, local_user_view: LocalUserView, context: Data, diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 502a24e6a..db32f9edd 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -2,7 +2,13 @@ use crate::federate_retry_sleep_duration; use chrono::{DateTime, Utc}; use lemmy_db_schema::{ newtypes::{ - CommentId, CommunityId, InstanceId, LanguageId, PersonId, PostId, RegistrationApplicationId, + CommentId, + CommunityId, + InstanceId, + LanguageId, + PersonId, + PostId, + RegistrationApplicationId, }, source::{ community::Community, @@ -14,20 +20,45 @@ use lemmy_db_schema::{ person::Person, tagline::Tagline, }, - CommentSortType, FederationMode, ListingType, ModlogActionType, PostListingMode, PostSortType, - RegistrationMode, SearchType, + CommentSortType, + FederationMode, + ListingType, + ModlogActionType, + PostListingMode, + PostSortType, + RegistrationMode, + SearchType, }; use lemmy_db_views::structs::{ - CommentView, LocalUserView, PostView, RegistrationApplicationView, SiteView, + CommentView, + LocalUserView, + PostView, + RegistrationApplicationView, + SiteView, }; use lemmy_db_views_actor::structs::{ - CommunityFollowerView, CommunityModeratorView, CommunityView, PersonView, + CommunityFollowerView, + CommunityModeratorView, + CommunityView, + PersonView, }; use lemmy_db_views_moderator::structs::{ - AdminBlockInstanceView, AdminPurgeCommentView, AdminPurgeCommunityView, AdminPurgePersonView, - AdminPurgePostView, ModAddCommunityView, ModAddView, ModBanFromCommunityView, ModBanView, - ModFeaturePostView, ModHideCommunityView, ModLockPostView, ModRemoveCommentView, - ModRemoveCommunityView, ModRemovePostView, ModTransferCommunityView, + AdminBlockInstanceView, + AdminPurgeCommentView, + AdminPurgeCommunityView, + AdminPurgePersonView, + AdminPurgePostView, + ModAddCommunityView, + ModAddView, + ModBanFromCommunityView, + ModBanView, + ModFeaturePostView, + ModHideCommunityView, + ModLockPostView, + ModRemoveCommentView, + ModRemoveCommunityView, + ModRemovePostView, + ModTransferCommunityView, }; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -635,7 +666,7 @@ pub struct BlockInstanceResponse { #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] -pub struct AdminBlockInstance { +pub struct AdminBlockInstanceParams { pub instance_id: InstanceId, pub block: bool, pub reason: Option, @@ -647,4 +678,4 @@ pub struct AdminBlockInstance { #[cfg_attr(feature = "full", ts(export))] pub struct AdminBlockInstanceResponse { pub blocked: bool, -} \ No newline at end of file +} diff --git a/crates/db_schema/src/impls/federation_blocklist.rs b/crates/db_schema/src/impls/federation_blocklist.rs index e5c7cfcbf..3751c19c5 100644 --- a/crates/db_schema/src/impls/federation_blocklist.rs +++ b/crates/db_schema/src/impls/federation_blocklist.rs @@ -1,30 +1,49 @@ use crate::{ - schema::federation_blocklist, - source::federation_blocklist::{FederationBlockList, FederationBlockListForm}, + newtypes::InstanceId, + schema::{admin_block_instance, federation_blocklist}, + source::federation_blocklist::{ + AdminBlockInstance, + AdminBlockInstanceForm, + FederationBlockListForm, + }, utils::{get_conn, DbPool}, }; -use diesel::{delete, ExpressionMethods, QueryDsl}; -use diesel::{dsl::insert_into, result::Error}; +use diesel::{delete, dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; -impl FederationBlockList { - pub async fn block(pool: &mut DbPool<'_>, form: &FederationBlockListForm) -> Result { +impl AdminBlockInstance { + pub async fn block(pool: &mut DbPool<'_>, form: &AdminBlockInstanceForm) -> Result<(), Error> { let conn = &mut get_conn(pool).await?; - insert_into(federation_blocklist::table) - .values(form) - .get_result::(conn) + conn + .build_transaction() + .run(|conn| { + Box::pin(async move { + insert_into(admin_block_instance::table) + .values(form) + .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 } - pub async fn unblock( - pool: &mut DbPool<'_>, - form: &FederationBlockListForm, - ) -> Result { + pub async fn unblock(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<(), Error> { let conn = &mut get_conn(pool).await?; delete( - federation_blocklist::table - .filter(federation_blocklist::dsl::instance_id.eq(form.instance_id)), + federation_blocklist::table.filter(federation_blocklist::dsl::instance_id.eq(instance_id)), ) - .get_result(conn) - .await + .execute(conn) + .await?; + Ok(()) } } diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 0562765fa..4dd6db601 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -42,6 +42,17 @@ pub mod sql_types { pub struct RegistrationModeEnum; } +diesel::table! { + admin_block_instance (id) { + id -> Int4, + instance_id -> Int4, + admin_person_id -> Int4, + reason -> Nullable, + expires -> Nullable, + published -> Timestamptz, + } +} + diesel::table! { admin_purge_comment (id) { id -> Int4, @@ -282,9 +293,7 @@ diesel::table! { instance_id -> Int4, published -> Timestamptz, updated -> Nullable, - admin_person_id -> Nullable, - reason -> Nullable, - expires -> Nullable, + block_expires -> Nullable, } } @@ -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 -> post (post_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!(federation_allowlist -> 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!(instance_actions -> instance (instance_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::allow_tables_to_appear_in_same_query!( + admin_block_instance, admin_purge_comment, admin_purge_community, admin_purge_person, diff --git a/crates/db_schema/src/source/federation_blocklist.rs b/crates/db_schema/src/source/federation_blocklist.rs index 367a97089..165db8ee0 100644 --- a/crates/db_schema/src/source/federation_blocklist.rs +++ b/crates/db_schema/src/source/federation_blocklist.rs @@ -1,6 +1,6 @@ use crate::newtypes::{InstanceId, PersonId}; #[cfg(feature = "full")] -use crate::schema::federation_blocklist; +use crate::schema::{admin_block_instance, federation_blocklist}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; @@ -23,10 +23,7 @@ pub struct FederationBlockList { pub instance_id: InstanceId, pub published: DateTime, pub updated: Option>, - // TODO: would be good to make this mandatory but value doesnt exist for old entries - pub admin_person_id: Option, - pub reason: Option, - pub expires: Option>, + pub block_expires: Option>, } #[derive(Clone, Default)] @@ -35,7 +32,37 @@ pub struct FederationBlockList { pub struct FederationBlockListForm { pub instance_id: InstanceId, pub updated: Option>, - pub admin_person_id: Option, + pub block_expires: Option>, +} + +#[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, + pub expires: Option>, + pub published: DateTime, +} + +#[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, pub expires: Option>, } diff --git a/crates/db_views_moderator/src/admin_block_instance.rs b/crates/db_views_moderator/src/admin_block_instance.rs index 909cb6de6..e0398e703 100644 --- a/crates/db_views_moderator/src/admin_block_instance.rs +++ b/crates/db_views_moderator/src/admin_block_instance.rs @@ -11,8 +11,8 @@ use diesel::{ use diesel_async::RunQueryDsl; use lemmy_db_schema::{ newtypes::PersonId, - schema::{federation_blocklist, instance, person}, - utils::{functions::coalesce, get_conn, limit_and_offset, DbPool}, + schema::{admin_block_instance, instance, person}, + utils::{get_conn, limit_and_offset, DbPool}, }; impl AdminBlockInstanceView { @@ -23,34 +23,29 @@ impl AdminBlockInstanceView { let show_mod_names = !params.hide_modlog_names; let show_mod_names_expr = show_mod_names.as_sql::(); - let admin_names_join = coalesce(federation_blocklist::admin_person_id, 0) + let admin_names_join = admin_block_instance::admin_person_id .eq(person::id) .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)) .inner_join(instance::table) .select(( - federation_blocklist::all_columns, + admin_block_instance::all_columns, instance::all_columns, person::all_columns.nullable(), )) .into_boxed(); 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)?; query .limit(limit) .offset(offset) - .order_by(federation_blocklist::published.desc()) + .order_by(admin_block_instance::published.desc()) .load::(conn) .await } diff --git a/crates/db_views_moderator/src/structs.rs b/crates/db_views_moderator/src/structs.rs index 06177bfaa..eeffb8a22 100644 --- a/crates/db_views_moderator/src/structs.rs +++ b/crates/db_views_moderator/src/structs.rs @@ -5,7 +5,7 @@ use lemmy_db_schema::{ source::{ comment::Comment, community::Community, - federation_blocklist::FederationBlockList, + federation_blocklist::AdminBlockInstance, instance::Instance, moderator::{ AdminPurgeComment, @@ -242,7 +242,7 @@ pub struct AdminPurgePostView { #[cfg_attr(feature = "full", ts(export))] /// When an admin purges a post. pub struct AdminBlockInstanceView { - pub blocklist_entry: FederationBlockList, + pub admin_block_instance: AdminBlockInstance, pub instance: Instance, #[cfg_attr(feature = "full", ts(optional))] pub admin: Option, diff --git a/migrations/2024-11-19-142005_instance-block-mod-log/down.sql b/migrations/2024-11-19-142005_instance-block-mod-log/down.sql index 7444ade78..98ab9e650 100644 --- a/migrations/2024-11-19-142005_instance-block-mod-log/down.sql +++ b/migrations/2024-11-19-142005_instance-block-mod-log/down.sql @@ -1,3 +1,5 @@ -alter table federation_blocklist drop column reason; -alter table federation_blocklist drop column expires; -alter table federation_blocklist drop column admin_person_id; \ No newline at end of file +ALTER TABLE federation_blocklist + DROP block_expires; + +DROP TABLE admin_block_instance; + diff --git a/migrations/2024-11-19-142005_instance-block-mod-log/up.sql b/migrations/2024-11-19-142005_instance-block-mod-log/up.sql index b8dca983c..44d6bcbe2 100644 --- a/migrations/2024-11-19-142005_instance-block-mod-log/up.sql +++ b/migrations/2024-11-19-142005_instance-block-mod-log/up.sql @@ -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 add column reason text; -alter table federation_blocklist add column expires timestamptz; \ No newline at end of file +ALTER TABLE federation_blocklist + ADD COLUMN block_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() +); + diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index 2f431419c..9b6daea81 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -76,7 +76,6 @@ use lemmy_api::{ resolve::resolve_pm_report, }, site::{ - block::block_instance, federated_instances::get_federated_instances, leave_admin::leave_admin, list_all_media::list_all_media, @@ -93,6 +92,7 @@ use lemmy_api::{ list::list_registration_applications, unread_count::get_unread_registration_application_count, }, + user_block_instance::user_block_instance, }, sitemap::get_sitemap, }; @@ -171,7 +171,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { // Admin Actions .route("", web::post().to(create_site)) .route("", web::put().to(update_site)) - .route("/block", web::post().to(block_instance)), + .route("/block", web::post().to(user_block_instance)), ) .service( web::resource("/modlog") diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs index 8a370f99f..05dc99b15 100644 --- a/src/scheduled_tasks.rs +++ b/src/scheduled_tasks.rs @@ -19,7 +19,16 @@ use lemmy_api_common::{ use lemmy_api_crud::post::create::send_webmention; use lemmy_db_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::{ 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}")) .ok(); - // TODO: need separate table for modlog, otherwise entry will disappear 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) .await