mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
* User can block instances (fixes #2397) * update comments * review comments * use route * update * add api test * update tests * fix * fix test * ci --------- Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
This commit is contained in:
parent
89b7c981f5
commit
50f81cf157
@ -19,7 +19,7 @@
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.19.0-rc.3",
|
||||
"lemmy-js-client": "0.19.0-rc.5",
|
||||
"prettier": "^3.0.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"typescript": "^5.0.4"
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
getComments,
|
||||
createComment,
|
||||
getCommunityByName,
|
||||
blockInstance,
|
||||
waitUntil,
|
||||
delay,
|
||||
} from "./shared";
|
||||
@ -348,3 +349,39 @@ test("Get community for different casing on domain", async () => {
|
||||
.community_view;
|
||||
assertCommunityFederation(betaCommunity, communityRes.community_view);
|
||||
});
|
||||
|
||||
test("User blocks instance, communities are hidden", async () => {
|
||||
// create community and post on beta
|
||||
let communityRes = await createCommunity(beta);
|
||||
expect(communityRes.community_view.community.name).toBeDefined();
|
||||
let postRes = await createPost(
|
||||
beta,
|
||||
communityRes.community_view.community.id,
|
||||
);
|
||||
expect(postRes.post_view.post.id).toBeDefined();
|
||||
|
||||
// fetch post to alpha
|
||||
let alphaPost = await resolvePost(alpha, postRes.post_view.post);
|
||||
expect(alphaPost.post?.post).toBeDefined();
|
||||
|
||||
// post should be included in listing
|
||||
let listing = await getPosts(alpha, "All");
|
||||
let listing_ids = listing.posts.map(p => p.post.ap_id);
|
||||
expect(listing_ids).toContain(postRes.post_view.post.ap_id);
|
||||
|
||||
// block the beta instance
|
||||
await blockInstance(alpha, alphaPost.post!.community.instance_id, true);
|
||||
|
||||
// after blocking, post should not be in listing
|
||||
let listing2 = await getPosts(alpha, "All");
|
||||
let listing_ids2 = listing2.posts.map(p => p.post.ap_id);
|
||||
expect(listing_ids2.indexOf(postRes.post_view.post.ap_id)).toBe(-1);
|
||||
|
||||
// unblock instance again
|
||||
await blockInstance(alpha, alphaPost.post!.community.instance_id, false);
|
||||
|
||||
// post should be included in listing
|
||||
let listing3 = await getPosts(alpha, "All");
|
||||
let listing_ids3 = listing3.posts.map(p => p.post.ap_id);
|
||||
expect(listing_ids3).toContain(postRes.post_view.post.ap_id);
|
||||
});
|
||||
|
@ -1,8 +1,11 @@
|
||||
import {
|
||||
BlockInstance,
|
||||
BlockInstanceResponse,
|
||||
GetReplies,
|
||||
GetRepliesResponse,
|
||||
GetUnreadCount,
|
||||
GetUnreadCountResponse,
|
||||
InstanceId,
|
||||
LemmyHttp,
|
||||
} from "lemmy-js-client";
|
||||
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
|
||||
@ -817,6 +820,19 @@ export function getPosts(
|
||||
return api.client.getPosts(form);
|
||||
}
|
||||
|
||||
export function blockInstance(
|
||||
api: API,
|
||||
instance_id: InstanceId,
|
||||
block: boolean,
|
||||
): Promise<BlockInstanceResponse> {
|
||||
let form: BlockInstance = {
|
||||
instance_id,
|
||||
block,
|
||||
auth: api.auth,
|
||||
};
|
||||
return api.client.blockInstance(form);
|
||||
}
|
||||
|
||||
export function delay(millis = 500) {
|
||||
return new Promise(resolve => setTimeout(resolve, millis));
|
||||
}
|
||||
|
@ -2174,10 +2174,10 @@ kleur@^3.0.3:
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||
|
||||
lemmy-js-client@0.19.0-rc.3:
|
||||
version "0.19.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.3.tgz#1efbfd5ce492319227a41cb020fc1cf9b2e7c075"
|
||||
integrity sha512-RmibQ3+YTvqsQ89II2I29pfPmVAWiSObGAU9Nc/AGYfyvaCya7f5+TirKwHdKA2eWDWLOTnD4rm6WgcgAwvhWw==
|
||||
lemmy-js-client@0.19.0-rc.5:
|
||||
version "0.19.0-rc.5"
|
||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.5.tgz#a0d3e0ac829b1e46124edcf3a0343ae04317d51a"
|
||||
integrity sha512-Z1T95Ht1VZNvWlLH9XpVnO2oC7LhMT81jTiU5BhYPIkEtGhOwN91jk5uI1oyI6/d4v9lwbrsyzFYPsiuMmXTFQ==
|
||||
dependencies:
|
||||
cross-fetch "^3.1.5"
|
||||
form-data "^4.0.0"
|
||||
|
41
crates/api/src/site/block.rs
Normal file
41
crates/api/src/site/block.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use activitypub_federation::config::Data;
|
||||
use actix_web::web::Json;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::{BlockInstance, BlockInstanceResponse},
|
||||
utils::local_user_view_from_jwt,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
traits::Blockable,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn block_instance(
|
||||
data: Json<BlockInstance>,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<Json<BlockInstanceResponse>, LemmyError> {
|
||||
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||
|
||||
let instance_id = data.instance_id;
|
||||
let person_id = local_user_view.person.id;
|
||||
let instance_block_form = InstanceBlockForm {
|
||||
person_id,
|
||||
instance_id,
|
||||
};
|
||||
|
||||
if data.block {
|
||||
InstanceBlock::block(&mut context.pool(), &instance_block_form)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?;
|
||||
} else {
|
||||
InstanceBlock::unblock(&mut context.pool(), &instance_block_form)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?;
|
||||
}
|
||||
|
||||
Ok(Json(BlockInstanceResponse {
|
||||
blocked: data.block,
|
||||
}))
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod block;
|
||||
pub mod federated_instances;
|
||||
pub mod leave_admin;
|
||||
pub mod mod_log;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::sensitive::Sensitive;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommentId, CommunityId, LanguageId, PersonId, PostId},
|
||||
newtypes::{CommentId, CommunityId, InstanceId, LanguageId, PersonId, PostId},
|
||||
source::{instance::Instance, language::Language, tagline::Tagline},
|
||||
ListingType,
|
||||
ModlogActionType,
|
||||
@ -21,6 +21,7 @@ use lemmy_db_views_actor::structs::{
|
||||
CommunityFollowerView,
|
||||
CommunityModeratorView,
|
||||
CommunityView,
|
||||
InstanceBlockView,
|
||||
PersonBlockView,
|
||||
PersonView,
|
||||
};
|
||||
@ -320,6 +321,7 @@ pub struct MyUserInfo {
|
||||
pub follows: Vec<CommunityFollowerView>,
|
||||
pub moderates: Vec<CommunityModeratorView>,
|
||||
pub community_blocks: Vec<CommunityBlockView>,
|
||||
pub instance_blocks: Vec<InstanceBlockView>,
|
||||
pub person_blocks: Vec<PersonBlockView>,
|
||||
pub discussion_languages: Vec<LanguageId>,
|
||||
}
|
||||
@ -450,3 +452,21 @@ pub struct GetUnreadRegistrationApplicationCount {
|
||||
pub struct GetUnreadRegistrationApplicationCountResponse {
|
||||
pub registration_applications: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Block an instance as user
|
||||
pub struct BlockInstance {
|
||||
pub instance_id: InstanceId,
|
||||
pub block: bool,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct BlockInstanceResponse {
|
||||
pub blocked: bool,
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use lemmy_db_views_actor::structs::{
|
||||
CommunityBlockView,
|
||||
CommunityFollowerView,
|
||||
CommunityModeratorView,
|
||||
InstanceBlockView,
|
||||
PersonBlockView,
|
||||
PersonView,
|
||||
};
|
||||
@ -52,6 +53,10 @@ pub async fn get_site(
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
|
||||
|
||||
let instance_blocks = InstanceBlockView::for_person(&mut context.pool(), person_id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let person_blocks = PersonBlockView::for_person(&mut context.pool(), person_id)
|
||||
.await
|
||||
@ -70,6 +75,7 @@ pub async fn get_site(
|
||||
follows,
|
||||
moderates,
|
||||
community_blocks,
|
||||
instance_blocks,
|
||||
person_blocks,
|
||||
discussion_languages,
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::newtypes::{CommentId, CommunityId, PersonId, PostId, SiteId};
|
||||
use crate::newtypes::{CommentId, CommunityId, InstanceId, PersonId, PostId, SiteId};
|
||||
#[cfg(feature = "full")]
|
||||
use crate::schema::{
|
||||
comment_aggregates,
|
||||
@ -100,6 +100,7 @@ pub struct PostAggregates {
|
||||
pub community_id: CommunityId,
|
||||
pub creator_id: PersonId,
|
||||
pub controversy_rank: f64,
|
||||
pub instance_id: InstanceId,
|
||||
/// A rank that amplifies smaller communities
|
||||
pub scaled_rank: f64,
|
||||
}
|
||||
|
36
crates/db_schema/src/impls/instance_block.rs
Normal file
36
crates/db_schema/src/impls/instance_block.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use crate::{
|
||||
schema::instance_block::dsl::{instance_block, instance_id, person_id},
|
||||
source::instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
traits::Blockable,
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
#[async_trait]
|
||||
impl Blockable for InstanceBlock {
|
||||
type Form = InstanceBlockForm;
|
||||
async fn block(pool: &mut DbPool<'_>, instance_block_form: &Self::Form) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(instance_block)
|
||||
.values(instance_block_form)
|
||||
.on_conflict((person_id, instance_id))
|
||||
.do_update()
|
||||
.set(instance_block_form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
async fn unblock(
|
||||
pool: &mut DbPool<'_>,
|
||||
instance_block_form: &Self::Form,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::delete(
|
||||
instance_block
|
||||
.filter(person_id.eq(instance_block_form.person_id))
|
||||
.filter(instance_id.eq(instance_block_form.instance_id)),
|
||||
)
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ pub mod federation_allowlist;
|
||||
pub mod federation_blocklist;
|
||||
pub mod image_upload;
|
||||
pub mod instance;
|
||||
pub mod instance_block;
|
||||
pub mod language;
|
||||
pub mod local_site;
|
||||
pub mod local_site_rate_limit;
|
||||
|
@ -337,6 +337,15 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
instance_block (id) {
|
||||
id -> Int4,
|
||||
person_id -> Int4,
|
||||
instance_id -> Int4,
|
||||
published -> Timestamptz,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
language (id) {
|
||||
id -> Int4,
|
||||
@ -712,6 +721,7 @@ diesel::table! {
|
||||
community_id -> Int4,
|
||||
creator_id -> Int4,
|
||||
controversy_rank -> Float8,
|
||||
instance_id -> Int4,
|
||||
scaled_rank -> Float8,
|
||||
}
|
||||
}
|
||||
@ -928,6 +938,8 @@ diesel::joinable!(federation_allowlist -> instance (instance_id));
|
||||
diesel::joinable!(federation_blocklist -> instance (instance_id));
|
||||
diesel::joinable!(federation_queue_state -> instance (instance_id));
|
||||
diesel::joinable!(image_upload -> local_user (local_user_id));
|
||||
diesel::joinable!(instance_block -> instance (instance_id));
|
||||
diesel::joinable!(instance_block -> person (person_id));
|
||||
diesel::joinable!(local_site -> site (site_id));
|
||||
diesel::joinable!(local_site_rate_limit -> local_site (local_site_id));
|
||||
diesel::joinable!(local_user -> person (person_id));
|
||||
@ -960,6 +972,7 @@ diesel::joinable!(post -> community (community_id));
|
||||
diesel::joinable!(post -> language (language_id));
|
||||
diesel::joinable!(post -> person (creator_id));
|
||||
diesel::joinable!(post_aggregates -> community (community_id));
|
||||
diesel::joinable!(post_aggregates -> instance (instance_id));
|
||||
diesel::joinable!(post_aggregates -> person (creator_id));
|
||||
diesel::joinable!(post_aggregates -> post (post_id));
|
||||
diesel::joinable!(post_like -> person (person_id));
|
||||
@ -1005,6 +1018,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||
federation_queue_state,
|
||||
image_upload,
|
||||
instance,
|
||||
instance_block,
|
||||
language,
|
||||
local_site,
|
||||
local_site_rate_limit,
|
||||
|
26
crates/db_schema/src/source/instance_block.rs
Normal file
26
crates/db_schema/src/source/instance_block.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::newtypes::{InstanceId, PersonId};
|
||||
#[cfg(feature = "full")]
|
||||
use crate::schema::instance_block;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::instance::Instance))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = instance_block))]
|
||||
pub struct InstanceBlock {
|
||||
pub id: i32,
|
||||
pub person_id: PersonId,
|
||||
pub instance_id: InstanceId,
|
||||
pub published: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = instance_block))]
|
||||
pub struct InstanceBlockForm {
|
||||
pub person_id: PersonId,
|
||||
pub instance_id: InstanceId,
|
||||
}
|
@ -17,6 +17,7 @@ pub mod federation_allowlist;
|
||||
pub mod federation_blocklist;
|
||||
pub mod image_upload;
|
||||
pub mod instance;
|
||||
pub mod instance_block;
|
||||
pub mod language;
|
||||
pub mod local_site;
|
||||
pub mod local_site_rate_limit;
|
||||
|
@ -23,6 +23,7 @@ use lemmy_db_schema::{
|
||||
community_follower,
|
||||
community_moderator,
|
||||
community_person_ban,
|
||||
instance_block,
|
||||
local_user_language,
|
||||
person,
|
||||
person_block,
|
||||
@ -121,6 +122,13 @@ fn queries<'a>() -> Queries<
|
||||
let local_user_id_join = local_user_id.unwrap_or(LocalUserId(-1));
|
||||
|
||||
let mut query = all_joins(comment::table.into_boxed(), person_id)
|
||||
.left_join(
|
||||
instance_block::table.on(
|
||||
community::instance_id
|
||||
.eq(instance_block::instance_id)
|
||||
.and(instance_block::person_id.eq(person_id_join)),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
community_block::table.on(
|
||||
community::id
|
||||
@ -221,6 +229,7 @@ fn queries<'a>() -> Queries<
|
||||
|
||||
// Don't show blocked communities or persons
|
||||
if options.post_id.is_none() {
|
||||
query = query.filter(instance_block::person_id.is_null());
|
||||
query = query.filter(community_block::person_id.is_null());
|
||||
}
|
||||
query = query.filter(person_block::person_id.is_null());
|
||||
|
@ -28,6 +28,7 @@ use lemmy_db_schema::{
|
||||
community_follower,
|
||||
community_moderator,
|
||||
community_person_ban,
|
||||
instance_block,
|
||||
local_user_language,
|
||||
person,
|
||||
person_block,
|
||||
@ -420,7 +421,7 @@ fn queries<'a>() -> Queries<
|
||||
),
|
||||
));
|
||||
|
||||
// Don't show blocked communities or persons
|
||||
// Don't show blocked instances, communities or persons
|
||||
query = query.filter(not(exists(
|
||||
community_block::table.filter(
|
||||
post_aggregates::community_id
|
||||
@ -428,6 +429,13 @@ fn queries<'a>() -> Queries<
|
||||
.and(community_block::person_id.eq(person_id_join)),
|
||||
),
|
||||
)));
|
||||
query = query.filter(not(exists(
|
||||
instance_block::table.filter(
|
||||
post_aggregates::instance_id
|
||||
.eq(instance_block::instance_id)
|
||||
.and(instance_block::person_id.eq(person_id_join)),
|
||||
),
|
||||
)));
|
||||
query = query.filter(not(is_creator_blocked(person_id)));
|
||||
}
|
||||
let now = diesel::dsl::now.into_sql::<Timestamptz>();
|
||||
@ -706,6 +714,7 @@ mod tests {
|
||||
community::{Community, CommunityInsertForm},
|
||||
community_block::{CommunityBlock, CommunityBlockForm},
|
||||
instance::Instance,
|
||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
language::Language,
|
||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
@ -1239,6 +1248,84 @@ mod tests {
|
||||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn post_listing_instance_block() {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await;
|
||||
|
||||
let blocked_instance = Instance::read_or_create(pool, "another_domain.tld".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let community_form = CommunityInsertForm::builder()
|
||||
.name("test_community_4".to_string())
|
||||
.title("none".to_owned())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(blocked_instance.id)
|
||||
.build();
|
||||
let inserted_community = Community::create(pool, &community_form).await.unwrap();
|
||||
|
||||
let post_form = PostInsertForm::builder()
|
||||
.name("blocked instance post".to_string())
|
||||
.creator_id(data.inserted_bot.id)
|
||||
.community_id(inserted_community.id)
|
||||
.language_id(Some(LanguageId(1)))
|
||||
.build();
|
||||
|
||||
let post_from_blocked_instance = Post::create(pool, &post_form).await.unwrap();
|
||||
|
||||
// no instance block, should return all posts
|
||||
let post_listings_all = PostQuery {
|
||||
local_user: Some(&data.local_user_view),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(post_listings_all.len(), 3);
|
||||
|
||||
// block the instance
|
||||
let block_form = InstanceBlockForm {
|
||||
person_id: data.local_user_view.person.id,
|
||||
instance_id: blocked_instance.id,
|
||||
};
|
||||
InstanceBlock::block(pool, &block_form).await.unwrap();
|
||||
|
||||
// now posts from communities on that instance should be hidden
|
||||
let post_listings_blocked = PostQuery {
|
||||
local_user: Some(&data.local_user_view),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(post_listings_blocked.len(), 2);
|
||||
assert_ne!(
|
||||
post_listings_blocked[0].post.id,
|
||||
post_from_blocked_instance.id
|
||||
);
|
||||
assert_ne!(
|
||||
post_listings_blocked[1].post.id,
|
||||
post_from_blocked_instance.id
|
||||
);
|
||||
|
||||
// after unblocking it should return all posts again
|
||||
InstanceBlock::unblock(pool, &block_form).await.unwrap();
|
||||
let post_listings_blocked = PostQuery {
|
||||
local_user: Some(&data.local_user_view),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(post_listings_blocked.len(), 3);
|
||||
|
||||
Instance::delete(pool, blocked_instance.id).await.unwrap();
|
||||
cleanup(data, pool).await;
|
||||
}
|
||||
|
||||
async fn cleanup(data: Data, pool: &mut DbPool<'_>) {
|
||||
let num_deleted = Post::delete(pool, data.inserted_post.id).await.unwrap();
|
||||
Community::delete(pool, data.inserted_community.id)
|
||||
@ -1359,6 +1446,7 @@ mod tests {
|
||||
scaled_rank: 0.3621,
|
||||
community_id: inserted_post.community_id,
|
||||
creator_id: inserted_post.creator_id,
|
||||
instance_id: data.inserted_instance.id,
|
||||
},
|
||||
subscribed: SubscribedType::NotSubscribed,
|
||||
read: false,
|
||||
|
@ -12,7 +12,14 @@ use diesel::{
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommunityId, PersonId},
|
||||
schema::{community, community_aggregates, community_block, community_follower, local_user},
|
||||
schema::{
|
||||
community,
|
||||
community_aggregates,
|
||||
community_block,
|
||||
community_follower,
|
||||
instance_block,
|
||||
local_user,
|
||||
},
|
||||
source::{community::CommunityFollower, local_user::LocalUser},
|
||||
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||
ListingType,
|
||||
@ -36,6 +43,13 @@ fn queries<'a>() -> Queries<
|
||||
.and(community_follower::person_id.eq(person_id_join)),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
instance_block::table.on(
|
||||
community::instance_id
|
||||
.eq(instance_block::instance_id)
|
||||
.and(instance_block::person_id.eq(person_id_join)),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
community_block::table.on(
|
||||
community::id
|
||||
@ -131,8 +145,10 @@ fn queries<'a>() -> Queries<
|
||||
};
|
||||
}
|
||||
|
||||
// Don't show blocked communities or nsfw communities if not enabled in profile
|
||||
// Don't show blocked communities and communities on blocked instances. nsfw communities are
|
||||
// also hidden (based on profile setting)
|
||||
if options.local_user.is_some() {
|
||||
query = query.filter(instance_block::person_id.is_null());
|
||||
query = query.filter(community_block::person_id.is_null());
|
||||
query = query.filter(community::nsfw.eq(false).or(local_user::show_nsfw.eq(true)));
|
||||
} else {
|
||||
|
27
crates/db_views_actor/src/instance_block_view.rs
Normal file
27
crates/db_views_actor/src/instance_block_view.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::structs::InstanceBlockView;
|
||||
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::PersonId,
|
||||
schema::{instance, instance_block, person, site},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
|
||||
impl InstanceBlockView {
|
||||
pub async fn for_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
instance_block::table
|
||||
.inner_join(person::table)
|
||||
.inner_join(instance::table)
|
||||
.left_join(site::table.on(site::instance_id.eq(instance::id)))
|
||||
.select((
|
||||
person::all_columns,
|
||||
instance::all_columns,
|
||||
site::all_columns.nullable(),
|
||||
))
|
||||
.filter(instance_block::person_id.eq(person_id))
|
||||
.order_by(instance_block::published)
|
||||
.load::<InstanceBlockView>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ pub mod community_person_ban_view;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod community_view;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod instance_block_view;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod person_block_view;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod person_mention_view;
|
||||
|
@ -6,9 +6,11 @@ use lemmy_db_schema::{
|
||||
comment::Comment,
|
||||
comment_reply::CommentReply,
|
||||
community::Community,
|
||||
instance::Instance,
|
||||
person::Person,
|
||||
person_mention::PersonMention,
|
||||
post::Post,
|
||||
site::Site,
|
||||
},
|
||||
SubscribedType,
|
||||
};
|
||||
@ -26,6 +28,17 @@ pub struct CommunityBlockView {
|
||||
pub community: Community,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// An instance block by a user.
|
||||
pub struct InstanceBlockView {
|
||||
pub person: Person,
|
||||
pub instance: Instance,
|
||||
pub site: Option<Site>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
|
@ -209,6 +209,7 @@ pub enum LemmyErrorType {
|
||||
InvalidUrlScheme,
|
||||
CouldntSendWebmention,
|
||||
ContradictingFilters,
|
||||
InstanceBlockAlreadyExists,
|
||||
AuthCookieInsecure,
|
||||
Unknown(String),
|
||||
}
|
||||
|
21
migrations/2023-08-09-101305_user_instance_block/down.sql
Normal file
21
migrations/2023-08-09-101305_user_instance_block/down.sql
Normal file
@ -0,0 +1,21 @@
|
||||
DROP TABLE instance_block;
|
||||
|
||||
ALTER TABLE post_aggregates
|
||||
DROP COLUMN instance_id;
|
||||
|
||||
CREATE OR REPLACE FUNCTION post_aggregates_post ()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
IF (TG_OP = 'INSERT') THEN
|
||||
INSERT INTO post_aggregates (post_id, published, newest_comment_time, newest_comment_time_necro, community_id, creator_id)
|
||||
VALUES (NEW.id, NEW.published, NEW.published, NEW.published, NEW.community_id, NEW.creator_id);
|
||||
ELSIF (TG_OP = 'DELETE') THEN
|
||||
DELETE FROM post_aggregates
|
||||
WHERE post_id = OLD.id;
|
||||
END IF;
|
||||
RETURN NULL;
|
||||
END
|
||||
$$;
|
||||
|
51
migrations/2023-08-09-101305_user_instance_block/up.sql
Normal file
51
migrations/2023-08-09-101305_user_instance_block/up.sql
Normal file
@ -0,0 +1,51 @@
|
||||
CREATE TABLE instance_block (
|
||||
id serial PRIMARY KEY,
|
||||
person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||
instance_id int REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||
published timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (person_id, instance_id)
|
||||
);
|
||||
|
||||
ALTER TABLE post_aggregates
|
||||
ADD COLUMN instance_id integer REFERENCES instance ON UPDATE CASCADE ON DELETE CASCADE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION post_aggregates_post ()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
IF (TG_OP = 'INSERT') THEN
|
||||
INSERT INTO post_aggregates (post_id, published, newest_comment_time, newest_comment_time_necro, community_id, creator_id, instance_id)
|
||||
SELECT
|
||||
NEW.id,
|
||||
NEW.published,
|
||||
NEW.published,
|
||||
NEW.published,
|
||||
NEW.community_id,
|
||||
NEW.creator_id,
|
||||
community.instance_id
|
||||
FROM
|
||||
community
|
||||
WHERE
|
||||
NEW.community_id = community.id;
|
||||
ELSIF (TG_OP = 'DELETE') THEN
|
||||
DELETE FROM post_aggregates
|
||||
WHERE post_id = OLD.id;
|
||||
END IF;
|
||||
RETURN NULL;
|
||||
END
|
||||
$$;
|
||||
|
||||
UPDATE
|
||||
post_aggregates
|
||||
SET
|
||||
instance_id = community.instance_id
|
||||
FROM
|
||||
post
|
||||
JOIN community ON post.community_id = community.id
|
||||
WHERE
|
||||
post.id = post_aggregates.post_id;
|
||||
|
||||
ALTER TABLE post_aggregates
|
||||
ALTER COLUMN instance_id SET NOT NULL;
|
||||
|
@ -56,6 +56,7 @@ use lemmy_api::{
|
||||
resolve::resolve_pm_report,
|
||||
},
|
||||
site::{
|
||||
block::block_instance,
|
||||
federated_instances::get_federated_instances,
|
||||
leave_admin::leave_admin,
|
||||
mod_log::get_mod_log,
|
||||
@ -129,7 +130,8 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||
.route("", web::get().to(get_site))
|
||||
// Admin Actions
|
||||
.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)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/modlog")
|
||||
|
Loading…
Reference in New Issue
Block a user