use crate::{
diesel::dsl::IntervalDsl,
source::instance::{
Instance,
InstanceActions,
InstanceBanForm,
InstanceCommunitiesBlockForm,
InstanceForm,
InstancePersonsBlockForm,
},
traits::Bannable,
};
use chrono::Utc;
use diesel::{
ExpressionMethods,
NullableExpressionMethods,
OptionalExtension,
QueryDsl,
SelectableHelper,
dsl::{count_star, exists, insert_into, not, select},
};
use diesel_async::RunQueryDsl;
use diesel_uplete::{UpleteCount, uplete};
use lemmy_db_schema_file::{
InstanceId,
PersonId,
schema::{
federation_allowlist,
federation_blocklist,
federation_queue_state,
instance,
instance_actions,
},
};
use lemmy_diesel_utils::{
connection::{DbPool, get_conn},
utils::{
functions::{coalesce, lower},
now,
},
};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
impl Instance {
pub async fn read_or_create(pool: &mut DbPool<'_>, domain_: &str) -> LemmyResult<Self> {
use lemmy_db_schema_file::schema::instance::domain;
let conn = &mut get_conn(pool).await?;
let instance = instance::table
.filter(lower(domain).eq(&domain_.to_lowercase()))
.first(conn)
.await
.optional()?;
match instance {
Some(i) => Ok(i),
None => {
let form = InstanceForm {
updated_at: Some(Utc::now()),
..InstanceForm::new(domain_.to_string())
};
insert_into(instance::table)
.values(&form)
.on_conflict(instance::domain)
.do_update()
.set(&form)
.get_result::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::CouldntCreate)
}
}
}
pub async fn read(pool: &mut DbPool<'_>, instance_id: InstanceId) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
instance::table
.find(instance_id)
.first(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn update(
pool: &mut DbPool<'_>,
instance_id: InstanceId,
form: InstanceForm,
) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?;
diesel::update(instance::table.find(instance_id))
.set(form)
.execute(conn)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdate)
}
pub async fn delete(pool: &mut DbPool<'_>, instance_id: InstanceId) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?;
diesel::delete(instance::table.find(instance_id))
.execute(conn)
.await
.with_lemmy_type(LemmyErrorType::Deleted)
}
pub async fn read_all(pool: &mut DbPool<'_>) -> LemmyResult<Vec<Instance>> {
let conn = &mut get_conn(pool).await?;
instance::table
.select(Self::as_select())
.get_results(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn delete_all(pool: &mut DbPool<'_>) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?;
diesel::delete(federation_queue_state::table)
.execute(conn)
.await?;
diesel::delete(instance::table)
.execute(conn)
.await
.with_lemmy_type(LemmyErrorType::Deleted)
}
pub async fn allowlist(pool: &mut DbPool<'_>) -> LemmyResult<Vec<Self>> {
let conn = &mut get_conn(pool).await?;
instance::table
.inner_join(federation_allowlist::table)
.select(Self::as_select())
.get_results(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn blocklist(pool: &mut DbPool<'_>) -> LemmyResult<Vec<Self>> {
let conn = &mut get_conn(pool).await?;
instance::table
.inner_join(federation_blocklist::table)
.select(Self::as_select())
.get_results(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn read_federated_with_blocked_and_dead(
pool: &mut DbPool<'_>,
) -> LemmyResult<Vec<(Self, bool, bool)>> {
let conn = &mut get_conn(pool).await?;
let is_dead_expr = coalesce(instance::updated_at, instance::published_at).lt(now() - 3.days());
let use_allowlist = federation_allowlist::table
.select(count_star().gt(0))
.get_result::<bool>(conn)
.await?;
if use_allowlist {
instance::table
.left_join(federation_allowlist::table)
.select((
Self::as_select(),
federation_allowlist::instance_id.nullable().is_not_null(),
is_dead_expr,
))
.order_by(instance::id)
.get_results::<(Self, bool, bool)>(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
} else {
instance::table
.left_join(federation_blocklist::table)
.select((
Self::as_select(),
federation_blocklist::instance_id.nullable().is_null(),
is_dead_expr,
))
.order_by(instance::id)
.get_results::<(Self, bool, bool)>(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
}
}
impl InstanceActions {
pub async fn block_communities(
pool: &mut DbPool<'_>,
form: &InstanceCommunitiesBlockForm,
) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
insert_into(instance_actions::table)
.values(form)
.on_conflict((instance_actions::person_id, instance_actions::instance_id))
.do_update()
.set(form)
.returning(Self::as_select())
.get_result::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::AlreadyExists)
}
pub async fn unblock_communities(
pool: &mut DbPool<'_>,
form: &InstanceCommunitiesBlockForm,
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::blocked_communities_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::AlreadyExists)
}
pub async fn read_communities_block(
pool: &mut DbPool<'_>,
person_id: PersonId,
instance_id: InstanceId,
) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?;
let find_action = instance_actions::table
.find((person_id, instance_id))
.filter(instance_actions::blocked_communities_at.is_not_null());
select(not(exists(find_action)))
.get_result::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
}
pub async fn read_communities_block_for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> LemmyResult<Vec<Instance>> {
let conn = &mut get_conn(pool).await?;
instance_actions::table
.filter(instance_actions::blocked_communities_at.is_not_null())
.inner_join(instance::table)
.select(instance::all_columns)
.filter(instance_actions::person_id.eq(person_id))
.order_by(instance_actions::blocked_communities_at)
.load::<Instance>(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn block_persons(
pool: &mut DbPool<'_>,
form: &InstancePersonsBlockForm,
) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
insert_into(instance_actions::table)
.values(form)
.on_conflict((instance_actions::person_id, instance_actions::instance_id))
.do_update()
.set(form)
.returning(Self::as_select())
.get_result::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::AlreadyExists)
}
pub async fn unblock_persons(
pool: &mut DbPool<'_>,
form: &InstancePersonsBlockForm,
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::blocked_persons_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::AlreadyExists)
}
pub async fn read_persons_block(
pool: &mut DbPool<'_>,
person_id: PersonId,
instance_id: InstanceId,
) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?;
let find_action = instance_actions::table
.find((person_id, instance_id))
.filter(instance_actions::blocked_persons_at.is_not_null());
select(not(exists(find_action)))
.get_result::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
}
pub async fn read_persons_block_for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> LemmyResult<Vec<Instance>> {
let conn = &mut get_conn(pool).await?;
instance_actions::table
.filter(instance_actions::blocked_persons_at.is_not_null())
.inner_join(instance::table)
.select(instance::all_columns)
.filter(instance_actions::person_id.eq(person_id))
.order_by(instance_actions::blocked_persons_at)
.load::<Instance>(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn check_ban(
pool: &mut DbPool<'_>,
person_id: PersonId,
instance_id: InstanceId,
) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?;
let ban_exists = select(exists(
instance_actions::table
.filter(instance_actions::person_id.eq(person_id))
.filter(instance_actions::instance_id.eq(instance_id))
.filter(instance_actions::received_ban_at.is_not_null()),
))
.get_result::<bool>(conn)
.await?;
if ban_exists {
return Err(LemmyErrorType::SiteBan.into());
}
Ok(())
}
}
impl Bannable for InstanceActions {
type Form = InstanceBanForm;
async fn ban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
Ok(
insert_into(instance_actions::table)
.values(form)
.on_conflict((instance_actions::person_id, instance_actions::instance_id))
.do_update()
.set(form)
.returning(Self::as_select())
.get_result::<Self>(conn)
.await?,
)
}
async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
Ok(
uplete(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::received_ban_at)
.set_null(instance_actions::ban_expires_at)
.get_result(conn)
.await?,
)
}
}