smbpndk_cli/account/forgot/
mod.rs

1use anyhow::Result;
2use console::style;
3use dialoguer::{theme::ColorfulTheme, Input, Password};
4use reqwest::{Client, StatusCode};
5use serde::{Deserialize, Serialize};
6use smbpndk_model::CommandResult;
7use smbpndk_networking::smb_base_url_builder;
8use smbpndk_utils::email_validation;
9use spinners::Spinner;
10
11#[derive(Debug, Serialize)]
12struct Args {
13    user: Email,
14}
15
16#[derive(Debug, Serialize)]
17struct Email {
18    email: String,
19}
20
21pub async fn process_forgot() -> Result<CommandResult> {
22    println!("Provide your login credentials.");
23    let email = Input::<String>::with_theme(&ColorfulTheme::default())
24        .with_prompt("Email")
25        .validate_with(|email: &String| email_validation(email))
26        .interact()
27        .unwrap();
28    let mut spinner = Spinner::new(
29        spinners::Spinners::SimpleDotsScrolling,
30        style("Checking email...").green().bold().to_string(),
31    );
32
33    let params = Args {
34        user: Email { email },
35    };
36
37    let response = Client::new()
38        .post(build_smb_forgot_url())
39        .json(&params)
40        .send()
41        .await?;
42
43    match response.status() {
44        StatusCode::OK => {
45            spinner.stop_and_persist(
46                "✅",
47                "Check your email and input your code here.".to_owned(),
48            );
49            input_code().await
50        }
51        _ => Ok(CommandResult {
52            spinner,
53            symbol: "😩".to_owned(),
54            msg: "Something wrong when trying to reset email.".to_owned(),
55        }),
56    }
57}
58
59#[derive(Debug, Serialize)]
60pub struct Param {
61    pub(crate) user: UserUpdatePassword,
62}
63
64#[derive(Debug, Serialize)]
65pub struct UserUpdatePassword {
66    pub(crate) reset_password_token: String,
67    pub(crate) password: String,
68    pub(crate) password_confirmation: String,
69}
70
71async fn input_code() -> Result<CommandResult> {
72    let security_code = Input::<String>::with_theme(&ColorfulTheme::default())
73        .with_prompt("Code")
74        .interact()
75        .unwrap();
76
77    Spinner::new(
78        spinners::Spinners::SimpleDotsScrolling,
79        style("Checking your code...").green().bold().to_string(),
80    )
81    .stop_and_persist("✅", "Great. Now input your new password.".to_owned());
82
83    let new_password = Password::with_theme(&ColorfulTheme::default())
84        .with_prompt("Password")
85        .validate_with(|input: &String| -> Result<(), &str> {
86            if input.len() >= 6 {
87                Ok(())
88            } else {
89                Err("Password must be at least 6 characters")
90            }
91        })
92        .with_confirmation("Confirm password", "Passwords do not match")
93        .interact()
94        .unwrap();
95    let password_confirmation = String::from(&new_password);
96
97    // Should reuse this somehow
98    let params = Param {
99        user: UserUpdatePassword {
100            reset_password_token: security_code,
101            password: new_password,
102            password_confirmation,
103        },
104    };
105
106    let spinner = Spinner::new(
107        spinners::Spinners::SimpleDotsScrolling,
108        style("Updating your password...")
109            .green()
110            .bold()
111            .to_string(),
112    );
113
114    let response = Client::new()
115        .put(build_smb_forgot_url())
116        .json(&params)
117        .send()
118        .await?;
119
120    #[derive(Debug, Serialize, Deserialize)]
121    struct Response {
122        status: i32,
123        email: Option<String>,
124    }
125
126    match response.status() {
127        StatusCode::OK => Ok(CommandResult {
128            spinner,
129            symbol: "✅".to_owned(),
130            msg: "Your password has been updated. Login with your new password.".to_owned(),
131        }),
132        StatusCode::NOT_FOUND => Ok(CommandResult {
133            spinner,
134            symbol: "😩".to_owned(),
135            msg: "URL not found.".to_owned(),
136        }),
137        _ => Ok(CommandResult {
138            spinner,
139            symbol: "😩".to_owned(),
140            msg: "Something wrong when trying to reset email.".to_owned(),
141        }),
142    }
143}
144
145fn build_smb_forgot_url() -> String {
146    let mut url_builder = smb_base_url_builder();
147    url_builder.add_route("v1/users/password");
148    url_builder.build()
149}