From b3986b3350b7d37a3e55e48733e636f4e62d72f5 Mon Sep 17 00:00:00 2001 From: strawberry Date: Thu, 21 Mar 2024 22:05:56 -0400 Subject: [PATCH] sdkfjsadklfsdklfdjsakfljas Signed-off-by: strawberry --- src/database/key_value/media.rs | 29 ++++++++++++-- src/service/admin/mod.rs | 43 ++++++++++++++++++++ src/service/media/data.rs | 4 ++ src/service/media/mod.rs | 71 ++++++++++++++++++++++++++++++++- 4 files changed, 142 insertions(+), 5 deletions(-) diff --git a/src/database/key_value/media.rs b/src/database/key_value/media.rs index af7a883a..ef171ded 100644 --- a/src/database/key_value/media.rs +++ b/src/database/key_value/media.rs @@ -1,4 +1,4 @@ -use ruma::api::client::error::ErrorKind; +use ruma::{api::client::error::ErrorKind, OwnedUserId}; use tracing::debug; use crate::{ @@ -25,8 +25,12 @@ impl service::media::Data for KeyValueDatabase { self.mediaid_file.insert(&key, &[])?; if let Some(user) = sender_user { - let key = mxc.as_bytes().to_vec(); - let user = user.as_bytes().to_vec(); + let mut key = mxc.as_bytes().to_vec(); + key.push(0xFF); + + let mut user = user.as_bytes().to_vec(); + user.push(0xFF); + self.mediaid_user.insert(&key, &user)?; } @@ -65,6 +69,8 @@ impl service::media::Data for KeyValueDatabase { let mut prefix = mxc.as_bytes().to_vec(); prefix.push(0xFF); + debug!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA prefix: {:?}", prefix); + let mut keys: Vec> = vec![]; for (key, _) in self.mediaid_file.scan_prefix(prefix) { @@ -82,6 +88,23 @@ impl service::media::Data for KeyValueDatabase { Ok(keys) } + fn get_all_media_keys_by_user(&self, user_id: OwnedUserId) -> Result>> { + debug!("User ID: {user_id:?}"); + + let mut user = user_id.as_bytes().to_vec(); + user.push(0xFF); + + let mut keys: Vec> = vec![]; + + for (key, value) in self.mediaid_user.iter() { + if value == user { + keys.push(key); + } + } + + Ok(keys) + } + fn search_file_metadata( &self, mxc: String, width: u32, height: u32, ) -> Result<(Option, Option, Vec)> { diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index 1b30a33c..ccfcf9c5 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -94,6 +94,12 @@ enum MediaCommand { event_id: Option>, }, + /// Deletes all **uploaded** local media from the specified user. + DeleteFromUser { + /// User ID to delete all media from + user_id: Box, + }, + /// - Deletes a codeblock list of MXC URLs from our database and on the /// filesystem DeleteList, @@ -105,6 +111,13 @@ enum MediaCommand { /// past 5 minutes duration: String, }, + + /// - Lists all **uploaded** local media from the specified user with their + /// MXC URLs. + ListFromUser { + /// User ID to list all media from + user_id: Box, + }, } #[cfg_attr(test, derive(Debug))] @@ -806,6 +819,17 @@ impl Service { )); } }, + MediaCommand::DeleteFromUser { + user_id, + } => { + let deleted_count = services().media.delete_from_user(user_id.into()).await?; + + debug!("Deleted {deleted_count} total media files."); + + return Ok(RoomMessageEventContent::text_plain(format!( + "Deleted {deleted_count} total media files.", + ))); + }, MediaCommand::DeleteList => { if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { let mxc_list = body.clone().drain(1..body.len() - 1).collect::>(); @@ -839,6 +863,25 @@ impl Service { deleted_count ))); }, + MediaCommand::ListFromUser { + user_id, + } => { + let mxc_list = services().media.list_all_media_by_user(user_id.clone().into()).await?; + + let output_plain = format!( + "All MXC URLs {user_id} Uploaded:\n{}", + mxc_list.iter().map(ToOwned::to_owned).collect::>().join("\n") + ); + let output_html = format!( + "\n\n{}
All MXC URLs {user_id} \ + Uploaded
URL
", + mxc_list.iter().fold(String::new(), |mut output, mxc| { + writeln!(output, "{}", mxc).unwrap(); + output + }) + ); + RoomMessageEventContent::text_html(output_plain, output_html) + }, } }, AdminCommand::Users(command) => match command { diff --git a/src/service/media/data.rs b/src/service/media/data.rs index 7cbde755..15745b25 100644 --- a/src/service/media/data.rs +++ b/src/service/media/data.rs @@ -1,3 +1,5 @@ +use ruma::OwnedUserId; + use crate::Result; pub trait Data: Send + Sync { @@ -15,6 +17,8 @@ pub trait Data: Send + Sync { fn search_mxc_metadata_prefix(&self, mxc: String) -> Result>>; + fn get_all_media_keys_by_user(&self, user_id: OwnedUserId) -> Result>>; + fn get_all_media_keys(&self) -> Result>>; fn remove_url_preview(&self, url: &str) -> Result<()>; diff --git a/src/service/media/mod.rs b/src/service/media/mod.rs index c028903c..1e3d6dad 100644 --- a/src/service/media/mod.rs +++ b/src/service/media/mod.rs @@ -12,7 +12,7 @@ use tokio::{ }; use tracing::{debug, error}; -use crate::{services, utils, Error, Result}; +use crate::{services, utils::string_from_bytes, Error, Result}; #[derive(Debug)] pub struct FileMeta { @@ -110,6 +110,71 @@ impl Service { } } + /// Deletes all media in the database and from the media directory by a + /// specific user + pub async fn delete_from_user(&self, user_id: OwnedUserId) -> Result { + if let Ok(user_keys) = self.db.get_all_media_keys_by_user(user_id.clone()) { + if user_keys.is_empty() { + error!("User \"{user_id}\" has not uploaded any media."); + return Err(Error::bad_database("User has not uploaded any media.")); + } + + let mut mxc_deletion_count = 0; + + for key in user_keys { + let mxc = String::from_utf8_lossy(&key); + + let keys = self.db.search_mxc_metadata_prefix(mxc.to_string())?; // the MXC alone does not determine the file path, it is only the prefix + + for key in keys { + let mxc = String::from_utf8_lossy(&key); + + debug!("Deleting MXC {mxc} from database"); + + self.delete(mxc.to_string()).await?; + + mxc_deletion_count += 1; + } + } + + Ok(mxc_deletion_count) + } else { + error!("Failed to find any media keys for user \"{user_id}\" in our database"); + Err(Error::bad_database( + "Failed to find any media keys for the provided user in our database", + )) + } + } + + pub async fn list_all_media_by_user(&self, user_id: OwnedUserId) -> Result> { + if let Ok(keys) = self.db.get_all_media_keys_by_user(user_id.clone()) { + if keys.is_empty() { + error!("User \"{user_id}\" has not uploaded any media."); + return Err(Error::bad_database("User has not uploaded any media.")); + } + + let mut mxc_list: Vec = vec![]; + + for key in keys { + let mxc_string = string_from_bytes(&key).map_err(|e| { + error!("Failed to convert MXC key to string in database for user \"{user_id}\": {e}"); + Error::bad_database("Failed to convert MXC key to string in database") + })?; + + // TODO: add the file name + + mxc_list.push(mxc_string); + } + + Ok(mxc_list) + } else { + error!("Failed to find any media keys for user \"{user_id}\" in our database"); + Err(Error::bad_database( + "Failed to find any media keys for the provided user in our database", + )) + } + } + /// Uploads or replaces a file thumbnail. #[allow(clippy::too_many_arguments)] pub async fn upload_thumbnail( @@ -200,7 +265,7 @@ impl Service { let mxc = parts .next() .map(|bytes| { - utils::string_from_bytes(bytes).map_err(|e| { + string_from_bytes(bytes).map_err(|e| { error!("Failed to parse MXC unicode bytes from our database: {}", e); Error::bad_database("Failed to parse MXC unicode bytes from our database") }) @@ -502,6 +567,8 @@ mod tests { fn search_mxc_metadata_prefix(&self, _mxc: String) -> Result>> { todo!() } + fn get_all_media_keys_by_user(&self, _user_id: OwnedUserId) -> Result>> { todo!() } + fn get_all_media_keys(&self) -> Result>> { todo!() } fn search_file_metadata(