diff --git a/crates/delta/src/routes/channels/channel_edit.rs b/crates/delta/src/routes/channels/channel_edit.rs index 45febe1c..8d54ae1f 100644 --- a/crates/delta/src/routes/channels/channel_edit.rs +++ b/crates/delta/src/routes/channels/channel_edit.rs @@ -16,19 +16,18 @@ use validator::Validate; pub struct DataEditChannel { /// Channel name #[validate(length(min = 1, max = 32))] - #[serde(skip_serializing_if = "Option::is_none")] name: Option, /// Channel description #[validate(length(min = 0, max = 1024))] - #[serde(skip_serializing_if = "Option::is_none")] description: Option, + /// Group owner + owner: Option, /// Icon /// /// Provide an Autumn attachment Id. #[validate(length(min = 1, max = 128))] icon: Option, /// Whether this channel is age-restricted - #[serde(skip_serializing_if = "Option::is_none")] nsfw: Option, #[validate(length(min = 1))] remove: Option>, @@ -59,12 +58,47 @@ pub async fn req( && data.description.is_none() && data.icon.is_none() && data.nsfw.is_none() + && data.owner.is_none() && data.remove.is_none() { return Ok(Json(channel)); } let mut partial: PartialChannel = Default::default(); + + // Transfer group ownership + if let Some(new_owner) = data.owner { + if let Channel::Group { + owner, recipients, .. + } = &mut channel + { + // Make sure we are the owner of this group + if owner != &user.id { + return Err(Error::NotOwner); + } + + // Ensure user is part of group + if !recipients.contains(&new_owner) { + return Err(Error::NotInGroup); + } + + // Transfer ownership + *owner = new_owner.to_string(); + + // Notify clients + SystemMessage::ChannelOwnershipChanged { + from: owner.to_string(), + to: new_owner, + } + } else { + return Err(Error::InvalidOperation); + } + .into_message(channel.id().to_string()) + .create(db, &channel, None) + .await + .ok(); + } + match &mut channel { Channel::Group { id, diff --git a/crates/quark/src/impl/generic/channels/channel.rs b/crates/quark/src/impl/generic/channels/channel.rs index 8c139071..5cc39d59 100644 --- a/crates/quark/src/impl/generic/channels/channel.rs +++ b/crates/quark/src/impl/generic/channels/channel.rs @@ -305,6 +305,15 @@ impl Channel { vec![], ) .await?; + + SystemMessage::ChannelOwnershipChanged { + from: owner.to_string(), + to: new_owner.into(), + } + .into_message(id.to_string()) + .create(db, self, None) + .await + .ok(); } else { db.delete_channel(self).await?; return Ok(()); diff --git a/crates/quark/src/impl/generic/channels/message.rs b/crates/quark/src/impl/generic/channels/message.rs index 6eaa3e5e..2c7ead71 100644 --- a/crates/quark/src/impl/generic/channels/message.rs +++ b/crates/quark/src/impl/generic/channels/message.rs @@ -262,6 +262,9 @@ impl From for String { "Channel description changed.".to_string() } SystemMessage::ChannelIconChanged { .. } => "Channel icon changed.".to_string(), + SystemMessage::ChannelOwnershipChanged { .. } => { + "Channel ownership changed.".to_string() + } } } } diff --git a/crates/quark/src/models/channels/message.rs b/crates/quark/src/models/channels/message.rs index e5ed7172..b3e094df 100644 --- a/crates/quark/src/models/channels/message.rs +++ b/crates/quark/src/models/channels/message.rs @@ -63,6 +63,8 @@ pub enum SystemMessage { ChannelDescriptionChanged { by: String }, #[serde(rename = "channel_icon_changed")] ChannelIconChanged { by: String }, + #[serde(rename = "channel_ownership_changed")] + ChannelOwnershipChanged { from: String, to: String }, } /// Name and / or avatar override information diff --git a/crates/quark/src/util/result.rs b/crates/quark/src/util/result.rs index 0f4fb6ca..6ee2fe63 100644 --- a/crates/quark/src/util/result.rs +++ b/crates/quark/src/util/result.rs @@ -72,6 +72,7 @@ pub enum Error { }, NotElevated, CannotGiveMissingPermissions, + NotOwner, // ? General errors. DatabaseError { @@ -167,6 +168,7 @@ impl<'r> Responder<'r, 'static> for Error { Error::MissingUserPermission { .. } => Status::Forbidden, Error::NotElevated => Status::Forbidden, Error::CannotGiveMissingPermissions => Status::Forbidden, + Error::NotOwner => Status::Forbidden, Error::DatabaseError { .. } => Status::InternalServerError, Error::InternalError => Status::InternalServerError, diff --git a/docker-compose.yml b/docker-compose.yml index 6852cc96..c712d1e4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,6 +50,7 @@ services: /usr/bin/mc mb minio/backgrounds; /usr/bin/mc mb minio/icons; /usr/bin/mc mb minio/banners; + /usr/bin/mc mb minio/emojis; exit 0; " # File server (autumn)