Move CLI subcommands to cli module
This commit is contained in:
parent
29402f0113
commit
5497f50977
3 changed files with 301 additions and 239 deletions
|
@ -1,178 +1,10 @@
|
||||||
use anyhow::Error;
|
|
||||||
use chrono::{Duration, Utc};
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use tokio_postgres::GenericClient;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use mitra::activitypub::builders::delete_note::prepare_delete_note;
|
use mitra::cli::{Opts, SubCommand};
|
||||||
use mitra::activitypub::builders::delete_person::prepare_delete_person;
|
use mitra::config::parse_config;
|
||||||
use mitra::activitypub::fetcher::fetchers::fetch_actor;
|
|
||||||
use mitra::activitypub::handlers::update_person::update_remote_profile;
|
|
||||||
use mitra::config::{parse_config, Config};
|
|
||||||
use mitra::database::create_database_client;
|
use mitra::database::create_database_client;
|
||||||
use mitra::database::migrate::apply_migrations;
|
use mitra::database::migrate::apply_migrations;
|
||||||
use mitra::ethereum::signatures::generate_ecdsa_key;
|
|
||||||
use mitra::ethereum::sync::save_current_block_number;
|
|
||||||
use mitra::ethereum::utils::key_to_ethereum_address;
|
|
||||||
use mitra::logger::configure_logger;
|
use mitra::logger::configure_logger;
|
||||||
use mitra::models::attachments::queries::delete_unused_attachments;
|
|
||||||
use mitra::models::cleanup::find_orphaned_files;
|
|
||||||
use mitra::models::posts::queries::{delete_post, find_extraneous_posts, get_post_by_id};
|
|
||||||
use mitra::models::profiles::queries::{
|
|
||||||
delete_profile,
|
|
||||||
get_profile_by_actor_id,
|
|
||||||
get_profile_by_id,
|
|
||||||
reset_subscriptions,
|
|
||||||
};
|
|
||||||
use mitra::models::users::queries::{
|
|
||||||
create_invite_code,
|
|
||||||
get_invite_codes,
|
|
||||||
get_user_by_id,
|
|
||||||
};
|
|
||||||
use mitra::utils::crypto::{generate_private_key, serialize_private_key};
|
|
||||||
use mitra::utils::files::remove_files;
|
|
||||||
|
|
||||||
/// Admin CLI tool
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct Opts {
|
|
||||||
#[clap(subcommand)]
|
|
||||||
subcmd: SubCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
enum SubCommand {
|
|
||||||
GenerateRsaKey(GenerateRsaKey),
|
|
||||||
GenerateEthereumAddress(GenerateEthereumAddress),
|
|
||||||
|
|
||||||
GenerateInviteCode(GenerateInviteCode),
|
|
||||||
ListInviteCodes(ListInviteCodes),
|
|
||||||
RefetchActor(RefetchActor),
|
|
||||||
DeleteProfile(DeleteProfile),
|
|
||||||
DeletePost(DeletePost),
|
|
||||||
DeleteExtraneousPosts(DeleteExtraneousPosts),
|
|
||||||
DeleteUnusedAttachments(DeleteUnusedAttachments),
|
|
||||||
UpdateCurrentBlock(UpdateCurrentBlock),
|
|
||||||
DeleteOrphanedFiles(DeleteOrphanedFiles),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate RSA private key
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct GenerateRsaKey;
|
|
||||||
|
|
||||||
impl GenerateRsaKey {
|
|
||||||
fn execute(&self) -> () {
|
|
||||||
let private_key = generate_private_key().unwrap();
|
|
||||||
let private_key_str = serialize_private_key(&private_key).unwrap();
|
|
||||||
println!("{}", private_key_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate ethereum address
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct GenerateEthereumAddress;
|
|
||||||
|
|
||||||
/// Generate invite code
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct GenerateInviteCode;
|
|
||||||
|
|
||||||
/// List invite codes
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct ListInviteCodes;
|
|
||||||
|
|
||||||
/// Re-fetch actor profile by actor ID
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct RefetchActor {
|
|
||||||
id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RefetchActor {
|
|
||||||
async fn execute(
|
|
||||||
&self,
|
|
||||||
config: &Config,
|
|
||||||
db_client: &impl GenericClient,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let profile = get_profile_by_actor_id(db_client, &self.id).await?;
|
|
||||||
let actor = fetch_actor(&config.instance(), &self.id).await?;
|
|
||||||
update_remote_profile(db_client, &config.media_dir(), profile, actor).await?;
|
|
||||||
println!("profile updated");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete profile
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct DeleteProfile {
|
|
||||||
id: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete post
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct DeletePost {
|
|
||||||
id: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete old remote posts
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct DeleteExtraneousPosts {
|
|
||||||
days: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete attachments that don't belong to any post
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct DeleteUnusedAttachments {
|
|
||||||
days: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find and delete orphaned files
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct DeleteOrphanedFiles;
|
|
||||||
|
|
||||||
impl DeleteOrphanedFiles {
|
|
||||||
async fn execute(
|
|
||||||
&self,
|
|
||||||
config: &Config,
|
|
||||||
db_client: &impl GenericClient,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let media_dir = config.media_dir();
|
|
||||||
let mut files = vec![];
|
|
||||||
for maybe_path in std::fs::read_dir(&media_dir)? {
|
|
||||||
let file_name = maybe_path?.file_name()
|
|
||||||
.to_string_lossy().to_string();
|
|
||||||
files.push(file_name);
|
|
||||||
};
|
|
||||||
println!("found {} files", files.len());
|
|
||||||
let orphaned = find_orphaned_files(db_client, files).await?;
|
|
||||||
if !orphaned.is_empty() {
|
|
||||||
remove_files(orphaned, &media_dir);
|
|
||||||
println!("orphaned files deleted");
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update blockchain synchronization starting block
|
|
||||||
#[derive(Parser)]
|
|
||||||
struct UpdateCurrentBlock {
|
|
||||||
number: u64,
|
|
||||||
|
|
||||||
#[clap(long)]
|
|
||||||
reset_db: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UpdateCurrentBlock {
|
|
||||||
async fn execute(
|
|
||||||
&self,
|
|
||||||
config: &Config,
|
|
||||||
db_client: &impl GenericClient,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
save_current_block_number(&config.storage_dir, self.number)?;
|
|
||||||
if self.reset_db {
|
|
||||||
reset_subscriptions(db_client).await?;
|
|
||||||
};
|
|
||||||
println!("current block updated");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
@ -180,14 +12,7 @@ async fn main() {
|
||||||
|
|
||||||
match opts.subcmd {
|
match opts.subcmd {
|
||||||
SubCommand::GenerateRsaKey(cmd) => cmd.execute(),
|
SubCommand::GenerateRsaKey(cmd) => cmd.execute(),
|
||||||
SubCommand::GenerateEthereumAddress(_) => {
|
SubCommand::GenerateEthereumAddress(cmd) => cmd.execute(),
|
||||||
let private_key = generate_ecdsa_key();
|
|
||||||
let address = key_to_ethereum_address(&private_key);
|
|
||||||
println!(
|
|
||||||
"address {:?}; private key {}",
|
|
||||||
address, private_key.display_secret(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
subcmd => {
|
subcmd => {
|
||||||
// Other commands require initialized app
|
// Other commands require initialized app
|
||||||
let config = parse_config();
|
let config = parse_config();
|
||||||
|
@ -198,68 +23,13 @@ async fn main() {
|
||||||
apply_migrations(db_client).await;
|
apply_migrations(db_client).await;
|
||||||
|
|
||||||
match subcmd {
|
match subcmd {
|
||||||
SubCommand::GenerateInviteCode(_) => {
|
SubCommand::GenerateInviteCode(cmd) => cmd.execute(db_client).await.unwrap(),
|
||||||
let invite_code = create_invite_code(db_client).await.unwrap();
|
SubCommand::ListInviteCodes(cmd) => cmd.execute(db_client).await.unwrap(),
|
||||||
println!("generated invite code: {}", invite_code);
|
|
||||||
},
|
|
||||||
SubCommand::ListInviteCodes(_) => {
|
|
||||||
let invite_codes = get_invite_codes(db_client).await.unwrap();
|
|
||||||
if invite_codes.is_empty() {
|
|
||||||
println!("no invite codes found");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
for code in invite_codes {
|
|
||||||
println!("{}", code);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
SubCommand::RefetchActor(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
SubCommand::RefetchActor(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
SubCommand::DeleteProfile(subopts) => {
|
SubCommand::DeleteProfile(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
let profile = get_profile_by_id(db_client, &subopts.id).await.unwrap();
|
SubCommand::DeletePost(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
let mut maybe_delete_person = None;
|
SubCommand::DeleteExtraneousPosts(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
if profile.is_local() {
|
SubCommand::DeleteUnusedAttachments(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
let user = get_user_by_id(db_client, &profile.id).await.unwrap();
|
|
||||||
let activity = prepare_delete_person(db_client, config.instance(), &user)
|
|
||||||
.await.unwrap();
|
|
||||||
maybe_delete_person = Some(activity);
|
|
||||||
};
|
|
||||||
let deletion_queue = delete_profile(db_client, &profile.id).await.unwrap();
|
|
||||||
deletion_queue.process(&config).await;
|
|
||||||
// Send Delete(Person) activities
|
|
||||||
if let Some(activity) = maybe_delete_person {
|
|
||||||
activity.deliver().await.unwrap();
|
|
||||||
};
|
|
||||||
println!("profile deleted");
|
|
||||||
},
|
|
||||||
SubCommand::DeletePost(subopts) => {
|
|
||||||
let post = get_post_by_id(db_client, &subopts.id).await.unwrap();
|
|
||||||
let deletion_queue = delete_post(db_client, &post.id).await.unwrap();
|
|
||||||
deletion_queue.process(&config).await;
|
|
||||||
if post.author.is_local() {
|
|
||||||
// Send Delete(Note) activity
|
|
||||||
let author = get_user_by_id(db_client, &post.author.id).await.unwrap();
|
|
||||||
prepare_delete_note(db_client, config.instance(), &author, &post).await.unwrap()
|
|
||||||
.deliver().await.unwrap();
|
|
||||||
};
|
|
||||||
println!("post deleted");
|
|
||||||
},
|
|
||||||
SubCommand::DeleteExtraneousPosts(subopts) => {
|
|
||||||
let created_before = Utc::now() - Duration::days(subopts.days);
|
|
||||||
let posts = find_extraneous_posts(db_client, &created_before).await.unwrap();
|
|
||||||
for post_id in posts {
|
|
||||||
let deletion_queue = delete_post(db_client, &post_id).await.unwrap();
|
|
||||||
deletion_queue.process(&config).await;
|
|
||||||
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");
|
|
||||||
},
|
|
||||||
SubCommand::DeleteOrphanedFiles(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
SubCommand::DeleteOrphanedFiles(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
SubCommand::UpdateCurrentBlock(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
SubCommand::UpdateCurrentBlock(cmd) => cmd.execute(&config, db_client).await.unwrap(),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
|
291
src/cli.rs
Normal file
291
src/cli.rs
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
use anyhow::Error;
|
||||||
|
use chrono::{Duration, Utc};
|
||||||
|
use clap::Parser;
|
||||||
|
use tokio_postgres::GenericClient;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::activitypub::builders::delete_note::prepare_delete_note;
|
||||||
|
use crate::activitypub::builders::delete_person::prepare_delete_person;
|
||||||
|
use crate::activitypub::fetcher::fetchers::fetch_actor;
|
||||||
|
use crate::activitypub::handlers::update_person::update_remote_profile;
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::ethereum::signatures::generate_ecdsa_key;
|
||||||
|
use crate::ethereum::sync::save_current_block_number;
|
||||||
|
use crate::ethereum::utils::key_to_ethereum_address;
|
||||||
|
use crate::models::attachments::queries::delete_unused_attachments;
|
||||||
|
use crate::models::cleanup::find_orphaned_files;
|
||||||
|
use crate::models::posts::queries::{delete_post, find_extraneous_posts, get_post_by_id};
|
||||||
|
use crate::models::profiles::queries::{
|
||||||
|
delete_profile,
|
||||||
|
get_profile_by_actor_id,
|
||||||
|
get_profile_by_id,
|
||||||
|
reset_subscriptions,
|
||||||
|
};
|
||||||
|
use crate::models::users::queries::{
|
||||||
|
create_invite_code,
|
||||||
|
get_invite_codes,
|
||||||
|
get_user_by_id,
|
||||||
|
};
|
||||||
|
use crate::utils::crypto::{generate_private_key, serialize_private_key};
|
||||||
|
use crate::utils::files::remove_files;
|
||||||
|
|
||||||
|
/// Admin CLI tool
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct Opts {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
pub subcmd: SubCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub enum SubCommand {
|
||||||
|
GenerateRsaKey(GenerateRsaKey),
|
||||||
|
GenerateEthereumAddress(GenerateEthereumAddress),
|
||||||
|
|
||||||
|
GenerateInviteCode(GenerateInviteCode),
|
||||||
|
ListInviteCodes(ListInviteCodes),
|
||||||
|
RefetchActor(RefetchActor),
|
||||||
|
DeleteProfile(DeleteProfile),
|
||||||
|
DeletePost(DeletePost),
|
||||||
|
DeleteExtraneousPosts(DeleteExtraneousPosts),
|
||||||
|
DeleteUnusedAttachments(DeleteUnusedAttachments),
|
||||||
|
UpdateCurrentBlock(UpdateCurrentBlock),
|
||||||
|
DeleteOrphanedFiles(DeleteOrphanedFiles),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate RSA private key
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct GenerateRsaKey;
|
||||||
|
|
||||||
|
impl GenerateRsaKey {
|
||||||
|
pub fn execute(&self) -> () {
|
||||||
|
let private_key = generate_private_key().unwrap();
|
||||||
|
let private_key_str = serialize_private_key(&private_key).unwrap();
|
||||||
|
println!("{}", private_key_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate ethereum address
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct GenerateEthereumAddress;
|
||||||
|
|
||||||
|
impl GenerateEthereumAddress {
|
||||||
|
pub fn execute(&self) -> () {
|
||||||
|
let private_key = generate_ecdsa_key();
|
||||||
|
let address = key_to_ethereum_address(&private_key);
|
||||||
|
println!(
|
||||||
|
"address {:?}; private key {}",
|
||||||
|
address, private_key.display_secret(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate invite code
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct GenerateInviteCode;
|
||||||
|
|
||||||
|
impl GenerateInviteCode {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let invite_code = create_invite_code(db_client).await?;
|
||||||
|
println!("generated invite code: {}", invite_code);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List invite codes
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct ListInviteCodes;
|
||||||
|
|
||||||
|
impl ListInviteCodes {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let invite_codes = get_invite_codes(db_client).await?;
|
||||||
|
if invite_codes.is_empty() {
|
||||||
|
println!("no invite codes found");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
for code in invite_codes {
|
||||||
|
println!("{}", code);
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Re-fetch actor profile by actor ID
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct RefetchActor {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RefetchActor {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let profile = get_profile_by_actor_id(db_client, &self.id).await?;
|
||||||
|
let actor = fetch_actor(&config.instance(), &self.id).await?;
|
||||||
|
update_remote_profile(db_client, &config.media_dir(), profile, actor).await?;
|
||||||
|
println!("profile updated");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete profile
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct DeleteProfile {
|
||||||
|
id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeleteProfile {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &mut impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let profile = get_profile_by_id(db_client, &self.id).await?;
|
||||||
|
let mut maybe_delete_person = None;
|
||||||
|
if profile.is_local() {
|
||||||
|
let user = get_user_by_id(db_client, &profile.id).await?;
|
||||||
|
let activity =
|
||||||
|
prepare_delete_person(db_client, config.instance(), &user).await?;
|
||||||
|
maybe_delete_person = Some(activity);
|
||||||
|
};
|
||||||
|
let deletion_queue = delete_profile(db_client, &profile.id).await?;
|
||||||
|
deletion_queue.process(config).await;
|
||||||
|
// Send Delete(Person) activities
|
||||||
|
if let Some(activity) = maybe_delete_person {
|
||||||
|
activity.deliver().await?;
|
||||||
|
};
|
||||||
|
println!("profile deleted");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete post
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct DeletePost {
|
||||||
|
id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeletePost {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &mut impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let post = get_post_by_id(db_client, &self.id).await?;
|
||||||
|
let deletion_queue = delete_post(db_client, &post.id).await?;
|
||||||
|
deletion_queue.process(config).await;
|
||||||
|
if post.author.is_local() {
|
||||||
|
// Send Delete(Note) activity
|
||||||
|
let author = get_user_by_id(db_client, &post.author.id).await?;
|
||||||
|
prepare_delete_note(db_client, config.instance(), &author, &post)
|
||||||
|
.await?
|
||||||
|
.deliver().await?;
|
||||||
|
};
|
||||||
|
println!("post deleted");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete old remote posts
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct DeleteExtraneousPosts {
|
||||||
|
days: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeleteExtraneousPosts {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &mut impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let created_before = Utc::now() - Duration::days(self.days);
|
||||||
|
let posts = find_extraneous_posts(db_client, &created_before).await?;
|
||||||
|
for post_id in posts {
|
||||||
|
let deletion_queue = delete_post(db_client, &post_id).await?;
|
||||||
|
deletion_queue.process(config).await;
|
||||||
|
println!("post {} deleted", post_id);
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete attachments that don't belong to any post
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct DeleteUnusedAttachments {
|
||||||
|
days: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeleteUnusedAttachments {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let created_before = Utc::now() - Duration::days(self.days);
|
||||||
|
let deletion_queue = delete_unused_attachments(
|
||||||
|
db_client,
|
||||||
|
&created_before,
|
||||||
|
).await?;
|
||||||
|
deletion_queue.process(config).await;
|
||||||
|
println!("unused attachments deleted");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find and delete orphaned files
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct DeleteOrphanedFiles;
|
||||||
|
|
||||||
|
impl DeleteOrphanedFiles {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let media_dir = config.media_dir();
|
||||||
|
let mut files = vec![];
|
||||||
|
for maybe_path in std::fs::read_dir(&media_dir)? {
|
||||||
|
let file_name = maybe_path?.file_name()
|
||||||
|
.to_string_lossy().to_string();
|
||||||
|
files.push(file_name);
|
||||||
|
};
|
||||||
|
println!("found {} files", files.len());
|
||||||
|
let orphaned = find_orphaned_files(db_client, files).await?;
|
||||||
|
if !orphaned.is_empty() {
|
||||||
|
remove_files(orphaned, &media_dir);
|
||||||
|
println!("orphaned files deleted");
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update blockchain synchronization starting block
|
||||||
|
#[derive(Parser)]
|
||||||
|
pub struct UpdateCurrentBlock {
|
||||||
|
number: u64,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
reset_db: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateCurrentBlock {
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
config: &Config,
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
save_current_block_number(&config.storage_dir, self.number)?;
|
||||||
|
if self.reset_db {
|
||||||
|
reset_subscriptions(db_client).await?;
|
||||||
|
};
|
||||||
|
println!("current block updated");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod activitypub;
|
pub mod activitypub;
|
||||||
pub mod atom;
|
pub mod atom;
|
||||||
|
pub mod cli;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod database;
|
pub mod database;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
|
Loading…
Reference in a new issue