add admin command to delete individual media files

This commit is contained in:
Benjamin Lee 2024-09-14 20:20:02 -07:00
parent 7672cc8473
commit d7087c66bb
No known key found for this signature in database
GPG key ID: FB9624E2885D55A4
4 changed files with 78 additions and 3 deletions

View file

@ -140,4 +140,25 @@ impl service::media::Data for KeyValueDatabase {
let parts = MediaFileKeyParts::try_from(&key)?; let parts = MediaFileKeyParts::try_from(&key)?;
Ok((parts.meta, key)) Ok((parts.meta, key))
} }
fn delete_file_metadata(&self, key: MediaFileKey) -> Result<()> {
self.mediaid_file.remove(key.as_bytes())
}
fn search_thumbnails_metadata(
&self,
mxc: OwnedMxcUri,
) -> Result<Vec<(FileMeta, MediaFileKey)>> {
let mut prefix = mxc.as_bytes().to_vec();
prefix.push(0xFF);
self.mediaid_file
.scan_prefix(prefix)
.map(|(key, _)| {
let key = MediaFileKey::new(key);
let parts = MediaFileKeyParts::try_from(&key)?;
Ok((parts.meta, key))
})
.collect()
}
} }

View file

@ -23,8 +23,8 @@ use ruma::{
TimelineEventType, TimelineEventType,
}, },
signatures::verify_json, signatures::verify_json,
EventId, MilliSecondsSinceUnixEpoch, OwnedRoomId, RoomId, RoomVersionId, EventId, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId, RoomId,
ServerName, UserId, RoomVersionId, ServerName, UserId,
}; };
use serde_json::value::to_raw_value; use serde_json::value::to_raw_value;
use tokio::sync::{mpsc, Mutex, RwLock}; use tokio::sync::{mpsc, Mutex, RwLock};
@ -179,6 +179,12 @@ enum AdminCommand {
room_id: Box<RoomId>, room_id: Box<RoomId>,
}, },
/// Delete media and all associated thumbnails.
DeleteMedia {
/// mxc:// URI of the media to delete
mxc: OwnedMxcUri,
},
/// Verify json signatures /// Verify json signatures
/// [commandbody]() /// [commandbody]()
/// # ``` /// # ```
@ -791,6 +797,12 @@ impl Service {
services().rooms.metadata.disable_room(&room_id, false)?; services().rooms.metadata.disable_room(&room_id, false)?;
RoomMessageEventContent::text_plain("Room enabled.") RoomMessageEventContent::text_plain("Room enabled.")
} }
AdminCommand::DeleteMedia {
mxc,
} => {
services().media.delete(mxc).await?;
RoomMessageEventContent::text_plain("Media deleted.")
}
AdminCommand::DeactivateUser { AdminCommand::DeactivateUser {
leave_rooms, leave_rooms,
user_id, user_id,

View file

@ -3,7 +3,7 @@ use std::io::Cursor;
use image::imageops::FilterType; use image::imageops::FilterType;
use ruma::{http_headers::ContentDisposition, OwnedMxcUri}; use ruma::{http_headers::ContentDisposition, OwnedMxcUri};
use tokio::{ use tokio::{
fs::File, fs::{self, File},
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
}; };
use tracing::{debug, warn}; use tracing::{debug, warn};
@ -110,6 +110,37 @@ impl Service {
} }
} }
/// Deletes a media object and all associated thumbnails.
#[tracing::instrument(skip(self))]
pub(crate) async fn delete(&self, mxc: OwnedMxcUri) -> Result<()> {
let (_, key) = self.db.search_file_metadata(mxc.clone(), 0, 0)?;
let thumbnails = self.db.search_thumbnails_metadata(mxc)?;
for (_, thumbnail_key) in thumbnails {
self.delete_by_key(thumbnail_key).await?;
}
self.delete_by_key(key).await?;
Ok(())
}
/// Deletes a specific media key, which may or may not be a thumbnail.
///
/// When deleting a non-thumbnail key with this method, the associated
/// thumbnails are not deleted.
async fn delete_by_key(&self, key: MediaFileKey) -> Result<()> {
let path = services().globals.get_media_file(&key);
match fs::remove_file(path).await {
Ok(()) => (),
// The file in the fs may already have been deleted by hand
Err(e) if e.kind() == std::io::ErrorKind::NotFound => (),
other_error => other_error?,
}
self.db.delete_file_metadata(key)?;
Ok(())
}
/// Returns width, height of the thumbnail and whether it should be cropped. /// Returns width, height of the thumbnail and whether it should be cropped.
/// Returns None when the server should send the original file. /// Returns None when the server should send the original file.
fn thumbnail_properties( fn thumbnail_properties(

View file

@ -18,4 +18,15 @@ pub(crate) trait Data: Send + Sync {
width: u32, width: u32,
height: u32, height: u32,
) -> Result<(FileMeta, MediaFileKey)>; ) -> Result<(FileMeta, MediaFileKey)>;
fn delete_file_metadata(&self, key: MediaFileKey) -> Result<()>;
/// Return all thumbnail keys/metadata associated with a MXC.
///
/// The original file is not returned. To fetch the key/metadata of the
/// original file, use [`Data::search_file_metadata`].
fn search_thumbnails_metadata(
&self,
mxc: OwnedMxcUri,
) -> Result<Vec<(FileMeta, MediaFileKey)>>;
} }