1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
mod browser_auth;
mod flags_auth;
mod types;
pub mod util;

use anyhow::Result;
use clap::Parser;

use self::browser_auth::browser_login;
use self::flags_auth::flags_login;
use crate::state::State;
use crate::utils::in_path;

const WEB_AUTH_URL: &str = "https://console.hop.io/cli-auth";
const PAT_FALLBACK_URL: &str = "https://console.hop.io/settings/pats";

#[derive(Debug, Parser, Default, PartialEq, Eq)]
#[clap(about = "Login to Hop")]
pub struct Options {
    #[clap(
        long,
        help = "Project Token or Personal Authorization Token, you can use `--token=` to take the token from stdin"
    )]
    token: Option<String>,
    #[clap(long, help = "Email")]
    email: Option<String>,
    #[clap(
        long,
        help = "Password, you can use `--password=` to take the token from stdin"
    )]
    password: Option<String>,
}

pub async fn handle(options: Options, state: State) -> Result<()> {
    let init_token = if Options::default() != options {
        flags_login(options, state.http.clone()).await
    } else if let Ok(env_token) = std::env::var("HOP_TOKEN") {
        env_token
    } else {
        browser_login().await
    };

    token(&init_token, state).await
}

pub async fn token(token: &str, mut state: State) -> Result<()> {
    state.login(Some(token.to_string())).await?;

    // safe to unwrap here
    let authorized = state.ctx.current.clone().unwrap();

    if Some(authorized.id.clone()) == state.ctx.default_user {
        log::info!(
            "Nothing was changed. You are already logged in as: `{}` ({})",
            authorized.name,
            authorized.email
        );
    } else {
        // output the login info
        log::info!("Logged in as: `{}` ({})", authorized.name, authorized.email);

        state.ctx.default_project = authorized.projects.first().map(|x| x.id.clone());
        state.ctx.default_user = Some(authorized.id.clone());
        state.ctx.save().await?;
    }

    // save the state
    state
        .auth
        .authorized
        .insert(authorized.id.clone(), token.to_string());
    state.auth.save().await?;

    if !state.is_ci
        && in_path("docker").await
        && dialoguer::Confirm::new()
            .with_prompt("Docker was detected, would you like to login to the Hop registry?")
            .default(false)
            .interact()?
    {
        super::docker::login_new(&authorized.email, token).await?;
    }

    Ok(())
}