mirror of https://github.com/LemmyNet/lemmy
* 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>pull/3968/head^2
parent
89b7c981f5
commit
50f81cf157
@ -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,
|
||||||
|
}))
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
$$;
|
||||||
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue