From c8ae34ba73fa59b2376ebd2cc8c884e2d3c4c0bd Mon Sep 17 00:00:00 2001 From: Zomatree Date: Tue, 28 Jan 2025 15:55:21 +0000 Subject: [PATCH] feat: implement missing permission syncs --- crates/core/database/src/events/client.rs | 5 + crates/core/database/src/voice/mod.rs | 125 ++++++++++++++++-- .../core/database/src/voice/voice_client.rs | 6 + crates/core/result/src/lib.rs | 21 ++- .../src/routes/channels/channel_delete.rs | 25 ++-- .../routes/channels/group_remove_member.rs | 56 ++++---- .../src/routes/channels/permissions_set.rs | 16 ++- .../channels/permissions_set_default.rs | 11 +- crates/delta/src/routes/servers/ban_create.rs | 3 +- .../delta/src/routes/servers/member_edit.rs | 79 ++--------- .../delta/src/routes/servers/member_remove.rs | 7 + .../src/routes/servers/permissions_set.rs | 10 +- .../routes/servers/permissions_set_default.rs | 10 +- .../delta/src/routes/servers/roles_delete.rs | 48 +------ crates/delta/src/routes/servers/roles_edit.rs | 53 +------- .../delta/src/routes/servers/server_delete.rs | 19 ++- 16 files changed, 266 insertions(+), 228 deletions(-) diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 6262f9da..99781ea4 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -254,6 +254,11 @@ pub enum EventV1 { id: String, user: String, }, + VoiceChannelMove { + user: String, + from: String, + to: String, + }, UserVoiceStateUpdate { id: String, channel_id: String, diff --git a/crates/core/database/src/voice/mod.rs b/crates/core/database/src/voice/mod.rs index 2e59ca33..996ee505 100644 --- a/crates/core/database/src/voice/mod.rs +++ b/crates/core/database/src/voice/mod.rs @@ -1,15 +1,20 @@ -use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; -use crate::models::{Channel, User}; +use livekit_protocol::ParticipantPermission; +use redis_kiss::{get_connection as _get_connection, Conn, redis::Pipeline, AsyncCommands}; +use crate::{events::client::EventV1, models::{Channel, User}, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, Role, Server}; use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState}; -use revolt_permissions::{ChannelPermission, PermissionValue}; +use revolt_permissions::{calculate_channel_permissions, ChannelPermission, PermissionValue}; use revolt_result::{create_error, Result, ToRevoltError}; mod voice_client; pub use voice_client::VoiceClient; +async fn get_connection() -> Result { + _get_connection().await.to_internal_error() +} + pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { - let mut conn = get_connection().await.to_internal_error()?; + let mut conn = get_connection().await?; if user.bot.is_some() // bots can be in as many voice channels as it wants so we just check if its already connected to the one its trying to connect to @@ -30,11 +35,27 @@ pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> { } } +pub async fn get_user_voice_channels(user_id: &str) -> Result> { + get_connection() + .await? + .smembers(format!("vc:{user_id}")) + .await + .to_internal_error() +} + +pub async fn is_in_voice_channel(user_id: &str, channel_id: &str) -> Result { + get_connection() + .await? + .sismember(format!("vc:{}", user_id), channel_id) + .await + .to_internal_error() +} + pub async fn get_user_voice_channel_in_server( user_id: &str, server_id: &str, ) -> Result> { - let mut conn = get_connection().await.to_internal_error()?; + let mut conn = get_connection().await?; let unique_key = format!("{}:{}", user_id, server_id); @@ -89,7 +110,7 @@ pub async fn create_voice_state( voice_state.screensharing, ) .set(format!("camera:{unique_key}"), voice_state.camera) - .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) + .query_async(&mut get_connection().await?.into_inner()) .await .to_internal_error()?; @@ -113,7 +134,7 @@ pub async fn delete_voice_state( format!("camera:{unique_key}"), unique_key.clone(), ]) - .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) + .query_async(&mut get_connection().await?.into_inner()) .await .to_internal_error()?; @@ -179,7 +200,7 @@ pub async fn update_voice_state( } pipeline - .query_async(&mut get_connection().await.to_internal_error()?.into_inner()) + .query_async(&mut get_connection().await?.into_inner()) .await .to_internal_error()?; @@ -188,8 +209,7 @@ pub async fn update_voice_state( pub async fn get_voice_channel_members(channel_id: &str) -> Result> { get_connection() - .await - .to_internal_error()? + .await? .smembers::<_, Vec>(format!("vc_members:{}", channel_id)) .await .to_internal_error() @@ -203,8 +223,7 @@ pub async fn get_voice_state( let unique_key = format!("{}:{user_id}", server_id.unwrap_or(channel_id)); let (is_publishing, is_receiving, screensharing, camera) = get_connection() - .await - .to_internal_error()? + .await? .mget::<_, (Option, Option, Option, Option)>(&[ format!("is_publishing:{unique_key}"), format!("is_receiving:{unique_key}"), @@ -257,3 +276,85 @@ pub async fn get_channel_voice_state(channel: &Channel) -> Result Result<()> { + get_connection() + .await? + .smove( + format!("vc-members-{from}"), + format!("vc-members-{to}"), + user, + ) + .await + .to_internal_error()?; + + Ok(()) +} + +pub async fn sync_voice_permissions(db: &Database, voice_client: &VoiceClient, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { + + for user_id in get_voice_channel_members(channel.id()).await? { + let user = Reference::from_unchecked(user_id.clone()).as_user(db).await?; + + sync_user_voice_permissions(db, voice_client, &user, channel, server, role_id).await?; + }; + + Ok(()) +} + +pub async fn sync_user_voice_permissions(db: &Database, voice_client: &VoiceClient, user: &User, channel: &Channel, server: Option<&Server>, role_id: Option<&str>) -> Result<()> { + let channel_id = channel.id(); + let server_id: Option<&str> = server.as_ref().map(|s| s.id.as_str()); + + let member = match server_id { + Some(server_id) => Some(Reference::from_unchecked(user.id.clone()).as_member(db, server_id).await?), + None => None + }; + + if role_id.is_none_or(|role_id| member.as_ref().is_none_or(|member| member.roles.iter().any(|r| r == role_id))) { + let voice_state = get_voice_state(channel_id, server_id, &user.id).await?.unwrap(); + + let mut query = DatabasePermissionQuery::new(db, user) + .channel(channel); + + if let (Some(server), Some(member)) = (server, member.as_ref()) { + query = query.member(member).server(server) + } + + let permissions = calculate_channel_permissions(&mut query).await; + + let mut update_event = PartialUserVoiceState { + id: Some(user.id.clone()), + ..Default::default() + }; + + let before = update_event.clone(); + + let can_video = permissions.has_channel_permission(ChannelPermission::Video); + let can_speak = permissions.has_channel_permission(ChannelPermission::Speak); + let can_listen = permissions.has_channel_permission(ChannelPermission::Listen); + + update_event.camera = voice_state.camera.then_some(can_video); + update_event.screensharing = voice_state.screensharing.then_some(can_video); + update_event.is_publishing = voice_state.is_publishing.then_some(can_speak); + + update_voice_state(channel_id, server_id, &user.id, &update_event).await?; + + voice_client.update_permissions(user, channel_id, ParticipantPermission { + can_subscribe: can_listen, + can_publish: can_speak, + can_publish_data: can_speak, + ..Default::default() + }).await?; + + if update_event != before { + EventV1::UserVoiceStateUpdate { + id: user.id.clone(), + channel_id: channel_id.to_string(), + data: update_event + }.p(channel_id.to_string()).await; + }; + }; + + Ok(()) +} \ No newline at end of file diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index 0aa78ec2..4f540870 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -105,4 +105,10 @@ impl VoiceClient { .await .to_internal_error() } + + pub async fn remove_user(&self, user: &User, channel_id: &str) -> Result<()> { + self.rooms.remove_participant(channel_id, &user.id) + .await + .to_internal_error() + } } diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 8c04b709..e9d4f24b 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -220,25 +220,24 @@ pub trait ToRevoltError { impl ToRevoltError for Result { #[track_caller] fn to_internal_error(self) -> Result { - self - .inspect_err(|e| log::error!("{e:?}")) - .map_err(|_| { - let loc = Location::caller(); + let loc = Location::caller(); - Error { - error_type: ErrorType::InternalError, - location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) - } - }) + self + .map_err(|_| { + Error { + error_type: ErrorType::InternalError, + location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) + } + }) } } impl ToRevoltError for Option { #[track_caller] fn to_internal_error(self) -> Result { - self.ok_or_else(|| { - let loc = Location::caller(); + let loc = Location::caller(); + self.ok_or_else(|| { Error { error_type: ErrorType::InternalError, location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 86dea166..8c8dd5db 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, PartialChannel, User, AMQP, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{delete_voice_state, get_voice_channel_members, VoiceClient}, Channel, Database, PartialChannel, User, AMQP }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -15,6 +14,7 @@ use rocket_empty::EmptyResponse; #[delete("/?")] pub async fn delete( db: &State, + voice_client: &State, amqp: &State, user: User, target: Reference, @@ -27,7 +27,7 @@ pub async fn delete( permissions.throw_if_lacking_channel_permission(ChannelPermission::ViewChannel)?; match &channel { - Channel::SavedMessages { .. } => Err(create_error!(NoEffect)), + Channel::SavedMessages { .. } => Err(create_error!(NoEffect))?, Channel::DirectMessage { .. } => channel .update( db, @@ -37,8 +37,7 @@ pub async fn delete( }, vec![], ) - .await - .map(|_| EmptyResponse), + .await?, Channel::Group { .. } => channel .remove_user_from_group( db, @@ -47,13 +46,21 @@ pub async fn delete( None, options.leave_silently.unwrap_or_default(), ) - .await - .map(|_| EmptyResponse), + .await?, Channel::TextChannel { .. } | Channel::VoiceChannel { .. } => { permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?; - channel.delete(db).await.map(|_| EmptyResponse) + channel.delete(db).await? } - } + }; + + for user_id in get_voice_channel_members(channel.id()).await? { + let user = Reference::from_unchecked(user_id).as_user(db).await?; + + voice_client.remove_user(&user, channel.id()).await?; + delete_voice_state(channel.id(), channel.server().as_deref(), &user.id).await?; + }; + + Ok(EmptyResponse) } #[cfg(test)] diff --git a/crates/delta/src/routes/channels/group_remove_member.rs b/crates/delta/src/routes/channels/group_remove_member.rs index 90cccfef..6bc0feb2 100644 --- a/crates/delta/src/routes/channels/group_remove_member.rs +++ b/crates/delta/src/routes/channels/group_remove_member.rs @@ -1,4 +1,4 @@ -use revolt_database::{util::reference::Reference, Channel, Database, User, AMQP}; +use revolt_database::{util::reference::Reference, voice::{delete_voice_state, is_in_voice_channel, raise_if_in_voice, VoiceClient}, Channel, Database, User, AMQP}; use revolt_permissions::ChannelPermission; use revolt_result::{create_error, Result}; @@ -12,6 +12,7 @@ use rocket_empty::EmptyResponse; #[delete("//recipients/")] pub async fn remove_member( db: &State, + voice_client: &State, amqp: &State, user: User, target: Reference, @@ -23,32 +24,35 @@ pub async fn remove_member( let channel = target.as_channel(db).await?; - match &channel { - Channel::Group { - owner, recipients, .. - } => { - if &user.id != owner { - return Err(create_error!(MissingPermission { - permission: ChannelPermission::ManageChannel.to_string() - })); - } - - let member = member.as_user(db).await?; - if user.id == member.id { - return Err(create_error!(CannotRemoveYourself)); - } - - if !recipients.iter().any(|x| *x == member.id) { - return Err(create_error!(NotInGroup)); - } - - channel - .remove_user_from_group(db, amqp, &member, Some(&user.id), false) - .await - .map(|_| EmptyResponse) + if let Channel::Group { owner, recipients, .. } = &channel { + if &user.id != owner { + return Err(create_error!(MissingPermission { + permission: ChannelPermission::ManageChannel.to_string() + })); } - _ => Err(create_error!(InvalidOperation)), - } + + let member = member.as_user(db).await?; + if user.id == member.id { + return Err(create_error!(CannotRemoveYourself)); + } + + if !recipients.iter().any(|x| *x == member.id) { + return Err(create_error!(NotInGroup)); + } + + channel + .remove_user_from_group(db, amqp, &member, Some(&user.id), false) + .await?; + } else { + return Err(create_error!(InvalidOperation)) + }; + + if is_in_voice_channel(&user.id, channel.id()).await? { + voice_client.remove_user(&user, channel.id()).await?; + delete_voice_state(channel.id(), None, &user.id).await?; + }; + + Ok(EmptyResponse) } #[cfg(test)] diff --git a/crates/delta/src/routes/channels/permissions_set.rs b/crates/delta/src/routes/channels/permissions_set.rs index b4b3c3dc..e3564540 100644 --- a/crates/delta/src/routes/channels/permissions_set.rs +++ b/crates/delta/src/routes/channels/permissions_set.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Channel, Database, User }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission, Override}; @@ -17,14 +16,15 @@ use serde::Deserialize; #[put("//permissions/", data = "", rank = 2)] pub async fn set_role_permissions( db: &State, + voice_client: &State, user: User, target: Reference, role_id: String, data: Json, ) -> Result> { - let mut channel = target.as_channel(db).await?; + let channel = target.as_channel(db).await?; let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel); - let permissions = calculate_channel_permissions(&mut query).await; + let permissions: revolt_permissions::PermissionValue = calculate_channel_permissions(&mut query).await; permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?; @@ -39,11 +39,15 @@ pub async fn set_role_permissions( .throw_permission_override(current_value, &data.permissions) .await?; - channel + let mut new_channel = channel.clone(); + + new_channel .set_role_permission(db, &role_id, data.permissions.clone().into()) .await?; - Ok(Json(channel.into())) + sync_voice_permissions(db, voice_client, &new_channel, Some(server), Some(&role_id)).await?; + + Ok(Json(new_channel.into())) } else { Err(create_error!(NotFound)) } diff --git a/crates/delta/src/routes/channels/permissions_set_default.rs b/crates/delta/src/routes/channels/permissions_set_default.rs index 5957426a..ed82de81 100644 --- a/crates/delta/src/routes/channels/permissions_set_default.rs +++ b/crates/delta/src/routes/channels/permissions_set_default.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, PartialChannel, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Channel, Database, PartialChannel, User }; use revolt_models::v0::{self, DataDefaultChannelPermissions}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -16,6 +15,7 @@ use rocket::{serde::json::Json, State}; #[put("//permissions/default", data = "", rank = 1)] pub async fn set_default_permissions( db: &State, + voice_client: &State, user: User, target: Reference, data: Json, @@ -75,5 +75,12 @@ pub async fn set_default_permissions( _ => return Err(create_error!(InvalidOperation)), } + let server = match channel.server() { + Some(server_id) => Some(Reference::from_unchecked(server_id).as_server(db).await?), + None => None + }; + + sync_voice_permissions(db, voice_client, &channel, server.as_ref(), None).await?; + Ok(Json(channel.into())) } diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 309b53f6..cd1c8cf3 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -19,7 +19,7 @@ use validator::Validate; #[put("//bans/", data = "")] pub async fn ban( db: &State, - voice: &State, + voice_client: &State, user: User, server: Reference, target: Reference, @@ -61,6 +61,7 @@ pub async fn ban( // If the member is in a voice channel while banned kick them from the voice channel if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { + voice_client.remove_user(&user, &channel_id).await?; delete_voice_state(&channel_id, Some(&server.id), &user.id).await? } } diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index e20fa846..d86155be 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -7,7 +7,7 @@ use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands}; use revolt_database::{ events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::VoiceClient, + voice::{get_user_voice_channel_in_server, move_user, sync_user_voice_permissions, VoiceClient}, Database, File, PartialMember, User, }; use revolt_models::v0::{self, FieldsMember, PartialUserVoiceState}; @@ -23,8 +23,8 @@ use validator::Validate; #[openapi(tag = "Server Members")] #[patch("//members/", data = "")] pub async fn edit( - voice_client: &State, db: &State, + voice_client: &State, user: User, server: Reference, target: Reference, @@ -190,6 +190,18 @@ pub async fn edit( .map(|r| r.contains(FieldsMember::CanPublish) || r.contains(FieldsMember::CanReceive)) .unwrap_or_default(); + if can_publish.is_some() + || can_receive.is_some() + || voice_channel.is_some() + || remove_contains_voice + { + if let Some(channel) = get_user_voice_channel_in_server(&target_user.id, &server.id).await? { + let channel = Reference::from_unchecked(channel).as_channel(db).await?; + + sync_user_voice_permissions(db, voice_client, &user, &channel, Some(&server), None).await?; + }; + }; + member .update( db, @@ -200,68 +212,5 @@ pub async fn edit( ) .await?; - if can_publish.is_some() - || can_receive.is_some() - || voice_channel.is_some() - || remove_contains_voice - { - let mut conn = get_connection().await.to_internal_error()?; - - let unique_key = format!("{}-{}", &member.id.user, &member.id.server); - - // if we edit the member while they are in a voice channel we need to also update the perms - // otherwise it wont take place until they leave and rejoin - if let Some(channel) = conn - .get::<_, Option>(format!("vc-{}", &unique_key)) - .await - .to_internal_error()? - { - let mut pipeline = Pipeline::new(); - let mut new_perms = ParticipantPermission::default(); - - if remove_contains_voice { - let mut query = DatabasePermissionQuery::new(db, &target_user) - .server(&server) - .member(&member); - - let permissions = calculate_server_permissions(&mut query).await; - - if !permissions.has_channel_permission(ChannelPermission::Speak) { - can_publish = Some(false) - } - - if !permissions.has_channel_permission(ChannelPermission::Listen) { - can_publish = Some(false) - } - } - - if let Some(can_publish) = can_publish { - new_perms.can_publish = can_publish; - new_perms.can_publish_data = can_publish; - }; - - if let Some(can_receive) = can_receive { - new_perms.can_subscribe = can_receive; - }; - - if let Some(new_channel) = voice_channel { - pipeline.smove( - format!("vc-members-{channel}"), - format!("vc-members-{new_channel}"), - &member.id.user, - ); - }; - - pipeline - .query_async(&mut conn.into_inner()) - .await - .to_internal_error()?; - - voice_client - .update_permissions(&user, &channel, new_perms) - .await?; - }; - }; - Ok(Json(member.into())) } diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index a7e2f50a..a06e4be4 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -1,5 +1,6 @@ use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, + voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, Database, RemovalIntention, User, }; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; @@ -14,6 +15,7 @@ use rocket_empty::EmptyResponse; #[delete("//members/")] pub async fn kick( db: &State, + voice_client: &State, user: User, target: Reference, member: Reference, @@ -40,6 +42,11 @@ pub async fn kick( return Err(create_error!(NotElevated)); } + if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { + voice_client.remove_user(&user, &channel_id).await?; + delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; + } + member .remove(db, &server, RemovalIntention::Kick, false) .await diff --git a/crates/delta/src/routes/servers/permissions_set.rs b/crates/delta/src/routes/servers/permissions_set.rs index 0c293aa8..3d80d387 100644 --- a/crates/delta/src/routes/servers/permissions_set.rs +++ b/crates/delta/src/routes/servers/permissions_set.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, User }; use revolt_models::v0; use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override}; @@ -14,6 +13,7 @@ use rocket::{serde::json::Json, State}; #[put("//permissions/", data = "", rank = 2)] pub async fn set_role_permission( db: &State, + voice_client: &State, user: User, target: Reference, role_id: String, @@ -40,6 +40,12 @@ pub async fn set_role_permission( .throw_permission_override(current_value, &data.permissions) .await?; + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; + }; + server .set_role_permission(db, &role_id, data.permissions.into()) .await?; diff --git a/crates/delta/src/routes/servers/permissions_set_default.rs b/crates/delta/src/routes/servers/permissions_set_default.rs index 8189e956..42c364d8 100644 --- a/crates/delta/src/routes/servers/permissions_set_default.rs +++ b/crates/delta/src/routes/servers/permissions_set_default.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, PartialServer, Server, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, PartialServer, Server, User }; use revolt_models::v0; use revolt_permissions::{ @@ -16,6 +15,7 @@ use rocket::{serde::json::Json, State}; #[put("//permissions/default", data = "", rank = 1)] pub async fn set_default_permissions( db: &State, + voice_client: &State, user: User, target: Reference, data: Json, @@ -39,6 +39,12 @@ pub async fn set_default_permissions( ) .await?; + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), None).await?; + }; + server .update( db, diff --git a/crates/delta/src/routes/servers/roles_delete.rs b/crates/delta/src/routes/servers/roles_delete.rs index 881ef6cb..38b40859 100644 --- a/crates/delta/src/routes/servers/roles_delete.rs +++ b/crates/delta/src/routes/servers/roles_delete.rs @@ -1,11 +1,9 @@ -use livekit_protocol::ParticipantPermission; use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, VoiceClient}, - Channel, Database, User + voice::{sync_voice_permissions, VoiceClient}, + Database, User }; -use revolt_models::v0::PartialUserVoiceState; -use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission}; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use rocket::State; use rocket_empty::EmptyResponse; @@ -38,45 +36,7 @@ pub async fn delete( for channel_id in &server.channels { let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; - if matches!(channel, Channel::VoiceChannel { .. }) { - for member_id in get_voice_channel_members(channel_id).await? { - let member = Reference::from_unchecked(member_id).as_member(db, &server.id).await?; - - if member.roles.contains(&role_id) { - let user = Reference::from_unchecked(member.id.user.clone()).as_user(db).await?; - let voice_state = get_voice_state(channel_id, Some(&server.id), &user.id).await?.unwrap(); - - let mut query = DatabasePermissionQuery::new(db, &user) - .member(&member) - .channel(&channel) - .server(&server); - - let permissions = calculate_channel_permissions(&mut query).await; - - let mut update_event = PartialUserVoiceState { - id: Some(user.id.clone()), - ..Default::default() - }; - - let can_video = permissions.has_channel_permission(ChannelPermission::Video); - let can_speak = permissions.has_channel_permission(ChannelPermission::Speak); - let can_listen = permissions.has_channel_permission(ChannelPermission::Listen); - - update_event.camera = voice_state.camera.then_some(can_video); - update_event.screensharing = voice_state.screensharing.then_some(can_video); - update_event.is_publishing = voice_state.is_publishing.then_some(can_speak); - - update_voice_state(channel_id, Some(&server.id), &user.id, &update_event).await?; - - voice_client.update_permissions(&user, channel_id, ParticipantPermission { - can_subscribe: can_listen, - can_publish: can_speak, - can_publish_data: can_speak, - ..Default::default() - }).await?; - } - } - } + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; }; role.delete(db, &server.id, &role_id) diff --git a/crates/delta/src/routes/servers/roles_edit.rs b/crates/delta/src/routes/servers/roles_edit.rs index 7b211d0f..9d3f85a2 100644 --- a/crates/delta/src/routes/servers/roles_edit.rs +++ b/crates/delta/src/routes/servers/roles_edit.rs @@ -1,14 +1,12 @@ -use livekit_protocol::ParticipantPermission; use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, - voice::{get_allowed_sources, get_voice_channel_members, get_voice_state, update_voice_state, update_voice_state_tracks, VoiceClient}, - Channel, Database, PartialRole, User + voice::{sync_voice_permissions, VoiceClient}, + Database, PartialRole, User }; -use revolt_models::v0::{self, PartialUserVoiceState}; -use revolt_permissions::{calculate_channel_permissions, calculate_server_permissions, ChannelPermission, PermissionQuery}; +use revolt_models::v0; +use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use rocket::{serde::json::Json, State}; -use serde::{Deserialize, Serialize}; use validator::Validate; /// # Edit Role @@ -18,12 +16,11 @@ use validator::Validate; #[patch("//roles/", data = "")] pub async fn edit( db: &State, - voice: &State, + voice_client: &State, user: User, target: Reference, role_id: String, data: Json, - voice_client: &State ) -> Result> { let data = data.into_inner(); data.validate().map_err(|error| { @@ -83,45 +80,7 @@ pub async fn edit( for channel_id in &server.channels { let channel = Reference::from_unchecked(channel_id.clone()).as_channel(db).await?; - if matches!(channel, Channel::VoiceChannel { .. }) { - for member_id in get_voice_channel_members(channel_id).await? { - let member = Reference::from_unchecked(member_id).as_member(db, &server.id).await?; - - if member.roles.contains(&role_id) { - let user = Reference::from_unchecked(member.id.user.clone()).as_user(db).await?; - let voice_state = get_voice_state(channel_id, Some(&server.id), &user.id).await?.unwrap(); - - let mut query = DatabasePermissionQuery::new(db, &user) - .member(&member) - .channel(&channel) - .server(&server); - - let permissions = calculate_channel_permissions(&mut query).await; - - let mut update_event = PartialUserVoiceState { - id: Some(user.id.clone()), - ..Default::default() - }; - - let can_video = permissions.has_channel_permission(ChannelPermission::Video); - let can_speak = permissions.has_channel_permission(ChannelPermission::Speak); - let can_listen = permissions.has_channel_permission(ChannelPermission::Listen); - - update_event.camera = voice_state.camera.then_some(can_video); - update_event.screensharing = voice_state.screensharing.then_some(can_video); - update_event.is_publishing = voice_state.is_publishing.then_some(can_speak); - - update_voice_state(channel_id, Some(&server.id), &user.id, &update_event).await?; - - voice_client.update_permissions(&user, channel_id, ParticipantPermission { - can_subscribe: can_listen, - can_publish: can_speak, - can_publish_data: can_speak, - ..Default::default() - }).await?; - } - } - } + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; }; Ok(Json(role.into())) diff --git a/crates/delta/src/routes/servers/server_delete.rs b/crates/delta/src/routes/servers/server_delete.rs index 28a3bf42..9c909659 100644 --- a/crates/delta/src/routes/servers/server_delete.rs +++ b/crates/delta/src/routes/servers/server_delete.rs @@ -1,4 +1,8 @@ -use revolt_database::{util::reference::Reference, Database, RemovalIntention, User}; +use revolt_database::{ + util::reference::Reference, + voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient}, + Database, RemovalIntention, User, +}; use revolt_models::v0; use revolt_result::Result; use rocket::State; @@ -12,6 +16,7 @@ use rocket_empty::EmptyResponse; #[delete("/?")] pub async fn delete( db: &State, + voice_client: &State, user: User, target: Reference, options: v0::OptionsServerDelete, @@ -20,8 +25,20 @@ pub async fn delete( let member = db.fetch_member(&target.id, &user.id).await?; if server.owner == user.id { + for channel_id in &server.channels { + voice_client.remove_user(&user, channel_id).await?; + delete_voice_state(channel_id, Some(&server.id), &user.id).await?; + } + server.delete(db).await } else { + if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { + if server.channels.iter().any(|c| c == &channel_id) { + voice_client.remove_user(&user, &channel_id).await?; + delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; + } + }; + member .remove( db,