From 77daf82b948395406ec75a4b88ac28995a273762 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Sun, 8 Jun 2025 01:40:48 +0100 Subject: [PATCH] chore: refactor new role rankings code --- .../admin_migrations/ops/mongodb/scripts.rs | 29 ++++++++---- .../core/database/src/models/servers/model.rs | 27 +++++++++++ .../routes/servers/roles_edit_positions.rs | 45 +++---------------- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs index e671e43d..40efe91a 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, ops::BitXor, time::Duration}; +use std::{collections::{HashMap, HashSet}, ops::BitXor, time::Duration}; use crate::{ mongodb::{ @@ -1167,6 +1167,19 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { if revision <= 41 { info!("Running migration [revision 41 / 05-06-2025]: convert role ranks to uniform numbers."); + #[derive(Serialize, Deserialize, Clone)] + struct Role { + pub rank: i64, + } + + #[derive(Serialize, Deserialize, Clone)] + struct Server { + #[serde(rename = "_id")] + pub id: String, + #[serde(default = "HashMap::::new")] + pub roles: HashMap, + } + let mut servers = db.db() .collection::("servers") .find(doc! { @@ -1180,21 +1193,21 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { .filter_map(|s| async { s.ok() }) .boxed(); - while let Some(mut server) = servers.next().await { + while let Some(server) = servers.next().await { let mut ordered_roles = server.roles.clone().into_iter().collect::>(); ordered_roles.sort_by(|(_, role_a), (_, role_b)| role_a.rank.cmp(&role_b.rank)); let ordered_roles = ordered_roles.into_iter().map(|(id, _)| id).collect::>(); - for (id, role) in server.roles.iter_mut() { - role.rank = ordered_roles.iter().position(|x| id == x).unwrap() as i64; - } + let mut doc = doc! {}; + + for id in server.roles.keys() { + doc.insert(format!("roles.{id}.rank"), ordered_roles.iter().position(|x| id == x).unwrap() as i64); + }; db.db().collection::("servers") .update_one( doc! { "_id": &server.id }, - doc! { "$set": { - "roles": bson::to_bson(&server.roles).unwrap() - } } + doc! { "$set": doc } ) .await .unwrap(); diff --git a/crates/core/database/src/models/servers/model.rs b/crates/core/database/src/models/servers/model.rs index c69b1196..65d7d282 100644 --- a/crates/core/database/src/models/servers/model.rs +++ b/crates/core/database/src/models/servers/model.rs @@ -253,6 +253,33 @@ impl Server { Err(create_error!(NotFound)) } } + + /// reorders the server's roles rankings + pub async fn update_role_rankings(&mut self, db: &Database, new_order: Vec) -> Result<()> { + // new_order must always contain every role + debug_assert_eq!(self.roles.len(), new_order.len()); + + // set the role's ranks to the positions in the vec + for (rank, id) in new_order.iter().enumerate() { + self.roles.get_mut(id).unwrap().rank = rank as i64; + } + + db.update_server( + &self.id, + &PartialServer { + roles: Some(self.roles.clone()), + ..Default::default() + }, + Vec::new() + ).await?; + + // publish bulk update event + EventV1::ServerRoleRanksUpdate { + ranks: new_order + }.p(self.id.clone()).await; + + Ok(()) + } } impl Role { diff --git a/crates/delta/src/routes/servers/roles_edit_positions.rs b/crates/delta/src/routes/servers/roles_edit_positions.rs index e62e7023..7e3b4d73 100644 --- a/crates/delta/src/routes/servers/roles_edit_positions.rs +++ b/crates/delta/src/routes/servers/roles_edit_positions.rs @@ -1,4 +1,4 @@ -use revolt_database::{events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, PartialRole, User}; +use revolt_database::{events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, PartialRole, PartialServer, User}; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use rocket::{serde::json::Json, State}; use revolt_result::{create_error, Result}; @@ -35,49 +35,18 @@ pub async fn edit_role_ranks( if server.owner != user.id { let member_top_rank = query.get_member_rank(); - // find all roles above the member which we should not be able to reorder - let cant_modify = server.roles.clone() - .into_iter() - .filter(|(_, role)| if let Some(top_rank) = member_top_rank { role.rank <= top_rank } else { true }) - .collect::>(); - + // find all roles above the member which we should not be able to reorder then // check if any roles which we cant reorder have tried to been reordered - if cant_modify.iter() - .any(|(id, _)| { - let existing_rank = existing_order.iter().position(|existing_id| id == existing_id); - let new_rank = new_order.iter().position(|new_id| id == new_id); - - existing_rank != new_rank - }) + if server.roles + .iter() + .filter(|(_, role)| if let Some(top_rank) = member_top_rank { role.rank <= top_rank } else { true }) + .any(|(id, _)| existing_order.iter().position(|existing_id| id == existing_id) != new_order.iter().position(|new_id| id == new_id)) { return Err(create_error!(NotElevated)) } } - // update the roles which have had their rank changed - for (role_id, role) in &mut server.roles { - let new_rank = new_order.iter().position(|id| id == role_id).unwrap() as i64; // unwrap - we check if its in the data earlier on - - if new_rank != role.rank { - // cant use Role::update here because we dont want to publish seperate role update events - let partial = PartialRole { - rank: Some(new_rank), - ..Default::default() - }; - - db.update_role(&server.id, role_id, &partial, Vec::new()).await?; - - role.apply_options(PartialRole { - rank: Some(new_rank), - ..Default::default() - }); - } - } - - // publish bulk update event - EventV1::ServerRoleRanksUpdate { - ranks: new_order - }.p(server.id.clone()).await; + server.update_role_rankings(db, new_order).await?; Ok(Json(server.into())) }