feat(labrinth): check privacy route (#7195)

* feat(labrinth): check privacy endpoint

* refactor(labrinth): remove duplicated code
This commit is contained in:
Sychic
2026-08-17 22:09:50 +00:00
committed by GitHub
parent 46d07163cd
commit 39c0951ecb
4 changed files with 105 additions and 15 deletions
@@ -16,6 +16,16 @@ pub struct UserPreferences {
pub social: SocialPreferences,
}
impl UserPreferences {
pub fn resolve(overrides: Option<PartialUserPreferences>) -> Self {
let mut preferences = Self::default();
if let Some(overrides) = overrides {
overrides.apply_to(&mut preferences);
}
preferences
}
}
#[derive(
Debug, Serialize, Deserialize, ToSchema, Default, PartialEq, Component,
)]
+3
View File
@@ -15,6 +15,7 @@ pub mod medal;
pub mod moderation;
pub mod mural;
pub mod pats;
pub mod privacy;
pub mod search;
pub mod server_ping;
pub mod session;
@@ -32,6 +33,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.wrap(default_cors())
.configure(admin::config)
.configure(blocked_users::config)
.configure(privacy::config)
.configure(session::config)
.configure(flows::config)
.configure(pats::config)
@@ -68,6 +70,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
paths(
admin::count_download,
blocked_users::block_status,
privacy::invite_privacy_status,
admin::force_reindex,
admin::force_reindex_project,
session::list,
@@ -0,0 +1,89 @@
use crate::database::PgPool;
use crate::database::models::blocked_user_item::DBBlockedUser;
use crate::database::models::friend_item::DBFriend;
use crate::database::models::user_item::DBUser;
use crate::database::models::user_preferences_item::DBUserPreferences;
use crate::models::v3::preferences::{InvitePrivacy, UserPreferences};
use crate::routes::ApiError;
use crate::util::error::Context as _;
use crate::util::guards::admin_key_guard;
use actix_web::{get, web};
use serde::Serialize;
use xredis::RedisPool;
pub fn config(cfg: &mut web::ServiceConfig) {
cfg.service(invite_privacy_status);
}
#[derive(Serialize, utoipa::ToSchema)]
pub struct InvitePrivacyStatus {
pub allowed: bool,
}
#[derive(strum::EnumString)]
#[strum(serialize_all = "snake_case")]
enum InviteType {
SharedInstances,
HostingAccess,
}
/// Check whether `user_id` may send `target_id` an invite of the given type.
#[utoipa::path(tag = "privacy", responses((status = OK, body = InvitePrivacyStatus)))]
#[get(
"/privacy/{invite_type}/{user_id}/{target_id}",
guard = "admin_key_guard"
)]
pub async fn invite_privacy_status(
info: web::Path<(String, String, String)>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
) -> Result<web::Json<InvitePrivacyStatus>, ApiError> {
let (invite_type, user_id, target_id) = info.into_inner();
let invite_type = invite_type
.parse::<InviteType>()
.wrap_request_err("invalid invite_type")?;
let (user, target) = futures::try_join!(
DBUser::get(&user_id, &**pool, &redis),
DBUser::get(&target_id, &**pool, &redis),
)
.wrap_internal_err("resolving user ids")?;
let user_id = user.wrap_not_found_err("user not found")?.id;
let target_id = target.wrap_not_found_err("target not found")?.id;
let blocked = DBBlockedUser::is_blocked(target_id, user_id, &**pool)
.await
.wrap_internal_err("checking whether user is blocked")?;
let allowed = if blocked {
false
} else {
let overrides = DBUserPreferences::get(target_id, &**pool)
.await
.wrap_internal_err("fetching user preferences")?;
let preferences = UserPreferences::resolve(overrides);
let privacy = match invite_type {
InviteType::SharedInstances => {
preferences.social.shared_instances_privacy
}
InviteType::HostingAccess => {
preferences.social.hosting_access_privacy
}
};
match privacy {
InvitePrivacy::Everyone => true,
InvitePrivacy::None => false,
InvitePrivacy::Friends => {
DBFriend::get_friend(user_id, target_id, &**pool)
.await
.wrap_internal_err("checking friendship status")?
.is_some_and(|friend| friend.accepted)
}
}
};
Ok(web::Json(InvitePrivacyStatus { allowed }))
}
+3 -15
View File
@@ -409,15 +409,7 @@ pub async fn get_user_preferences(
.await
.wrap_internal_err("failed to fetch user preferences")?;
let preferences = preference_overrides
.map(|overrides| {
let mut preferences = UserPreferences::default();
overrides.apply_to(&mut preferences);
preferences
})
.unwrap_or_default();
Ok(web::Json(preferences))
Ok(web::Json(UserPreferences::resolve(preference_overrides)))
}
#[utoipa::path(
@@ -466,10 +458,7 @@ pub async fn edit_user_preferences(
.await
.wrap_internal_err("failed to fetch user preferences")?;
let mut preferences = UserPreferences::default();
if let Some(stored) = stored {
stored.apply_to(&mut preferences);
}
let mut preferences = UserPreferences::resolve(stored);
body.into_inner().apply_to(&mut preferences);
let overrides = preferences.into_diff_from(&UserPreferences::default());
@@ -481,8 +470,7 @@ pub async fn edit_user_preferences(
.await
.wrap_internal_err("committing database transaction")?;
let mut preferences = UserPreferences::default();
overrides.apply_to(&mut preferences);
let preferences = UserPreferences::resolve(Some(overrides));
Ok(web::Json(preferences))
}