use std::io::{self, BufRead, Write};
use anyhow::{bail, Result};
use crossterm::style::Stylize;
use crate::cli::args::AuthCommands;
use crate::config::manager::ConfigManager;
use crate::model::loader::ModelLoader;
use crate::model::types::{
DEFAULT_CN_CONTROL_PLANE_ENDPOINT, DEFAULT_CONTROL_PLANE_ENDPOINT,
DEFAULT_DEV_CONTROL_PLANE_ENDPOINT,
};
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<()> {
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));
}
}
print_cloud_info(config_mgr);
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(());
}
if let Some(api_key) = config_mgr.get_credential("api_key") {
println!("Authenticated via API key");
println!(" API Key: {}", mask_api_key(&api_key));
print_cloud_info(config_mgr);
return Ok(());
}
println!("Not authenticated. Run `zilliz login` to authenticate.");
Ok(())
}
fn print_cloud_info(config_mgr: &ConfigManager) {
let endpoint = match ModelLoader::load_builtin() {
Ok(models) => {
crate::cli::endpoint::resolve_control_plane_url(config_mgr, &models.control_plane, None)
}
Err(_) => DEFAULT_CONTROL_PLANE_ENDPOINT.to_string(),
};
let label = cloud_label(&endpoint);
println!(" Cloud: {}", label);
println!(" Endpoint: {}", endpoint);
}
fn cloud_label(endpoint: &str) -> &'static str {
if endpoint == DEFAULT_CONTROL_PLANE_ENDPOINT {
"Global"
} else if endpoint == DEFAULT_CN_CONTROL_PLANE_ENDPOINT {
"CN"
} else if endpoint == DEFAULT_DEV_CONTROL_PLANE_ENDPOINT {
"Dev (UAT)"
} else {
"Custom"
}
}
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 => {
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()
}
};
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(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cloud_label_global() {
assert_eq!(cloud_label(DEFAULT_CONTROL_PLANE_ENDPOINT), "Global");
}
#[test]
fn cloud_label_cn() {
assert_eq!(cloud_label(DEFAULT_CN_CONTROL_PLANE_ENDPOINT), "CN");
}
#[test]
fn cloud_label_dev() {
assert_eq!(cloud_label(DEFAULT_DEV_CONTROL_PLANE_ENDPOINT), "Dev (UAT)");
}
#[test]
fn cloud_label_custom() {
assert_eq!(cloud_label("https://staging.example.com"), "Custom");
}
}