Add Cleanup binary for fixing broken DB/file relations
This commit is contained in:
143
src/bin/cleanup_files.rs
Normal file
143
src/bin/cleanup_files.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use image_api::cleanup::{
|
||||
resolve_missing_files, validate_file_types, CleanupConfig, DatabaseUpdater,
|
||||
};
|
||||
use image_api::database::{SqliteExifDao, SqliteFavoriteDao};
|
||||
use image_api::tags::SqliteTagDao;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "cleanup_files")]
|
||||
#[command(about = "File cleanup and fix utility for ImageApi", long_about = None)]
|
||||
struct Args {
|
||||
#[arg(long, help = "Preview changes without making them")]
|
||||
dry_run: bool,
|
||||
|
||||
#[arg(long, help = "Auto-fix all issues without prompting")]
|
||||
auto_fix: bool,
|
||||
|
||||
#[arg(long, help = "Skip phase 1 (missing file resolution)")]
|
||||
skip_phase1: bool,
|
||||
|
||||
#[arg(long, help = "Skip phase 2 (file type validation)")]
|
||||
skip_phase2: bool,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
// Initialize logging
|
||||
env_logger::init();
|
||||
|
||||
// Load environment variables
|
||||
dotenv::dotenv()?;
|
||||
|
||||
// Parse CLI arguments
|
||||
let args = Args::parse();
|
||||
|
||||
// Get base path from environment
|
||||
let base_path = dotenv::var("BASE_PATH")?;
|
||||
let base = PathBuf::from(&base_path);
|
||||
|
||||
println!("File Cleanup and Fix Utility");
|
||||
println!("============================");
|
||||
println!("Base path: {}", base.display());
|
||||
println!("Dry run: {}", args.dry_run);
|
||||
println!("Auto fix: {}", args.auto_fix);
|
||||
println!();
|
||||
|
||||
// Pre-flight checks
|
||||
if !base.exists() {
|
||||
eprintln!("Error: Base path does not exist: {}", base.display());
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if !base.is_dir() {
|
||||
eprintln!("Error: Base path is not a directory: {}", base.display());
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Create configuration
|
||||
let config = CleanupConfig {
|
||||
base_path: base,
|
||||
dry_run: args.dry_run,
|
||||
auto_fix: args.auto_fix,
|
||||
};
|
||||
|
||||
// Create DAOs
|
||||
println!("Connecting to database...");
|
||||
let tag_dao: Arc<Mutex<dyn image_api::tags::TagDao>> =
|
||||
Arc::new(Mutex::new(SqliteTagDao::default()));
|
||||
let exif_dao: Arc<Mutex<dyn image_api::database::ExifDao>> =
|
||||
Arc::new(Mutex::new(SqliteExifDao::new()));
|
||||
let favorites_dao: Arc<Mutex<dyn image_api::database::FavoriteDao>> =
|
||||
Arc::new(Mutex::new(SqliteFavoriteDao::new()));
|
||||
|
||||
// Create database updater
|
||||
let mut db_updater = DatabaseUpdater::new(tag_dao, exif_dao, favorites_dao);
|
||||
|
||||
println!("✓ Database connected\n");
|
||||
|
||||
// Track overall statistics
|
||||
let mut total_issues_found = 0;
|
||||
let mut total_issues_fixed = 0;
|
||||
let mut total_errors = Vec::new();
|
||||
|
||||
// Phase 1: Missing file resolution
|
||||
if !args.skip_phase1 {
|
||||
match resolve_missing_files(&config, &mut db_updater) {
|
||||
Ok(stats) => {
|
||||
total_issues_found += stats.issues_found;
|
||||
total_issues_fixed += stats.issues_fixed;
|
||||
total_errors.extend(stats.errors);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Phase 1 failed: {:?}", e);
|
||||
total_errors.push(format!("Phase 1 error: {}", e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Phase 1: Skipped (--skip-phase1)");
|
||||
}
|
||||
|
||||
// Phase 2: File type validation
|
||||
if !args.skip_phase2 {
|
||||
match validate_file_types(&config, &mut db_updater) {
|
||||
Ok(stats) => {
|
||||
total_issues_found += stats.issues_found;
|
||||
total_issues_fixed += stats.issues_fixed;
|
||||
total_errors.extend(stats.errors);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Phase 2 failed: {:?}", e);
|
||||
total_errors.push(format!("Phase 2 error: {}", e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("\nPhase 2: Skipped (--skip-phase2)");
|
||||
}
|
||||
|
||||
// Final summary
|
||||
println!("\n============================");
|
||||
println!("Cleanup Complete!");
|
||||
println!("============================");
|
||||
println!("Total issues found: {}", total_issues_found);
|
||||
if config.dry_run {
|
||||
println!("Total issues that would be fixed: {}", total_issues_found);
|
||||
} else {
|
||||
println!("Total issues fixed: {}", total_issues_fixed);
|
||||
}
|
||||
|
||||
if !total_errors.is_empty() {
|
||||
println!("\nErrors encountered:");
|
||||
for (i, error) in total_errors.iter().enumerate() {
|
||||
println!(" {}. {}", i + 1, error);
|
||||
}
|
||||
println!("\nSome operations failed. Review errors above.");
|
||||
} else {
|
||||
println!("\n✓ No errors encountered");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user