mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 13:36:59 +00:00
refactor(quark): port member_edit, member_experimental_query, member_fetch_all, member_remove, permissions_set_default, permissions_set
#283
This commit is contained in:
@@ -5,6 +5,8 @@ use crate::{events::client::EventV1, Database, File, RatelimitEvent};
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::seq::SliceRandom;
|
||||
use revolt_config::config;
|
||||
use revolt_models::v0;
|
||||
use revolt_presence::filter_online;
|
||||
use revolt_result::{create_error, Error, ErrorType, Result};
|
||||
use ulid::Ulid;
|
||||
|
||||
@@ -264,7 +266,27 @@ impl User {
|
||||
Ok(username)
|
||||
}
|
||||
|
||||
// Find a free discriminator for a given username
|
||||
/// Helper function to fetch many users as a mutually connected user
|
||||
/// (while optimising the online ID query)
|
||||
pub async fn fetch_many_ids_as_mutuals(
|
||||
db: &Database,
|
||||
perspective: &User,
|
||||
ids: &[String],
|
||||
) -> Result<Vec<v0::User>> {
|
||||
let online_ids = filter_online(ids).await;
|
||||
|
||||
Ok(db
|
||||
.fetch_users(ids)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|user| {
|
||||
let is_online = online_ids.contains(&user.id);
|
||||
user.into_known(perspective, is_online)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Find a free discriminator for a given username
|
||||
pub async fn find_discriminator(
|
||||
db: &Database,
|
||||
username: &str,
|
||||
|
||||
@@ -500,6 +500,17 @@ impl From<crate::FieldsMember> for FieldsMember {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FieldsMember> for crate::FieldsMember {
|
||||
fn from(value: FieldsMember) -> crate::FieldsMember {
|
||||
match value {
|
||||
FieldsMember::Avatar => crate::FieldsMember::Avatar,
|
||||
FieldsMember::Nickname => crate::FieldsMember::Nickname,
|
||||
FieldsMember::Roles => crate::FieldsMember::Roles,
|
||||
FieldsMember::Timeout => crate::FieldsMember::Timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::RemovalIntention> for RemovalIntention {
|
||||
fn from(value: crate::RemovalIntention) -> Self {
|
||||
match value {
|
||||
@@ -702,7 +713,8 @@ impl crate::User {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn into_known<'a, P>(self, perspective: P) -> User
|
||||
/// Convert user object into user model assuming mutual connection
|
||||
pub fn into_known<'a, P>(self, perspective: P, is_online: bool) -> User
|
||||
where
|
||||
P: Into<Option<&'a crate::User>>,
|
||||
{
|
||||
@@ -725,10 +737,7 @@ impl crate::User {
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
// TODO: refactor all of this to be less code? (as in with the method above)
|
||||
// TODO: also not sure if this is the best solution, but we don't want to
|
||||
// TODO: fetch mutual information again here
|
||||
let can_see_profile = relationship == RelationshipStatus::Friend;
|
||||
let can_see_profile = relationship != RelationshipStatus::BlockedOther;
|
||||
(relationship, can_see_profile)
|
||||
}
|
||||
} else {
|
||||
@@ -768,7 +777,7 @@ impl crate::User {
|
||||
privileged: self.privileged,
|
||||
bot: self.bot.map(|bot| bot.into()),
|
||||
relationship,
|
||||
online: can_see_profile && revolt_presence::is_online(&self.id).await,
|
||||
online: can_see_profile && is_online,
|
||||
id: self.id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
use super::File;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{File, Role, User};
|
||||
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
#[cfg(feature = "validator")]
|
||||
use validator::Validate;
|
||||
|
||||
#[cfg(feature = "rocket")]
|
||||
use rocket::FromForm;
|
||||
|
||||
/// Regex for valid role colours
|
||||
///
|
||||
/// Allows the use of named colours, rgb(a), variables and all gradients.
|
||||
@@ -77,4 +85,46 @@ auto_derived!(
|
||||
Kick,
|
||||
Ban,
|
||||
}
|
||||
|
||||
/// Member response
|
||||
#[serde(untagged)]
|
||||
pub enum MemberResponse {
|
||||
Member(Member),
|
||||
MemberWithRoles {
|
||||
member: Member,
|
||||
roles: HashMap<String, Role>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Options for fetching all members
|
||||
#[cfg_attr(feature = "rocket", derive(FromForm))]
|
||||
pub struct OptionsFetchAllMembers {
|
||||
/// Whether to exclude offline users
|
||||
pub exclude_offline: Option<bool>,
|
||||
}
|
||||
|
||||
/// Response with all members
|
||||
pub struct AllMemberResponse {
|
||||
/// List of members
|
||||
pub members: Vec<Member>,
|
||||
/// List of users
|
||||
pub users: Vec<User>,
|
||||
}
|
||||
|
||||
/// New member information
|
||||
#[cfg_attr(feature = "validator", derive(Validate))]
|
||||
pub struct DataMemberEdit {
|
||||
/// Member nickname
|
||||
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||
pub nickname: Option<String>,
|
||||
/// Attachment Id to set for avatar
|
||||
pub avatar: Option<String>,
|
||||
/// Array of role ids
|
||||
pub roles: Option<Vec<String>>,
|
||||
/// Timestamp this member is timed out until
|
||||
pub timeout: Option<Timestamp>,
|
||||
/// Fields to remove from channel object
|
||||
#[cfg_attr(feature = "validator", validate(length(min = 1)))]
|
||||
pub remove: Option<Vec<FieldsMember>>,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{Channel, File};
|
||||
|
||||
use revolt_permissions::OverrideField;
|
||||
use revolt_permissions::{Override, OverrideField};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(feature = "validator")]
|
||||
@@ -160,6 +160,12 @@ auto_derived!(
|
||||
pub nsfw: Option<bool>,
|
||||
}
|
||||
|
||||
/// New role permissions
|
||||
pub struct DataSetServerRolePermission {
|
||||
/// Allow / deny values for the role in this server.
|
||||
pub permissions: Override,
|
||||
}
|
||||
|
||||
/// Information returned when creating server
|
||||
pub struct CreateServerLegacyResponse {
|
||||
/// Server object
|
||||
|
||||
@@ -28,13 +28,7 @@ pub async fn fetch_members(
|
||||
|
||||
if let Channel::Group { recipients, .. } = channel {
|
||||
Ok(Json(
|
||||
join_all(
|
||||
db.fetch_users(&recipients)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|other_user| other_user.into_known(&user)),
|
||||
)
|
||||
.await,
|
||||
User::fetch_many_ids_as_mutuals(db, &user, &recipients).await?,
|
||||
))
|
||||
} else {
|
||||
Err(create_error!(InvalidOperation))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use futures::future::join_all;
|
||||
use revolt_database::{util::reference::Reference, Channel, Database, Invite, Member, User};
|
||||
use revolt_models::v0::{self, InviteJoinResponse};
|
||||
use revolt_result::{create_error, Result};
|
||||
@@ -37,13 +36,7 @@ pub async fn join(
|
||||
channel.add_user_to_group(db, &user, creator).await?;
|
||||
if let Channel::Group { recipients, .. } = &channel {
|
||||
Ok(Json(InviteJoinResponse::Group {
|
||||
users: join_all(
|
||||
db.fetch_users(&recipients)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|other_user| other_user.into_known(&user)),
|
||||
)
|
||||
.await,
|
||||
users: User::fetch_many_ids_as_mutuals(db, &user, &recipients).await?,
|
||||
channel: channel.into(),
|
||||
}))
|
||||
} else {
|
||||
|
||||
@@ -1,69 +1,55 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use revolt_quark::{
|
||||
models::{
|
||||
server_member::{FieldsMember, PartialMember},
|
||||
File, Member, User,
|
||||
},
|
||||
perms, Db, Error, Permission, Ref, Result, Timestamp,
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, File, PartialMember, User,
|
||||
};
|
||||
use revolt_models::v0;
|
||||
|
||||
use rocket::serde::json::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
use validator::Validate;
|
||||
|
||||
/// # Member Data
|
||||
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct DataMemberEdit {
|
||||
/// Member nickname
|
||||
#[validate(length(min = 1, max = 32))]
|
||||
nickname: Option<String>,
|
||||
/// Attachment Id to set for avatar
|
||||
avatar: Option<String>,
|
||||
/// Array of role ids
|
||||
roles: Option<Vec<String>>,
|
||||
/// Timestamp this member is timed out until
|
||||
timeout: Option<Timestamp>,
|
||||
/// Fields to remove from channel object
|
||||
#[validate(length(min = 1))]
|
||||
remove: Option<Vec<FieldsMember>>,
|
||||
}
|
||||
|
||||
/// # Edit Member
|
||||
///
|
||||
/// Edit a member by their id.
|
||||
#[openapi(tag = "Server Members")]
|
||||
#[patch("/<server>/members/<target>", data = "<data>")]
|
||||
pub async fn req(
|
||||
db: &Db,
|
||||
pub async fn edit(
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
server: Ref,
|
||||
target: Ref,
|
||||
data: Json<DataMemberEdit>,
|
||||
) -> Result<Json<Member>> {
|
||||
server: Reference,
|
||||
target: Reference,
|
||||
data: Json<v0::DataMemberEdit>,
|
||||
) -> Result<Json<v0::Member>> {
|
||||
let data = data.into_inner();
|
||||
data.validate()
|
||||
.map_err(|error| Error::FailedValidation { error })?;
|
||||
data.validate().map_err(|error| {
|
||||
create_error!(FailedValidation {
|
||||
error: error.to_string()
|
||||
})
|
||||
})?;
|
||||
|
||||
// Fetch server, target member and current permissions
|
||||
let mut server = server.as_server(db).await?;
|
||||
let mut member = target.as_member(db, &server.id).await?;
|
||||
let mut permissions = perms(&user).server(&server);
|
||||
let mut query = DatabasePermissionQuery::new(db, &user)
|
||||
.server(&server)
|
||||
.member(&member);
|
||||
let permissions = calculate_server_permissions(&mut query).await;
|
||||
|
||||
// Check permissions in server
|
||||
let mut required = vec![];
|
||||
|
||||
if data.nickname.is_some()
|
||||
|| data
|
||||
.remove
|
||||
.as_ref()
|
||||
.map(|x| x.contains(&FieldsMember::Nickname))
|
||||
.map(|x| x.contains(&v0::FieldsMember::Nickname))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if user.id == member.id.user {
|
||||
required.push(Permission::ChangeNickname);
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::ChangeNickname)?;
|
||||
} else {
|
||||
required.push(Permission::ManageNicknames);
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageNicknames)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,13 +57,13 @@ pub async fn req(
|
||||
|| data
|
||||
.remove
|
||||
.as_ref()
|
||||
.map(|x| x.contains(&FieldsMember::Avatar))
|
||||
.map(|x| x.contains(&v0::FieldsMember::Avatar))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if user.id == member.id.user {
|
||||
required.push(Permission::ChangeAvatar);
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::ChangeAvatar)?;
|
||||
} else {
|
||||
return Err(Error::InvalidOperation);
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,34 +71,30 @@ pub async fn req(
|
||||
|| data
|
||||
.remove
|
||||
.as_ref()
|
||||
.map(|x| x.contains(&FieldsMember::Roles))
|
||||
.map(|x| x.contains(&v0::FieldsMember::Roles))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
required.push(Permission::AssignRoles);
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::AssignRoles)?;
|
||||
}
|
||||
|
||||
if data.timeout.is_some()
|
||||
|| data
|
||||
.remove
|
||||
.as_ref()
|
||||
.map(|x| x.contains(&FieldsMember::Timeout))
|
||||
.map(|x| x.contains(&v0::FieldsMember::Timeout))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
required.push(Permission::TimeoutMembers);
|
||||
}
|
||||
|
||||
for permission in required {
|
||||
permissions.throw_permission(db, permission).await?;
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::TimeoutMembers)?;
|
||||
}
|
||||
|
||||
// Resolve our ranking
|
||||
let our_ranking = permissions.get_member_rank().unwrap_or(i64::MIN);
|
||||
let our_ranking = query.get_member_rank().unwrap_or(i64::MIN);
|
||||
|
||||
// Check that we have permissions to act against this member
|
||||
if member.id.user != user.id
|
||||
&& member.get_ranking(permissions.server.get().unwrap()) <= our_ranking
|
||||
&& member.get_ranking(query.server_ref().as_ref().unwrap()) <= our_ranking
|
||||
{
|
||||
return Err(Error::NotElevated);
|
||||
return Err(create_error!(NotElevated));
|
||||
}
|
||||
|
||||
// Check permissions against roles in diff
|
||||
@@ -125,16 +107,16 @@ pub async fn req(
|
||||
for role_id in added_roles {
|
||||
if let Some(role) = server.roles.remove(*role_id) {
|
||||
if role.rank <= our_ranking {
|
||||
return Err(Error::NotElevated);
|
||||
return Err(create_error!(NotElevated));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::InvalidRole);
|
||||
return Err(create_error!(InvalidRole));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply edits to the member object
|
||||
let DataMemberEdit {
|
||||
let v0::DataMemberEdit {
|
||||
nickname,
|
||||
avatar,
|
||||
roles,
|
||||
@@ -151,7 +133,7 @@ pub async fn req(
|
||||
|
||||
// 1. Remove fields from object
|
||||
if let Some(fields) = &remove {
|
||||
if fields.contains(&FieldsMember::Avatar) {
|
||||
if fields.contains(&v0::FieldsMember::Avatar) {
|
||||
if let Some(avatar) = &member.avatar {
|
||||
db.mark_attachment_as_deleted(&avatar.id).await?;
|
||||
}
|
||||
@@ -164,8 +146,14 @@ pub async fn req(
|
||||
}
|
||||
|
||||
member
|
||||
.update(db, partial, remove.unwrap_or_default())
|
||||
.update(
|
||||
db,
|
||||
partial,
|
||||
remove
|
||||
.map(|v| v.into_iter().map(Into::into).collect())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(member))
|
||||
Ok(Json(member.into()))
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use revolt_quark::{
|
||||
models::{Member, User},
|
||||
perms, Db, Error, Ref, Result,
|
||||
use futures::future::join_all;
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, Member, User,
|
||||
};
|
||||
|
||||
use rocket::serde::json::Json;
|
||||
use revolt_models::v0;
|
||||
use revolt_permissions::PermissionQuery;
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # Query Parameters
|
||||
@@ -20,9 +23,9 @@ pub struct OptionsQueryMembers {
|
||||
#[derive(Serialize, JsonSchema)]
|
||||
pub struct MemberQueryResponse {
|
||||
/// List of members
|
||||
members: Vec<Member>,
|
||||
members: Vec<v0::Member>,
|
||||
/// List of users
|
||||
users: Vec<User>,
|
||||
users: Vec<v0::User>,
|
||||
}
|
||||
|
||||
/// # Query members by name
|
||||
@@ -31,17 +34,20 @@ pub struct MemberQueryResponse {
|
||||
#[openapi(tag = "Server Members")]
|
||||
#[get("/<target>/members_experimental_query?<options..>")]
|
||||
pub async fn member_experimental_query(
|
||||
db: &Db,
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
target: Ref,
|
||||
target: Reference,
|
||||
options: OptionsQueryMembers,
|
||||
) -> Result<Json<MemberQueryResponse>> {
|
||||
if !options.experimental_api {
|
||||
return Err(Error::InternalError);
|
||||
return Err(create_error!(InternalError));
|
||||
}
|
||||
|
||||
let server = target.as_server(db).await?;
|
||||
perms(&user).server(&server).calc(db).await?;
|
||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||
if !query.are_we_a_member().await {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
let mut members = db.fetch_all_members(&server.id).await?;
|
||||
|
||||
@@ -50,7 +56,7 @@ pub async fn member_experimental_query(
|
||||
user_ids.push(member.id.user.clone());
|
||||
}
|
||||
|
||||
let mut users = User::fetch_foreign_users(db, &user_ids).await?;
|
||||
let mut users = db.fetch_users(&user_ids).await?;
|
||||
|
||||
// Ensure the lists match up exactly
|
||||
members.sort_by(|a, b| a.id.user.cmp(&b.id.user));
|
||||
@@ -76,7 +82,15 @@ pub async fn member_experimental_query(
|
||||
left.len().cmp(&right.len())
|
||||
});
|
||||
|
||||
// Take the first five and return them
|
||||
let (members, users) = zipped_vec.into_iter().take(10).unzip();
|
||||
Ok(Json(MemberQueryResponse { members, users }))
|
||||
// Take the first ten and return them
|
||||
let (members, users): (Vec<Member>, Vec<User>) = zipped_vec.into_iter().take(10).unzip();
|
||||
Ok(Json(MemberQueryResponse {
|
||||
members: members.into_iter().map(Into::into).collect(),
|
||||
users: join_all(
|
||||
users
|
||||
.into_iter()
|
||||
.map(|other_user| other_user.into(db, &user)),
|
||||
)
|
||||
.await,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,25 +1,42 @@
|
||||
use revolt_quark::models::server_member::MemberResponse;
|
||||
use revolt_quark::{models::User, perms, Db, Ref, Result};
|
||||
use rocket::serde::json::Json;
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, User,
|
||||
};
|
||||
use revolt_models::v0;
|
||||
use revolt_permissions::PermissionQuery;
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
/// # Fetch Member
|
||||
///
|
||||
/// Retrieve a member.
|
||||
#[openapi(tag = "Server Members")]
|
||||
#[get("/<target>/members/<member>?<roles>")]
|
||||
pub async fn req(
|
||||
db: &Db,
|
||||
pub async fn fetch(
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
target: Ref,
|
||||
member: Ref,
|
||||
target: Reference,
|
||||
member: Reference,
|
||||
roles: Option<bool>,
|
||||
) -> Result<Json<MemberResponse>> {
|
||||
) -> Result<Json<v0::MemberResponse>> {
|
||||
let server = target.as_server(db).await?;
|
||||
perms(&user).server(&server).calc(db).await?;
|
||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||
if !query.are_we_a_member().await {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
let member_response: MemberResponse = match roles {
|
||||
Some(true) => member.as_member_with_roles(db, &server.id).await?.into(),
|
||||
_ => member.as_member(db, &server.id).await?.into(),
|
||||
};
|
||||
|
||||
Ok(Json(member_response))
|
||||
let member = member.as_member(db, &server.id).await?;
|
||||
if let Some(true) = roles {
|
||||
Ok(Json(v0::MemberResponse::MemberWithRoles {
|
||||
roles: server
|
||||
.roles
|
||||
.into_iter()
|
||||
.filter(|(k, _)| member.roles.contains(k))
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect(),
|
||||
member: member.into(),
|
||||
}))
|
||||
} else {
|
||||
Ok(Json(v0::MemberResponse::Member(member.into())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,37 @@
|
||||
use revolt_quark::{
|
||||
models::{Member, User},
|
||||
perms, Db, Ref, Result,
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, User,
|
||||
};
|
||||
|
||||
use rocket::serde::json::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # Query Parameters
|
||||
#[derive(Deserialize, JsonSchema, FromForm)]
|
||||
pub struct OptionsFetchAllMembers {
|
||||
/// Whether to exclude offline users
|
||||
exclude_offline: Option<bool>,
|
||||
}
|
||||
|
||||
/// # Member List
|
||||
///
|
||||
/// Both lists are sorted by ID.
|
||||
#[derive(Serialize, JsonSchema)]
|
||||
pub struct AllMemberResponse {
|
||||
/// List of members
|
||||
members: Vec<Member>,
|
||||
/// List of users
|
||||
users: Vec<User>,
|
||||
}
|
||||
use revolt_models::v0;
|
||||
use revolt_permissions::PermissionQuery;
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
/// # Fetch Members
|
||||
///
|
||||
/// Fetch all server members.
|
||||
#[openapi(tag = "Server Members")]
|
||||
#[get("/<target>/members?<options..>")]
|
||||
pub async fn req(
|
||||
db: &Db,
|
||||
pub async fn fetch_all(
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
target: Ref,
|
||||
options: OptionsFetchAllMembers,
|
||||
) -> Result<Json<AllMemberResponse>> {
|
||||
target: Reference,
|
||||
options: v0::OptionsFetchAllMembers,
|
||||
) -> Result<Json<v0::AllMemberResponse>> {
|
||||
let server = target.as_server(db).await?;
|
||||
perms(&user).server(&server).calc(db).await?;
|
||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||
if !query.are_we_a_member().await {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
let mut members = db.fetch_all_members(&server.id).await?;
|
||||
|
||||
let mut user_ids = vec![];
|
||||
for member in &members {
|
||||
user_ids.push(member.id.user.clone());
|
||||
}
|
||||
let user_ids: Vec<String> = members
|
||||
.iter()
|
||||
.map(|member| member.id.user.clone())
|
||||
.collect();
|
||||
|
||||
let mut users = User::fetch_foreign_users(db, &user_ids).await?;
|
||||
let mut users = User::fetch_many_ids_as_mutuals(db, &user, &user_ids).await?;
|
||||
|
||||
// Ensure the lists match up exactly.
|
||||
members.sort_by(|a, b| a.id.user.cmp(&b.id.user));
|
||||
@@ -54,9 +40,12 @@ pub async fn req(
|
||||
// Optionally, remove all offline user entries.
|
||||
if let Some(true) = options.exclude_offline {
|
||||
let mut iter = users.iter();
|
||||
members.retain(|_| iter.next().unwrap().online.unwrap_or(false));
|
||||
users.retain(|user| user.online.unwrap_or(false));
|
||||
members.retain(|_| iter.next().unwrap().online);
|
||||
users.retain(|user| user.online);
|
||||
}
|
||||
|
||||
Ok(Json(AllMemberResponse { members, users }))
|
||||
Ok(Json(v0::AllMemberResponse {
|
||||
members: members.into_iter().map(Into::into).collect(),
|
||||
users,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,40 +1,47 @@
|
||||
use revolt_quark::{
|
||||
models::{server_member::RemovalIntention, User},
|
||||
perms, Db, EmptyResponse, Error, Permission, Ref, Result,
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, RemovalIntention, User,
|
||||
};
|
||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::State;
|
||||
use rocket_empty::EmptyResponse;
|
||||
|
||||
/// # Kick Member
|
||||
///
|
||||
/// Removes a member from the server.
|
||||
#[openapi(tag = "Server Members")]
|
||||
#[delete("/<target>/members/<member>")]
|
||||
pub async fn req(db: &Db, user: User, target: Ref, member: Ref) -> Result<EmptyResponse> {
|
||||
pub async fn kick(
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
target: Reference,
|
||||
member: Reference,
|
||||
) -> Result<EmptyResponse> {
|
||||
let server = target.as_server(db).await?;
|
||||
|
||||
if member.id == user.id {
|
||||
return Err(Error::CannotRemoveYourself);
|
||||
return Err(create_error!(CannotRemoveYourself));
|
||||
}
|
||||
|
||||
if member.id == server.owner {
|
||||
return Err(Error::InvalidOperation);
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
|
||||
let mut permissions = perms(&user).server(&server);
|
||||
|
||||
permissions
|
||||
.throw_permission(db, Permission::KickMembers)
|
||||
.await?;
|
||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||
calculate_server_permissions(&mut query)
|
||||
.await
|
||||
.throw_if_lacking_channel_permission(ChannelPermission::KickMembers)?;
|
||||
|
||||
let member = member.as_member(db, &server.id).await?;
|
||||
|
||||
if member.get_ranking(permissions.server.get().unwrap())
|
||||
<= permissions.get_member_rank().unwrap_or(i64::MIN)
|
||||
if member.get_ranking(query.server_ref().as_ref().unwrap())
|
||||
<= query.get_member_rank().unwrap_or(i64::MIN)
|
||||
{
|
||||
return Err(Error::NotElevated);
|
||||
return Err(create_error!(NotElevated));
|
||||
}
|
||||
|
||||
server
|
||||
.remove_member(db, member, RemovalIntention::Kick, false)
|
||||
member
|
||||
.remove(db, &server, RemovalIntention::Kick, false)
|
||||
.await
|
||||
.map(|_| EmptyResponse)
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||
server_edit::req,
|
||||
server_ack::req,
|
||||
channel_create::create_server_channel,
|
||||
member_fetch_all::req,
|
||||
member_remove::req,
|
||||
member_fetch::req,
|
||||
member_edit::req,
|
||||
member_fetch_all::fetch_all,
|
||||
member_remove::kick,
|
||||
member_fetch::fetch,
|
||||
member_edit::edit,
|
||||
member_experimental_query::member_experimental_query,
|
||||
ban_create::ban,
|
||||
ban_remove::unban,
|
||||
@@ -45,8 +45,8 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||
roles_edit::req,
|
||||
roles_fetch::req,
|
||||
roles_delete::req,
|
||||
permissions_set::req,
|
||||
permissions_set_default::req,
|
||||
permissions_set::set_role_permission,
|
||||
permissions_set_default::set_default_permissions,
|
||||
emoji_list::list_emoji
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,58 +1,51 @@
|
||||
use rocket::serde::json::Json;
|
||||
use serde::Deserialize;
|
||||
|
||||
use revolt_quark::{
|
||||
models::{Server, User},
|
||||
perms, Db, Error, Override, Permission, Ref, Result,
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, User,
|
||||
};
|
||||
|
||||
/// # Permission Value
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
pub struct DataSetServerRolePermission {
|
||||
/// Allow / deny values for the role in this server.
|
||||
permissions: Override,
|
||||
}
|
||||
use revolt_models::v0;
|
||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
/// # Set Role Permission
|
||||
///
|
||||
/// Sets permissions for the specified role in the server.
|
||||
#[openapi(tag = "Server Permissions")]
|
||||
#[put("/<target>/permissions/<role_id>", data = "<data>", rank = 2)]
|
||||
pub async fn req(
|
||||
db: &Db,
|
||||
pub async fn set_role_permission(
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
target: Ref,
|
||||
target: Reference,
|
||||
role_id: String,
|
||||
data: Json<DataSetServerRolePermission>,
|
||||
) -> Result<Json<Server>> {
|
||||
data: Json<v0::DataSetServerRolePermission>,
|
||||
) -> Result<Json<v0::Server>> {
|
||||
let data = data.into_inner();
|
||||
|
||||
let mut server = target.as_server(db).await?;
|
||||
if let Some((current_value, rank)) = server.roles.get(&role_id).map(|x| (x.permissions, x.rank))
|
||||
{
|
||||
let mut permissions = perms(&user).server(&server);
|
||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||
let permissions = calculate_server_permissions(&mut query).await;
|
||||
|
||||
permissions
|
||||
.throw_permission(db, Permission::ManagePermissions)
|
||||
.await?;
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?;
|
||||
|
||||
// Prevent us from editing roles above us
|
||||
if rank <= permissions.get_member_rank().unwrap_or(i64::MIN) {
|
||||
return Err(Error::NotElevated);
|
||||
if rank <= query.get_member_rank().unwrap_or(i64::MIN) {
|
||||
return Err(create_error!(NotElevated));
|
||||
}
|
||||
|
||||
// Ensure we have access to grant these permissions forwards
|
||||
let current_value: Override = current_value.into();
|
||||
permissions
|
||||
.throw_permission_override(db, current_value, data.permissions)
|
||||
.throw_permission_override(current_value, &data.permissions)
|
||||
.await?;
|
||||
|
||||
server
|
||||
.set_role_permission(db, &role_id, data.permissions.into())
|
||||
.await?;
|
||||
|
||||
Ok(Json(server))
|
||||
Ok(Json(server.into()))
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,42 @@
|
||||
use revolt_permissions::DataPermissionsValue;
|
||||
use revolt_quark::{
|
||||
models::{server::PartialServer, Server, User},
|
||||
perms, Db, Permission, Ref, Result,
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, PartialServer, Server, User,
|
||||
};
|
||||
use rocket::serde::json::Json;
|
||||
use revolt_models::v0;
|
||||
use revolt_permissions::{
|
||||
calculate_server_permissions, ChannelPermission, DataPermissionsValue, Override,
|
||||
};
|
||||
use revolt_result::Result;
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
/// # Set Default Permission
|
||||
///
|
||||
/// Sets permissions for the default role in this server.
|
||||
#[openapi(tag = "Server Permissions")]
|
||||
#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
|
||||
pub async fn req(
|
||||
db: &Db,
|
||||
pub async fn set_default_permissions(
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
target: Ref,
|
||||
target: Reference,
|
||||
data: Json<DataPermissionsValue>,
|
||||
) -> Result<Json<Server>> {
|
||||
) -> Result<Json<v0::Server>> {
|
||||
let data = data.into_inner();
|
||||
|
||||
let mut server = target.as_server(db).await?;
|
||||
let mut permissions = perms(&user).server(&server);
|
||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||
let permissions = calculate_server_permissions(&mut query).await;
|
||||
|
||||
permissions
|
||||
.throw_permission(db, Permission::ManagePermissions)
|
||||
.await?;
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?;
|
||||
|
||||
// Ensure we have permissions to grant these permissions forwards
|
||||
permissions
|
||||
.throw_permission_value(db, data.permissions)
|
||||
.throw_permission_override(
|
||||
None,
|
||||
&Override {
|
||||
allow: data.permissions,
|
||||
deny: 0,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
server
|
||||
@@ -40,5 +50,5 @@ pub async fn req(
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(server))
|
||||
Ok(Json(server.into()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user