nsg_cli/commands/
login.rs1use anyhow::Result;
2use clap::Args;
3use colored::Colorize;
4use std::io::{self, Write};
5use crate::client::NsgClient;
6use crate::config::Credentials;
7
8#[derive(Debug, Args)]
9pub struct LoginCommand {
10 #[arg(short, long, help = "NSG username")]
11 username: Option<String>,
12
13 #[arg(short, long, help = "NSG password")]
14 password: Option<String>,
15
16 #[arg(short, long, help = "NSG application key")]
17 app_key: Option<String>,
18
19 #[arg(long, help = "Skip connection test")]
20 no_verify: bool,
21}
22
23impl LoginCommand {
24 pub fn execute(self) -> Result<()> {
25 println!("{}", "NSG Login".bold().cyan());
26 println!("{}", "=".repeat(60).cyan());
27 println!();
28
29 let username = self.get_or_prompt_username()?;
30 let password = self.get_or_prompt_password()?;
31 let app_key = self.get_or_prompt_app_key()?;
32
33 println!();
34 println!("{} Saving credentials...", "→".cyan());
35
36 let credentials = Credentials::new(username, password, app_key);
37
38 if !self.no_verify {
39 println!("{} Testing connection to NSG...", "→".cyan());
40 let client = NsgClient::new(credentials.clone())?;
41
42 match client.test_connection() {
43 Ok(_) => {
44 println!("{} Connection successful!", "✓".green().bold());
45 }
46 Err(e) => {
47 eprintln!();
48 eprintln!("{} {}", "✗".red().bold(), "Authentication failed!".red());
49 eprintln!();
50 eprintln!("Error: {}", e);
51 eprintln!();
52 eprintln!("Please check your credentials:");
53 eprintln!(" 1. Username and password are correct");
54 eprintln!(" 2. Application key is valid");
55 eprintln!(" 3. Your NSG account is active");
56 eprintln!();
57 eprintln!("Get credentials at: {}", "https://www.nsgportal.org/".cyan());
58 anyhow::bail!("Login failed");
59 }
60 }
61 }
62
63 credentials.save()?;
64
65 println!();
66 println!("{}", "=".repeat(60).green());
67 println!("{} {}", "✓".green().bold(), "Login successful!".green().bold());
68 println!("{}", "=".repeat(60).green());
69 println!();
70 println!("Credentials saved to: {}", Credentials::credentials_location().cyan());
71 println!();
72 println!("You can now use:");
73 println!(" {} - List your NSG jobs", "nsg list".cyan());
74 println!(" {} - Check job status", "nsg status <job_id>".cyan());
75 println!(" {} - Submit a new job", "nsg submit <zip_file> --tool <tool>".cyan());
76 println!(" {} - Download job results", "nsg download <job_id>".cyan());
77 println!();
78
79 Ok(())
80 }
81
82 fn get_or_prompt_username(&self) -> Result<String> {
83 if let Some(username) = &self.username {
84 return Ok(username.clone());
85 }
86
87 print!("NSG Username: ");
88 io::stdout().flush()?;
89
90 let mut input = String::new();
91 io::stdin().read_line(&mut input)?;
92 let username = input.trim().to_string();
93
94 if username.is_empty() {
95 anyhow::bail!("Username cannot be empty");
96 }
97
98 Ok(username)
99 }
100
101 fn get_or_prompt_password(&self) -> Result<String> {
102 if let Some(password) = &self.password {
103 return Ok(password.clone());
104 }
105
106 print!("NSG Password: ");
107 io::stdout().flush()?;
108 let password = rpassword::read_password()?;
109
110 if password.is_empty() {
111 anyhow::bail!("Password cannot be empty");
112 }
113
114 Ok(password)
115 }
116
117 fn get_or_prompt_app_key(&self) -> Result<String> {
118 if let Some(app_key) = &self.app_key {
119 return Ok(app_key.clone());
120 }
121
122 print!("NSG Application Key: ");
123 io::stdout().flush()?;
124
125 let mut input = String::new();
126 io::stdin().read_line(&mut input)?;
127 let app_key = input.trim().to_string();
128
129 if app_key.is_empty() {
130 anyhow::bail!("Application key cannot be empty");
131 }
132
133 Ok(app_key)
134 }
135}