use crate::client::NsgClient;
use crate::config::Credentials;
use anyhow::Result;
use clap::Args;
use colored::Colorize;
use std::io::{self, Write};
#[derive(Debug, Args)]
pub struct LoginCommand {
#[arg(short, long, help = "NSG username")]
username: Option<String>,
#[arg(short, long, help = "NSG password")]
password: Option<String>,
#[arg(short, long, help = "NSG application key")]
app_key: Option<String>,
#[arg(long, help = "Skip connection test")]
no_verify: bool,
}
impl LoginCommand {
pub fn execute(self) -> Result<()> {
println!("{}", "NSG Login".bold().cyan());
println!("{}", "=".repeat(60).cyan());
println!();
let username = self.get_or_prompt_username()?;
let password = self.get_or_prompt_password()?;
let app_key = self.get_or_prompt_app_key()?;
println!();
println!("{} Saving credentials...", "→".cyan());
let credentials = Credentials::new(username, password, app_key);
if !self.no_verify {
println!("{} Testing connection to NSG...", "→".cyan());
let client = NsgClient::new(credentials.clone())?;
match client.test_connection() {
Ok(_) => {
println!("{} Connection successful!", "✓".green().bold());
}
Err(e) => {
eprintln!();
eprintln!("{} {}", "✗".red().bold(), "Authentication failed!".red());
eprintln!();
eprintln!("Error: {}", e);
eprintln!();
eprintln!("Please check your credentials:");
eprintln!(" 1. Username and password are correct");
eprintln!(" 2. Application key is valid");
eprintln!(" 3. Your NSG account is active");
eprintln!();
eprintln!(
"Get credentials at: {}",
"https://www.nsgportal.org/".cyan()
);
anyhow::bail!("Login failed");
}
}
}
credentials.save()?;
println!();
println!("{}", "=".repeat(60).green());
println!(
"{} {}",
"✓".green().bold(),
"Login successful!".green().bold()
);
println!("{}", "=".repeat(60).green());
println!();
println!(
"Credentials saved to: {}",
Credentials::credentials_location().cyan()
);
println!();
println!("You can now use:");
println!(" {} - List your NSG jobs", "nsg list".cyan());
println!(" {} - Check job status", "nsg status <job_id>".cyan());
println!(
" {} - Submit a new job",
"nsg submit <zip_file> --tool <tool>".cyan()
);
println!(
" {} - Download job results",
"nsg download <job_id>".cyan()
);
println!();
Ok(())
}
fn get_or_prompt_username(&self) -> Result<String> {
if let Some(username) = &self.username {
return Ok(username.clone());
}
print!("NSG Username: ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let username = input.trim().to_string();
if username.is_empty() {
anyhow::bail!("Username cannot be empty");
}
Ok(username)
}
fn get_or_prompt_password(&self) -> Result<String> {
if let Some(password) = &self.password {
return Ok(password.clone());
}
print!("NSG Password: ");
io::stdout().flush()?;
let password = rpassword::read_password()?;
if password.is_empty() {
anyhow::bail!("Password cannot be empty");
}
Ok(password)
}
fn get_or_prompt_app_key(&self) -> Result<String> {
if let Some(app_key) = &self.app_key {
return Ok(app_key.clone());
}
print!("NSG Application Key: ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let app_key = input.trim().to_string();
if app_key.is_empty() {
anyhow::bail!("Application key cannot be empty");
}
Ok(app_key)
}
}