add admin command to delete all remote media from a specific server
Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
parent
8923c9a227
commit
fb49e37067
3 changed files with 105 additions and 6 deletions
|
@ -1,5 +1,5 @@
|
||||||
use conduit::{debug, info, Result};
|
use conduit::{debug, info, trace, warn, Result};
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
use ruma::{events::room::message::RoomMessageEventContent, EventId, Mxc, MxcUri, ServerName};
|
||||||
|
|
||||||
use crate::{admin_command, utils::parse_local_user_id};
|
use crate::{admin_command, utils::parse_local_user_id};
|
||||||
|
|
||||||
|
@ -201,3 +201,59 @@ pub(super) async fn delete_all_from_user(&self, username: String, force: bool) -
|
||||||
"Deleted {deleted_count} total files.",
|
"Deleted {deleted_count} total files.",
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn delete_all_from_server(
|
||||||
|
&self, server_name: Box<ServerName>, force: bool,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
if server_name == self.services.globals.server_name() {
|
||||||
|
return Ok(RoomMessageEventContent::text_plain("This command only works for remote media."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(all_mxcs) = self.services.media.get_all_mxcs().await else {
|
||||||
|
return Ok(RoomMessageEventContent::text_plain("Failed to get MXC URIs from our database"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut deleted_count: usize = 0;
|
||||||
|
|
||||||
|
for mxc in all_mxcs {
|
||||||
|
let mxc_server_name = match mxc.server_name() {
|
||||||
|
Ok(server_name) => server_name,
|
||||||
|
Err(e) => {
|
||||||
|
if force {
|
||||||
|
warn!("Failed to parse MXC {mxc} server name from database, ignoring error and skipping: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Failed to parse MXC {mxc} server name from database: {e}",
|
||||||
|
)));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if mxc_server_name != server_name || self.services.globals.server_is_ours(mxc_server_name) {
|
||||||
|
trace!("skipping MXC URI {mxc}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mxc: Mxc<'_> = mxc.as_str().try_into()?;
|
||||||
|
|
||||||
|
match self.services.media.delete(&mxc).await {
|
||||||
|
Ok(()) => {
|
||||||
|
deleted_count = deleted_count.saturating_add(1);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
if force {
|
||||||
|
warn!("Failed to delete {mxc}, ignoring error and skipping: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(RoomMessageEventContent::text_plain(format!("Failed to delete MXC {mxc}: {e}")));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Deleted {deleted_count} total files.",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod commands;
|
||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::{EventId, MxcUri};
|
use ruma::{EventId, MxcUri, ServerName};
|
||||||
|
|
||||||
use crate::admin_command_dispatch;
|
use crate::admin_command_dispatch;
|
||||||
|
|
||||||
|
@ -46,4 +46,13 @@ pub(super) enum MediaCommand {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
force: bool,
|
force: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// - Deletes all remote media from the specified remote server
|
||||||
|
DeleteAllFromServer {
|
||||||
|
server_name: Box<ServerName>,
|
||||||
|
|
||||||
|
/// Continues deleting media if an undeletable object is found
|
||||||
|
#[arg(short, long)]
|
||||||
|
force: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,11 +159,43 @@ impl Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets all the MXC URIs in our media database
|
||||||
|
pub async fn get_all_mxcs(&self) -> Result<Vec<OwnedMxcUri>> {
|
||||||
|
let all_keys = self.db.get_all_media_keys();
|
||||||
|
|
||||||
|
let mut mxcs = Vec::with_capacity(all_keys.len());
|
||||||
|
|
||||||
|
for key in all_keys {
|
||||||
|
debug!("Full MXC key from database: {key:?}");
|
||||||
|
|
||||||
|
// we need to get the MXC URL from the first part of the key (the first 0xff /
|
||||||
|
// 255 push). this is all necessary because of conduit using magic keys for
|
||||||
|
// media
|
||||||
|
let mut parts = key.split(|&b| b == 0xFF);
|
||||||
|
let mxc = parts
|
||||||
|
.next()
|
||||||
|
.map(|bytes| {
|
||||||
|
utils::string_from_bytes(bytes)
|
||||||
|
.map_err(|e| err!(Database(error!("Failed to parse MXC unicode bytes from our database: {e}"))))
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
let Some(mxc_s) = mxc else {
|
||||||
|
return Err!(Database("Parsed MXC URL unicode bytes from database but still is None"));
|
||||||
|
};
|
||||||
|
|
||||||
|
debug_info!("Parsed MXC key to URL: {mxc_s}");
|
||||||
|
let mxc = OwnedMxcUri::from(mxc_s);
|
||||||
|
|
||||||
|
mxcs.push(mxc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mxcs)
|
||||||
|
}
|
||||||
|
|
||||||
/// Deletes all remote only media files in the given at or after
|
/// Deletes all remote only media files in the given at or after
|
||||||
/// time/duration. Returns a u32 with the amount of media files deleted.
|
/// time/duration. Returns a u32 with the amount of media files deleted.
|
||||||
pub async fn delete_all_remote_media_at_after_time(&self, time: String, force: bool) -> Result<usize> {
|
pub async fn delete_all_remote_media_at_after_time(&self, time: String, force: bool) -> Result<usize> {
|
||||||
let all_keys = self.db.get_all_media_keys();
|
|
||||||
|
|
||||||
let user_duration: SystemTime = match cyborgtime::parse_duration(&time) {
|
let user_duration: SystemTime = match cyborgtime::parse_duration(&time) {
|
||||||
Err(e) => return Err!(Database(error!("Failed to parse specified time duration: {e}"))),
|
Err(e) => return Err!(Database(error!("Failed to parse specified time duration: {e}"))),
|
||||||
Ok(duration) => SystemTime::now()
|
Ok(duration) => SystemTime::now()
|
||||||
|
@ -171,7 +203,9 @@ impl Service {
|
||||||
.ok_or(err!(Arithmetic("Duration {duration:?} is too large")))?,
|
.ok_or(err!(Arithmetic("Duration {duration:?} is too large")))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut remote_mxcs: Vec<String> = vec![];
|
let all_keys = self.db.get_all_media_keys();
|
||||||
|
|
||||||
|
let mut remote_mxcs = Vec::with_capacity(all_keys.len());
|
||||||
|
|
||||||
for key in all_keys {
|
for key in all_keys {
|
||||||
debug!("Full MXC key from database: {key:?}");
|
debug!("Full MXC key from database: {key:?}");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue