nsg_cli/commands/
login.rs

1use 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}