138 lines
4.8 KiB
Rust
138 lines
4.8 KiB
Rust
use crate::model::{PackingContext, UserRelationship};
|
|
use either::Either;
|
|
use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum;
|
|
use magnetar_calckey_model::note_model::NoteVisibilityFilterFactory;
|
|
use magnetar_calckey_model::sea_orm::prelude::Expr;
|
|
use magnetar_calckey_model::sea_orm::sea_query::{Alias, IntoIden, PgFunc, Query, SimpleExpr};
|
|
use magnetar_calckey_model::sea_orm::{ColumnTrait, IntoSimpleExpr};
|
|
use magnetar_calckey_model::{ck, CalckeyDbError};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct NoteVisibilityFilterSimple(Option<String>);
|
|
|
|
impl NoteVisibilityFilterFactory for NoteVisibilityFilterSimple {
|
|
fn with_note_and_user_tables(
|
|
&self,
|
|
note_tbl: Option<Alias>,
|
|
user_tbl: Option<Alias>,
|
|
) -> SimpleExpr {
|
|
let note_tbl_name =
|
|
note_tbl.map_or_else(|| ck::note::Entity.into_iden(), |a| a.into_iden());
|
|
let user_tbl_name =
|
|
user_tbl.map_or_else(|| ck::user::Entity.into_iden(), |a| a.into_iden());
|
|
|
|
let note_visibility = Expr::col((note_tbl_name.clone(), ck::note::Column::Visibility));
|
|
let note_mentions = Expr::col((note_tbl_name.clone(), ck::note::Column::Mentions));
|
|
let note_reply_user_id = Expr::col((note_tbl_name.clone(), ck::note::Column::ReplyUserId));
|
|
let note_visible_user_ids =
|
|
Expr::col((note_tbl_name.clone(), ck::note::Column::VisibleUserIds));
|
|
let note_user_id = Expr::col((user_tbl_name, ck::note::Column::UserId));
|
|
|
|
let is_public = note_visibility
|
|
.clone()
|
|
.eq(NoteVisibilityEnum::Public)
|
|
.or(note_visibility.clone().eq(NoteVisibilityEnum::Home));
|
|
|
|
let Some(user_id_str) = &self.0 else {
|
|
return is_public;
|
|
};
|
|
|
|
let self_user_id = SimpleExpr::Constant(user_id_str.into());
|
|
|
|
let is_self = note_user_id.clone().eq(self_user_id.clone());
|
|
|
|
let is_visible_specified = {
|
|
let either_specified_or_followers = note_visibility
|
|
.clone()
|
|
.eq(NoteVisibilityEnum::Specified)
|
|
.or(note_visibility.clone().eq(NoteVisibilityEnum::Followers))
|
|
.into_simple_expr();
|
|
|
|
let mentioned_or_specified = self_user_id
|
|
.clone()
|
|
.eq(PgFunc::any(note_mentions.into_simple_expr()))
|
|
.or(self_user_id.eq(PgFunc::any(note_visible_user_ids)));
|
|
|
|
either_specified_or_followers.and(mentioned_or_specified)
|
|
};
|
|
|
|
let is_visible_followers = {
|
|
note_visibility
|
|
.eq(NoteVisibilityEnum::Followers)
|
|
.and(
|
|
note_user_id.in_subquery(
|
|
Query::select()
|
|
.column(ck::following::Column::FolloweeId)
|
|
.from(ck::following::Entity)
|
|
.cond_where(ck::following::Column::FollowerId.eq(user_id_str))
|
|
.to_owned(),
|
|
),
|
|
)
|
|
.or(note_reply_user_id.eq(user_id_str))
|
|
};
|
|
|
|
is_self
|
|
.or(is_public)
|
|
.or(is_visible_followers)
|
|
.or(is_visible_specified)
|
|
}
|
|
}
|
|
|
|
pub struct NoteVisibilityFilterModel;
|
|
|
|
impl NoteVisibilityFilterModel {
|
|
pub async fn is_note_visible(
|
|
&self,
|
|
ctx: &PackingContext,
|
|
user: Option<&ck::user::Model>,
|
|
note: &ck::note::Model,
|
|
) -> Result<bool, CalckeyDbError> {
|
|
if user.is_some_and(|user| user.id == note.user_id) {
|
|
return Ok(true);
|
|
}
|
|
|
|
if matches!(
|
|
note.visibility,
|
|
NoteVisibilityEnum::Public | NoteVisibilityEnum::Home
|
|
) {
|
|
return Ok(true);
|
|
}
|
|
|
|
if matches!(
|
|
note.visibility,
|
|
NoteVisibilityEnum::Followers | NoteVisibilityEnum::Specified
|
|
) {
|
|
let Some(user) = user else {
|
|
return Ok(false);
|
|
};
|
|
|
|
if note.mentions.contains(&user.id) || note.visible_user_ids.contains(&user.id) {
|
|
return Ok(true);
|
|
}
|
|
|
|
if matches!(note.visibility, NoteVisibilityEnum::Specified) {
|
|
return Ok(false);
|
|
}
|
|
|
|
let following = ctx
|
|
.is_relationship_between(
|
|
Either::Right(user),
|
|
Either::Left(¬e.user_id),
|
|
UserRelationship::Follow,
|
|
)
|
|
.await?;
|
|
|
|
// The second condition generally will not happen in the API,
|
|
// however it allows some AP processing, with activities
|
|
// between two foreign objects
|
|
return Ok(following || user.host.is_some() && note.user_host.is_some());
|
|
}
|
|
|
|
return Ok(false);
|
|
}
|
|
|
|
pub fn new_note_visibility_filter(&self, user: Option<&str>) -> NoteVisibilityFilterSimple {
|
|
NoteVisibilityFilterSimple(user.map(str::to_string))
|
|
}
|
|
}
|