From da6ceadd5106f42a4229eea8cab2d2c69c8d0160 Mon Sep 17 00:00:00 2001 From: silverpill Date: Sat, 7 May 2022 20:27:11 +0000 Subject: [PATCH] Add CLI command for deleting unused media attachments --- README.md | 6 ++++++ src/bin/mitractl.rs | 18 ++++++++++++++++ src/models/attachments/queries.rs | 35 +++++++++++++++++++++++++++++++ src/utils/files.rs | 8 ++++--- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 02b4f59..7d7e5ac 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,12 @@ Remove remote posts and media older than 30 days: mitractl delete-extraneous-posts -d 30 ``` +Delete attachments that doesn't belong to any post: + +``` +mitractl delete-unused-attachments -d 5 +``` + Generate ethereum address: ``` diff --git a/src/bin/mitractl.rs b/src/bin/mitractl.rs index 37b760b..ccac3c8 100644 --- a/src/bin/mitractl.rs +++ b/src/bin/mitractl.rs @@ -8,6 +8,7 @@ use mitra::database::migrate::apply_migrations; use mitra::ethereum::signatures::generate_ecdsa_key; use mitra::ethereum::utils::key_to_ethereum_address; use mitra::logger::configure_logger; +use mitra::models::attachments::queries::delete_unused_attachments; use mitra::models::posts::queries::{delete_post, find_extraneous_posts}; use mitra::models::profiles::queries::delete_profile; use mitra::models::users::queries::{ @@ -33,6 +34,7 @@ enum SubCommand { DeleteProfile(DeleteProfile), DeletePost(DeletePost), DeleteExtraneousPosts(DeleteExtraneousPosts), + DeleteUnusedAttachments(DeleteUnusedAttachments), } /// Generate RSA private key @@ -83,6 +85,13 @@ struct DeleteExtraneousPosts { dry_run: bool, } +/// Delete attachments that doesn't belong to any post +#[derive(Parser)] +struct DeleteUnusedAttachments { + #[clap(short)] + days: i64, +} + #[tokio::main] async fn main() { let opts: Opts = Opts::parse(); @@ -142,6 +151,15 @@ async fn main() { println!("post {} deleted", post_id); }; }, + SubCommand::DeleteUnusedAttachments(subopts) => { + let created_before = Utc::now() - Duration::days(subopts.days); + let deletion_queue = delete_unused_attachments( + db_client, + &created_before, + ).await.unwrap(); + deletion_queue.process(&config).await; + println!("unused attachments deleted"); + }, _ => panic!(), }; }, diff --git a/src/models/attachments/queries.rs b/src/models/attachments/queries.rs index 3237dc6..f394f61 100644 --- a/src/models/attachments/queries.rs +++ b/src/models/attachments/queries.rs @@ -1,7 +1,13 @@ +use chrono::{DateTime, Utc}; use tokio_postgres::GenericClient; use uuid::Uuid; use crate::errors::DatabaseError; +use crate::models::cleanup::{ + find_orphaned_files, + find_orphaned_ipfs_objects, + DeletionQueue, +}; use crate::utils::id::new_uuid; use super::types::DbMediaAttachment; @@ -42,3 +48,32 @@ pub async fn set_attachment_ipfs_cid( let db_attachment = row.try_get("media_attachment")?; Ok(db_attachment) } + +pub async fn delete_unused_attachments( + db_client: &impl GenericClient, + created_before: &DateTime, +) -> Result { + let rows = db_client.query( + " + DELETE FROM media_attachment + WHERE post_id IS NULL AND created_at < $1 + RETURNING file_name, ipfs_cid + ", + &[&created_before], + ).await?; + let mut files = vec![]; + let mut ipfs_objects = vec![]; + for row in rows { + let file_name = row.try_get("file_name")?; + files.push(file_name); + if let Some(ipfs_cid) = row.try_get("ipfs_cid")? { + ipfs_objects.push(ipfs_cid); + }; + }; + let orphaned_files = find_orphaned_files(db_client, files).await?; + let orphaned_ipfs_objects = find_orphaned_ipfs_objects(db_client, ipfs_objects).await?; + Ok(DeletionQueue { + files: orphaned_files, + ipfs_objects: orphaned_ipfs_objects, + }) +} diff --git a/src/utils/files.rs b/src/utils/files.rs index a9345b6..a4b0bc9 100644 --- a/src/utils/files.rs +++ b/src/utils/files.rs @@ -104,9 +104,11 @@ pub fn remove_files(files: Vec, from_dir: &Path) -> () { let file_path_str = file_path.to_string_lossy(); match remove_file(&file_path) { Ok(_) => log::info!("removed file {}", file_path_str), - Err(_) => log::warn!("failed to remove file {}", file_path_str), - } - } + Err(err) => { + log::warn!("failed to remove file {} ({})", file_path_str, err); + }, + }; + }; } #[cfg(test)]