Skip to content

Commit

Permalink
User can block instances (fixes #2397) (#3869)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
Nutomic and dessalines committed Sep 20, 2023
1 parent 89b7c98 commit 50f81cf
Show file tree
Hide file tree
Showing 24 changed files with 441 additions and 11 deletions.
2 changes: 1 addition & 1 deletion api_tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
37 changes: 37 additions & 0 deletions api_tests/src/community.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
getComments,
createComment,
getCommunityByName,
blockInstance,
waitUntil,
delay,
} from "./shared";
Expand Down Expand Up @@ -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);
});
16 changes: 16 additions & 0 deletions api_tests/src/shared.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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));
}
Expand Down
8 changes: 4 additions & 4 deletions api_tests/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
41 changes: 41 additions & 0 deletions crates/api/src/site/block.rs
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions crates/api/src/site/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod block;
pub mod federated_instances;
pub mod leave_admin;
pub mod mod_log;
Expand Down
22 changes: 21 additions & 1 deletion crates/api_common/src/site.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -21,6 +21,7 @@ use lemmy_db_views_actor::structs::{
CommunityFollowerView,
CommunityModeratorView,
CommunityView,
InstanceBlockView,
PersonBlockView,
PersonView,
};
Expand Down Expand Up @@ -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>,
}
Expand Down Expand Up @@ -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,
}
6 changes: 6 additions & 0 deletions crates/api_crud/src/site/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use lemmy_db_views_actor::structs::{
CommunityBlockView,
CommunityFollowerView,
CommunityModeratorView,
InstanceBlockView,
PersonBlockView,
PersonView,
};
Expand Down Expand Up @@ -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
Expand All @@ -70,6 +75,7 @@ pub async fn get_site(
follows,
moderates,
community_blocks,
instance_blocks,
person_blocks,
discussion_languages,
})
Expand Down
3 changes: 2 additions & 1 deletion crates/db_schema/src/aggregates/structs.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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,
}
Expand Down
36 changes: 36 additions & 0 deletions crates/db_schema/src/impls/instance_block.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
1 change: 1 addition & 0 deletions crates/db_schema/src/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions crates/db_schema/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -712,6 +721,7 @@ diesel::table! {
community_id -> Int4,
creator_id -> Int4,
controversy_rank -> Float8,
instance_id -> Int4,
scaled_rank -> Float8,
}
}
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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,
Expand Down
26 changes: 26 additions & 0 deletions crates/db_schema/src/source/instance_block.rs
Original file line number Diff line number Diff line change
@@ -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,
}
1 change: 1 addition & 0 deletions crates/db_schema/src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions crates/db_views/src/comment_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use lemmy_db_schema::{
community_follower,
community_moderator,
community_person_ban,
instance_block,
local_user_language,
person,
person_block,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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());
Expand Down

0 comments on commit 50f81cf

Please sign in to comment.