Skip to main content

nsg_cli/commands/
login.rs

1use crate::client::NsgClient;
2use crate::config::Credentials;
3use anyhow::Result;
4use clap::Args;
5use colored::Colorize;
6use std::io::{self, Write};
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!(
58                        "Get credentials at: {}",
59                        "https://www.nsgportal.org/".cyan()
60                    );
61                    anyhow::bail!("Login failed");
62                }
63            }
64        }
65
66        credentials.save()?;
67
68        println!();
69        println!("{}", "=".repeat(60).green());
70        println!(
71            "{} {}",
72            "✓".green().bold(),
73            "Login successful!".green().bold()
74        );
75        println!("{}", "=".repeat(60).green());
76        println!();
77        println!(
78            "Credentials saved to: {}",
79            Credentials::credentials_location().cyan()
80        );
81        println!();
82        println!("You can now use:");
83        println!("  {} - List your NSG jobs", "nsg list".cyan());
84        println!("  {} - Check job status", "nsg status <job_id>".cyan());
85        println!(
86            "  {} - Submit a new job",
87            "nsg submit <zip_file> --tool <tool>".cyan()
88        );
89        println!(
90            "  {} - Download job results",
91            "nsg download <job_id>".cyan()
92        );
93        println!();
94
95        Ok(())
96    }
97
98    fn get_or_prompt_username(&self) -> Result<String> {
99        if let Some(username) = &self.username {
100            return Ok(username.clone());
101        }
102
103        print!("NSG Username: ");
104        io::stdout().flush()?;
105
106        let mut input = String::new();
107        io::stdin().read_line(&mut input)?;
108        let username = input.trim().to_string();
109
110        if username.is_empty() {
111            anyhow::bail!("Username cannot be empty");
112        }
113
114        Ok(username)
115    }
116
117    fn get_or_prompt_password(&self) -> Result<String> {
118        if let Some(password) = &self.password {
119            return Ok(password.clone());
120        }
121
122        print!("NSG Password: ");
123        io::stdout().flush()?;
124        let password = rpassword::read_password()?;
125
126        if password.is_empty() {
127            anyhow::bail!("Password cannot be empty");
128        }
129
130        Ok(password)
131    }
132
133    fn get_or_prompt_app_key(&self) -> Result<String> {
134        if let Some(app_key) = &self.app_key {
135            return Ok(app_key.clone());
136        }
137
138        print!("NSG Application Key: ");
139        io::stdout().flush()?;
140
141        let mut input = String::new();
142        io::stdin().read_line(&mut input)?;
143        let app_key = input.trim().to_string();
144
145        if app_key.is_empty() {
146            anyhow::bail!("Application key cannot be empty");
147        }
148
149        Ok(app_key)
150    }
151}