From 05bceb6d4ab66697e340c08c025bcf0d9f49c025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0spik?= Date: Sat, 13 Jun 2026 11:33:23 +0300 Subject: [PATCH] feat: add admin channel wipe endpoint --- crates/core/database/src/events/client.rs | 10 +++++ .../database/src/models/channels/model.rs | 7 ++++ .../core/database/src/models/channels/ops.rs | 4 +- .../src/models/channels/ops/mongodb.rs | 17 ++++++++ .../src/models/channels/ops/reference.rs | 6 +++ crates/core/models/src/v0/admin.rs | 2 + .../admin/channels/actions/channel_wipe.rs | 40 +++++++++++++++++++ crates/delta/src/routes/admin/mod.rs | 1 + 8 files changed, 86 insertions(+), 1 deletion(-) diff --git a/crates/core/database/src/events/client.rs b/crates/core/database/src/events/client.rs index 31864cee..ca40530a 100644 --- a/crates/core/database/src/events/client.rs +++ b/crates/core/database/src/events/client.rs @@ -282,6 +282,16 @@ pub enum EventV1 { id: String, }, + /// Channel wiped + /// + /// Clients should remove all associated data: + /// - Messages + /// - Unreads + /// - Voice States + ChannelWipe { + id: String, + }, + /// User joins a group ChannelGroupJoin { id: String, diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index 74b56e11..deb6f496 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -762,6 +762,13 @@ impl Channel { // - channels list / categories list on server db.delete_channel(self).await } + + /// Wipe a channel's messages + pub async fn wipe(&self, db: &Database) -> Result<()> { + let id = self.id().to_string(); + EventV1::ChannelWipe { id: id.clone() }.p(id).await; + db.wipe_channel(self).await + } } #[cfg(feature = "mongodb")] diff --git a/crates/core/database/src/models/channels/ops.rs b/crates/core/database/src/models/channels/ops.rs index 3ee90d54..57589e80 100644 --- a/crates/core/database/src/models/channels/ops.rs +++ b/crates/core/database/src/models/channels/ops.rs @@ -1,3 +1,4 @@ +use bson::{doc, Bson}; use crate::{revolt_result::Result, Channel, FieldsChannel, PartialChannel}; use revolt_permissions::OverrideField; @@ -48,5 +49,6 @@ pub trait AbstractChannels: Sync + Send { async fn remove_user_from_group(&self, channel_id: &str, user_id: &str) -> Result<()>; // Delete a channel - async fn delete_channel(&self, channel_id: &Channel) -> Result<()>; + async fn delete_channel(&self, channel_id: &Channel) -> Result<()>; // Wipe a channel's messages + async fn wipe_channel(&self, channel: &Channel) -> Result<()>; } diff --git a/crates/core/database/src/models/channels/ops/mongodb.rs b/crates/core/database/src/models/channels/ops/mongodb.rs index 79612f8d..c39f5eb8 100644 --- a/crates/core/database/src/models/channels/ops/mongodb.rs +++ b/crates/core/database/src/models/channels/ops/mongodb.rs @@ -261,6 +261,23 @@ impl AbstractChannels for MongoDb { // Delete the channel itself query!(self, delete_one_by_id, COL, channel.id()).map(|_| ()) } + + // Wipe a channel's messages + async fn wipe_channel(&self, channel: &Channel) -> Result<()> { + let id = channel.id().to_string(); + + // Delete invites and unreads. + self.delete_associated_channel_objects(Bson::String(id.to_string())) + .await?; + + // Delete messages. + self.delete_bulk_messages(doc! { + "channel": &id + }) + .await?; + + Ok(()) + } } impl MongoDb { diff --git a/crates/core/database/src/models/channels/ops/reference.rs b/crates/core/database/src/models/channels/ops/reference.rs index 518808bc..04309891 100644 --- a/crates/core/database/src/models/channels/ops/reference.rs +++ b/crates/core/database/src/models/channels/ops/reference.rs @@ -151,4 +151,10 @@ impl AbstractChannels for ReferenceDb { Err(create_error!(NotFound)) } } + + async fn wipe_channel(&self, channel: &Channel) -> Result<()> { + let mut messages = self.messages.lock().await; + messages.retain(|_, message| message.channel != channel.id()); + Ok(()) + } } diff --git a/crates/core/models/src/v0/admin.rs b/crates/core/models/src/v0/admin.rs index 4d2b6e16..0114b7a1 100644 --- a/crates/core/models/src/v0/admin.rs +++ b/crates/core/models/src/v0/admin.rs @@ -329,6 +329,7 @@ auto_derived! { // Channels DeleteChannel, EditChannel, + WipeChannel } // Joiner payloads @@ -380,6 +381,7 @@ impl AdminAuditItemActions { AdminAuditItemActions::DisableAccount => true, AdminAuditItemActions::DeleteChannel => true, AdminAuditItemActions::EditChannel => true, + AdminAuditItemActions::WipeChannel => true, } } } diff --git a/crates/delta/src/routes/admin/channels/actions/channel_wipe.rs b/crates/delta/src/routes/admin/channels/actions/channel_wipe.rs index e69de29b..ec81e77e 100644 --- a/crates/delta/src/routes/admin/channels/actions/channel_wipe.rs +++ b/crates/delta/src/routes/admin/channels/actions/channel_wipe.rs @@ -0,0 +1,40 @@ +use rocket::State; +use rocket_empty::EmptyResponse; +use revolt_result::{create_error, Result}; +use revolt_database::{AdminAuthorization, Database}; +use revolt_database::util::reference::Reference; +use revolt_models::v0; +use crate::routes::admin::util::{create_audit_action, flatten_authorized_user, user_has_permission}; + +/// Wipe all the messages from a channel +#[openapi(tag = "Admin")] +#[delete("/admin/channels//wipe?")] +pub async fn admin_channel_wipe( + db: &State, + auth: AdminAuthorization, + channel_id: Reference<'_>, + case: Option<&str> +) -> Result { + let admin = flatten_authorized_user(&auth); + if !user_has_permission(admin, v0::AdminUserPermissionFlags::ManageChannels) { + return Err(create_error!(MissingPermission { + permission: "ManageChannels".to_string() + })); + } + + let mut channel = channel_id.as_channel(db).await?; + + channel.wipe(db).await?; + + create_audit_action( + &db, + &admin.id, + v0::AdminAuditItemActions::WipeChannel, + case, + Some(channel_id.id), + None, + ) + .await?; + + Ok(EmptyResponse) +} \ No newline at end of file diff --git a/crates/delta/src/routes/admin/mod.rs b/crates/delta/src/routes/admin/mod.rs index 85b51dff..ceb03dd4 100644 --- a/crates/delta/src/routes/admin/mod.rs +++ b/crates/delta/src/routes/admin/mod.rs @@ -42,5 +42,6 @@ pub fn routes() -> (Vec, OpenApi) { accounts::account_disable::admin_account_disable, channels::actions::channel_delete::admin_delete_channel, channels::actions::channel_edit::admin_channel_edit, + channels::actions::channel_wipe::admin_channel_wipe, ] }