6.0 KiB · text History 1ce9641
use clap::{Parser, Subcommand};
use sea_orm::DatabaseConnection;
use std::sync::Arc;
use dms::{auth, config::{Config, create_db_connection}};

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(name = "registration-token")]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Create a new registration token
    Create {
        /// Email to restrict token to (optional)
        #[arg(short, long)]
        email: Option<String>,
        /// Number of days until token expires
        #[arg(short, long, default_value = "7")]
        days: i64,
        /// User ID of the admin creating this token (optional)
        #[arg(short, long)]
        created_by: Option<i64>,
    },
    /// List all registration tokens
    List,
    /// Show detailed information about a token
    Show {
        /// Token to show
        token: String,
    },
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize logging
    tracing_subscriber::fmt::init();

    let cli = Cli::parse();

    // Load configuration
    let config = Config::from_env()?;
    let db = Arc::new(create_db_connection(&config.database_url).await?);

    match cli.command {
        Commands::Create { email, days, created_by } => {
            create_token(&db, email, days, created_by).await?;
        }
        Commands::List => {
            list_tokens(&db).await?;
        }
        Commands::Show { token } => {
            show_token(&db, &token).await?;
        }
    }

    Ok(())
}

async fn create_token(
    db: &DatabaseConnection,
    email: Option<String>,
    days: i64,
    created_by: Option<i64>,
) -> Result<(), Box<dyn std::error::Error>> {
    let token = auth::create_registration_token(db, created_by, email.clone(), days).await?;

    println!("✅ Registration token created successfully!");
    println!();
    println!("Token: {}", token.token);
    println!("Email: {}", email.unwrap_or_else(|| "Any".to_string()));
    println!("Expires: {}", token.expires_at);
    println!("Created by: {}", created_by.map_or_else(|| "System".to_string(), |id| id.to_string()));
    println!();
    println!("🔗 Registration URL:");
    println!("   http://localhost:3007/register?token={}", token.token);

    Ok(())
}

async fn list_tokens(db: &DatabaseConnection) -> Result<(), Box<dyn std::error::Error>> {
    use sea_orm::*;
    use dms::entities::registration_tokens;

    let tokens = registration_tokens::Entity::find()
        .order_by_desc(registration_tokens::Column::CreatedAt)
        .all(db)
        .await?;

    if tokens.is_empty() {
        println!("No registration tokens found.");
        return Ok(());
    }

    println!("Registration Tokens:");
    println!("┌────────────────────────────────────────┬──────────────────┬──────────┬─────────────────────┐");
    println!("│ Token                                  │ Email            │ Status   │ Expires             │");
    println!("├────────────────────────────────────────┼──────────────────┼──────────┼─────────────────────┤");

    for token in tokens {
        let email = token.email.as_deref().unwrap_or("Any");
        let status = if token.is_used {
            "Used"
        } else if token.expires_at < chrono::DateTime::<chrono::FixedOffset>::from(chrono::Utc::now()) {
            "Expired"
        } else {
            "Active"
        };
        
        println!(
            "│ {:<38} │ {:<16} │ {:<8} │ {:<19} │",
            &token.token[..38.min(token.token.len())],
            &email[..16.min(email.len())],
            status,
            token.expires_at.format("%Y-%m-%d %H:%M:%S")
        );
    }
    
    println!("└────────────────────────────────────────┴──────────────────┴──────────┴─────────────────────┘");

    Ok(())
}

async fn show_token(
    db: &DatabaseConnection,
    token: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    use sea_orm::*;
    use dms::entities::registration_tokens;

    let token_model = registration_tokens::Entity::find()
        .filter(registration_tokens::Column::Token.eq(token))
        .one(db)
        .await?;

    match token_model {
        Some(token) => {
            println!("Registration Token Details:");
            println!();
            println!("Token: {}", token.token);
            println!("Email: {}", token.email.unwrap_or_else(|| "Any".to_string()));
            println!("Status: {}", if token.is_used {
                "Used"
            } else if token.expires_at < chrono::DateTime::<chrono::FixedOffset>::from(chrono::Utc::now()) {
                "Expired"
            } else {
                "Active"
            });
            println!("Created: {}", token.created_at);
            println!("Expires: {}", token.expires_at);
            println!("Created by: {}", token.created_by_user_id.map_or_else(|| "System".to_string(), |id| id.to_string()));
            
            if token.is_used {
                println!("Used by: {}", token.used_by_user_id.map_or_else(|| "Unknown".to_string(), |id| id.to_string()));
                if let Some(used_at) = token.used_at {
                    println!("Used at: {}", used_at);
                }
            }
            
            println!();
            println!("🔗 Registration URL:");
            println!("   http://localhost:3007/register?token={}", token.token);
        }
        None => {
            println!("❌ Token not found: {}", token);
        }
    }

    Ok(())
}