zilliz 1.0.0

TUI and CLI tool for managing Zilliz Cloud clusters and Milvus operations
Documentation
use std::io::{self, BufRead, Write};

use anyhow::{bail, Result};
use crossterm::style::Stylize;

use crate::cli::args::AuthCommands;
use crate::config::manager::ConfigManager;

pub fn run(config_mgr: &ConfigManager, cmd: AuthCommands) -> Result<()> {
    match cmd {
        AuthCommands::Status => auth_status(config_mgr),
        AuthCommands::Switch { org_id } => auth_switch(config_mgr, org_id),
    }
}

fn mask_api_key(key: &str) -> String {
    if key.len() >= 16 {
        format!("{}****{}", &key[..4], &key[key.len() - 4..])
    } else {
        "****".to_string()
    }
}

fn auth_status(config_mgr: &ConfigManager) -> Result<()> {
    // Check OAuth login
    if let Some(user) = config_mgr.get_user_info() {
        println!("Authenticated via browser login");
        println!("  User: {} ({})", user.name, user.email);

        let current_org = config_mgr.get_current_org();
        if let Some(ref org) = current_org {
            println!("  Organization: {} ({})", org.name, org.org_id);
            if let Some(ref key) = org.api_key {
                println!("  API Key: {}", mask_api_key(key));
            }
        }

        let orgs = config_mgr.get_all_orgs();
        if !orgs.is_empty() {
            println!();
            println!("Available organizations ({}):", orgs.len());
            let current_id = current_org.as_ref().map(|o| o.org_id.as_str());
            for org in &orgs {
                let line = format!("  {} ({})", org.name, org.org_id);
                if current_id == Some(org.org_id.as_str()) {
                    println!("{}", line.green());
                } else {
                    println!("{}", line);
                }
            }
        }

        return Ok(());
    }

    // Check API key
    if let Some(api_key) = config_mgr.get_credential("api_key") {
        println!("Authenticated via API key");
        println!("  API Key: {}", mask_api_key(&api_key));
        return Ok(());
    }

    println!("Not authenticated. Run `zilliz login` to authenticate.");
    Ok(())
}

fn auth_switch(config_mgr: &ConfigManager, org_id: Option<String>) -> Result<()> {
    let orgs = config_mgr.get_all_orgs();

    if orgs.is_empty() {
        bail!(
            "No organization data available. Log in with browser auth first: zilliz login"
        );
    }

    if orgs.len() == 1 {
        println!("Only one organization available. No switch needed.");
        return Ok(());
    }

    let current_org = config_mgr.get_current_org();
    let current_id = current_org.as_ref().map(|o| o.org_id.as_str());

    let target_org_id = match org_id {
        Some(id) => id,
        None => {
            // Interactive selection
            println!("Available organizations:");
            for (i, org) in orgs.iter().enumerate() {
                let line = format!("  [{}] {} ({})", i + 1, org.name, org.org_id);
                if current_id == Some(org.org_id.as_str()) {
                    println!("{}", line.green());
                } else {
                    println!("{}", line);
                }
            }
            print!("Select organization [1-{}]: ", orgs.len());
            io::stdout().flush()?;

            let mut input = String::new();
            io::stdin().lock().read_line(&mut input)?;
            let idx: usize = input
                .trim()
                .parse()
                .map_err(|_| anyhow::anyhow!("Invalid selection"))?;

            if idx < 1 || idx > orgs.len() {
                bail!("Selection out of range");
            }

            orgs[idx - 1].org_id.clone()
        }
    };

    // Find the org and switch
    let org = orgs
        .iter()
        .find(|o| o.org_id == target_org_id)
        .ok_or_else(|| anyhow::anyhow!("Organization '{}' not found", target_org_id))?;

    if let Some(ref api_key) = org.api_key {
        config_mgr.set_credential("api_key", api_key)?;
        config_mgr.set_credential("org_id", &org.org_id)?;
        config_mgr.set_credential("org_name", &org.name)?;
        config_mgr.clear_context()?;
        println!(
            "Switched to organization: {} ({})",
            org.name, org.org_id
        );
    } else {
        bail!(
            "Organization '{}' has no API key. Try logging in again.",
            org.name
        );
    }

    Ok(())
}