Skip to main content

htb_cli/cli/
vpn.rs

1use clap::Subcommand;
2
3use crate::api::HtbClient;
4use crate::output::{self, OutputFormat};
5
6#[derive(Subcommand)]
7pub enum VpnCommand {
8    /// Show current VPN connection status
9    Status,
10    /// List available VPN servers
11    List,
12    /// Switch VPN server
13    Switch {
14        /// Server ID
15        server_id: u32,
16    },
17    /// Download .ovpn file
18    Download {
19        /// Server ID (uses default if omitted)
20        server_id: Option<u32>,
21    },
22}
23
24pub async fn handle(
25    client: &HtbClient,
26    cmd: VpnCommand,
27    format: OutputFormat,
28) -> anyhow::Result<()> {
29    match cmd {
30        VpnCommand::Status => {
31            let status = client.vpn().status().await?;
32            if status.is_empty() {
33                output::print_message("Not connected to VPN.");
34            } else {
35                match format {
36                    OutputFormat::Json => output::json::print_json(&status),
37                    OutputFormat::Table => {
38                        for entry in &status {
39                            output::print_message(
40                                &serde_json::to_string_pretty(entry)
41                                    .expect("serde_json::Value always serializes"),
42                            );
43                        }
44                    }
45                }
46            }
47        }
48
49        VpnCommand::List => {
50            let connections = client.vpn().connections().await?;
51            output::print_list(&connections, format);
52        }
53
54        VpnCommand::Switch { server_id } => {
55            let resp = client.vpn().switch(server_id).await?;
56            if let Some(msg) = &resp.message {
57                output::print_message(msg);
58            }
59            if let Some(server) = &resp.data {
60                output::print_message(&format!(
61                    "Switched to {} ({} clients)",
62                    server.friendly_name, server.current_clients
63                ));
64            }
65        }
66
67        VpnCommand::Download { server_id } => {
68            let sid = server_id.unwrap_or(1);
69            let bytes = client.vpn().download_ovpn(sid).await?;
70            let filename = format!("lab-vpn-{sid}.ovpn");
71            std::fs::write(&filename, &bytes)?;
72            output::print_message(&format!("Downloaded {} ({} bytes)", filename, bytes.len()));
73        }
74    }
75    Ok(())
76}