db-sync 0.1.1

A secure and stable database backup synchronization system with automatic file transfer and verification
use clap::{Parser, Subcommand};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::time::{SystemTime, UNIX_EPOCH};

/// CLI tool: Generates authentication tokens for the DB-Sync system
#[derive(Parser, Debug)]
#[command(
    name = "gen-auth-token",
    about = "Generates 512-bit authentication tokens for the DB-Sync system",
    version = "0.1.0",
    author = "DB-Sync Team"
)]
struct Args {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
    /// Generate a token for a single system
    Generate {
        /// IP address of the system
        #[arg(long)]
        system_ip: String,
        
        /// System name (optional)
        #[arg(long)]
        system_name: Option<String>,
        
        /// Custom secret key (optional, uses built-in key by default)
        #[arg(long)]
        secret_key: Option<String>,
        
        /// Output format: json or text
        #[arg(long, default_value = "text")]
        format: String,
    },
    
    /// Generate tokens for multiple systems in batch
    Batch {
        /// JSON configuration file containing system information
        #[arg(long)]
        config_file: String,
        
        /// Output format: json or text
        #[arg(long, default_value = "text")]
        format: String,
    },
    
    /// Validate token format
    Validate {
        /// The token to validate
        #[arg(long)]
        token: String,
    },
    
    /// Generate example configuration
    Example,
}

/// Generate a 512-bit secure token
fn generate_secure_token_512(system_ip: &str, system_name: &str, secret_key: &str) -> String {
    let mut token = String::new();
    
    // Generate multiple hash values to form a 512-bit token (128 hex characters)
    for i in 0..8 {
        let mut hasher = DefaultHasher::new();
        
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        
        // Add different salt for each part
        let combined = format!("{}:{}:{}:{}:{}", system_ip, system_name, secret_key, timestamp, i);
        combined.hash(&mut hasher);
        
        token.push_str(&format!("{:016x}", hasher.finish()));
    }
    
    // Ensure token length is 128 hex characters (512 bits)
    token.truncate(128);
    token
}

/// Validate token format
fn validate_token_format(token: &str) -> bool {
    // Check if length is 128 characters (512 bits)
    if token.len() != 128 {
        return false;
    }
    
    // Check if it contains only valid hexadecimal characters
    token.chars().all(|c| c.is_ascii_hexdigit())
}

fn main() {
    let args = Args::parse();
    
    match args.command {
        Commands::Generate { system_ip, system_name, secret_key, format } => {
            let system_name = system_name.unwrap_or_else(|| "Unknown System".to_string());
            let secret_key = secret_key.unwrap_or_else(|| "db_sync_default_secret_key".to_string());
            
            let token = generate_secure_token_512(&system_ip, &system_name, &secret_key);
            
            match format.as_str() {
                "json" => {
                    println!("{}", serde_json::json!({
                        "system_ip": system_ip,
                        "system_name": system_name,
                        "token": token,
                        "token_length_bits": token.len() * 4,
                        "generated_at": SystemTime::now()
                            .duration_since(UNIX_EPOCH)
                            .unwrap()
                            .as_secs()
                    }));
                }
                "text" => {
                    println!("=== DB-Sync Authentication Token Generation ===");
                    println!("System IP: {}", system_ip);
                    println!("System Name: {}", system_name);
                    println!("Token Length: {} bits", token.len() * 4);
                    println!("Generated Token: {}", token);
                    println!("Generated At: {}", SystemTime::now()
                        .duration_since(UNIX_EPOCH)
                        .unwrap()
                        .as_secs());
                }
                _ => {
                    eprintln!("Error: Unsupported output format '{}'", format);
                    std::process::exit(1);
                }
            }
        }
        
        Commands::Batch { config_file, format } => {
            // Read configuration file
            let config_content = match std::fs::read_to_string(&config_file) {
                Ok(content) => content,
                Err(e) => {
                    eprintln!("错误:无法读取配置文件 '{}': {}", config_file, e);
                    std::process::exit(1);
                }
            };
            
            // Parse JSON configuration
            let config: serde_json::Value = match serde_json::from_str(&config_content) {
                Ok(config) => config,
                Err(e) => {
                    eprintln!("错误:无法解析配置文件: {}", e);
                    std::process::exit(1);
                }
            };
            
            let systems = config.get("systems").and_then(|v| v.as_array());
            
            if systems.is_none() {
                eprintln!("Error: 'systems' array missing in configuration file");
                std::process::exit(1);
            }
            
            let default_secret = config.get("secret_key")
                .and_then(|v| v.as_str())
                .unwrap_or("db_sync_default_secret_key");
            
            match format.as_str() {
                "json" => {
                    let mut results = serde_json::Map::new();
                    
                    for system in systems.unwrap() {
                        let system_ip = system.get("system_ip").and_then(|v| v.as_str());
                        let system_name = system.get("system_name").and_then(|v| v.as_str());
                        let secret_key = system.get("secret_key").and_then(|v| v.as_str()).unwrap_or(default_secret);
                        
                        if let (Some(ip), Some(name)) = (system_ip, system_name) {
                            let token = generate_secure_token_512(ip, name, secret_key);
                            results.insert(ip.to_string(), serde_json::json!({
                                "system_name": name,
                                "token": token,
                                "token_length_bits": token.len() * 4
                            }));
                        }
                    }
                    
                    println!("{}", serde_json::json!({
                        "generated_tokens": results,
                        "generated_at": SystemTime::now()
                            .duration_since(UNIX_EPOCH)
                            .unwrap()
                            .as_secs()
                    }));
                }
                "text" => {
                    println!("=== DB-Sync Batch Token Generation ===");
                    for system in systems.unwrap() {
                        let system_ip = system.get("system_ip").and_then(|v| v.as_str());
                        let system_name = system.get("system_name").and_then(|v| v.as_str());
                        let secret_key = system.get("secret_key").and_then(|v| v.as_str()).unwrap_or(default_secret);
                        
                        if let (Some(ip), Some(name)) = (system_ip, system_name) {
                            let token = generate_secure_token_512(ip, name, secret_key);
                            println!("\nSystem: {}", name);
                            println!("IP: {}", ip);
                            println!("Token: {}", token);
                            println!("Length: {} bits", token.len() * 4);
                        }
                    }
                }
                _ => {
                    eprintln!("Error: Unsupported output format '{}'", format);
                    std::process::exit(1);
                }
            }
        }
        
        Commands::Validate { token } => {
            let is_valid = validate_token_format(&token);
            
            if is_valid {
                println!("✓ Token format is valid");
                println!("  Length: {} characters ({} bits)", token.len(), token.len() * 4);
                println!("  Format: Hexadecimal string");
            } else {
                println!("✗ Token format is invalid");
                if token.len() != 128 {
                    println!("  Length Error: Expected 128 characters, got {}", token.len());
                }
                if !token.chars().all(|c| c.is_ascii_hexdigit()) {
                    println!("  Character Error: Contains non-hexadecimal characters");
                }
                std::process::exit(1);
            }
        }
        
        Commands::Example => {
            let example_config = serde_json::json!({
                "secret_key": "your_custom_secret_key_here",
                "systems": [
                    {
                        "system_ip": "192.168.1.100",
                        "system_name": "HR System",
                        "secret_key": "optional_override_for_this_system"
                    },
                    {
                        "system_ip": "192.168.1.101",
                        "system_name": "Asset Management System"
                    },
                    {
                        "system_ip": "192.168.1.102",
                        "system_name": "Database Server"
                    }
                ]
            });
            
            println!("=== Example Configuration File (batch_config.json) ===");
            println!("{}", serde_json::to_string_pretty(&example_config).unwrap());
            
            println!("\n=== Usage Examples ===");
            println!("# Generate a token for a single system");
            println!("cargo run --bin gen-auth-token generate --system-ip 192.168.1.100 --system-name 'HR System'");
            println!();
            println!("# Generate tokens in batch");
            println!("cargo run --bin gen-auth-token batch --config-file batch_config.json --format json");
            println!();
            println!("# Validate a token");
            println!("cargo run --bin gen-auth-token validate --token 'your_token_here'");
        }
    }
}