1use std::sync::Arc;
2
3use clap::Subcommand;
4
5use crate::api::HtbClient;
6use crate::cache::Cache;
7use crate::output::OutputFormat;
8
9#[derive(Subcommand)]
10pub enum AuthCommand {
11 Login,
13 Status,
15 Logout,
17}
18
19pub async fn handle(
20 cmd: AuthCommand,
21 format: OutputFormat,
22 cache: &Arc<Cache>,
23) -> anyhow::Result<()> {
24 match cmd {
25 AuthCommand::Login => login(cache).await,
26 AuthCommand::Status => status(format).await,
27 AuthCommand::Logout => logout(cache),
28 }
29}
30
31async fn login(cache: &Cache) -> anyhow::Result<()> {
32 println!("Enter your HTB API token (from https://app.hackthebox.com/account-settings):");
33 let token = rpassword::read_password()?;
34 let token = token.trim().to_string();
35
36 if token.is_empty() {
37 anyhow::bail!("Token cannot be empty");
38 }
39
40 let client = HtbClient::new(token.clone());
41 let user = client.user().current().await?;
42
43 crate::config::save_token(&token)?;
44 cache.clear();
45 println!(
46 "Authenticated as {} ({})",
47 user.name,
48 if user.is_vip { "VIP" } else { "Free" }
49 );
50 Ok(())
51}
52
53async fn status(format: OutputFormat) -> anyhow::Result<()> {
54 let token = crate::config::read_token()?;
55 let client = HtbClient::new(token);
56 let user = client.user().current().await?;
57
58 let fields = vec![
59 ("Username", user.name.clone()),
60 ("ID", user.id.to_string()),
61 (
62 "Subscription",
63 user.subscription_type
64 .clone()
65 .unwrap_or_else(|| "Free".into()),
66 ),
67 ("VIP", if user.is_vip { "Yes" } else { "No" }.into()),
68 ("Verified", if user.verified { "Yes" } else { "No" }.into()),
69 ];
70
71 crate::output::print_detail(&user, format, &fields);
72 Ok(())
73}
74
75fn logout(cache: &Cache) -> anyhow::Result<()> {
76 crate::config::remove_token()?;
77 cache.clear();
78 println!("Token removed.");
79 Ok(())
80}