1 use anyhow::Result;
2 use console::style;
3 use dialoguer::{theme::ColorfulTheme, Input, Password};
4 use reqwest::{Client, StatusCode};
5 use serde::{Deserialize, Serialize};
6 use smbpndk_model::CommandResult;
7 use smbpndk_networking::constants::BASE_URL;
8 use smbpndk_utils::email_validation;
9 use spinners::Spinner;
10
11 #[derive(Debug, Serialize)]
12 struct Args {
13 user: Email,
14 }
15
16 #[derive(Debug, Serialize)]
17 struct Email {
18 email: String,
19 }
20
21 pub 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([BASE_URL, "/v1/users/password"].join(""))
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 async fn input_code() -> Result<CommandResult> {
60 let security_code = Input::<String>::with_theme(&ColorfulTheme::default())
61 .with_prompt("Code")
62 .interact()
63 .unwrap();
64
65 Spinner::new(
66 spinners::Spinners::SimpleDotsScrolling,
67 style("Checking your code...").green().bold().to_string(),
68 )
69 .stop_and_persist("✅", "Great. Now input your new password.".to_owned());
70
71 let new_password = Password::with_theme(&ColorfulTheme::default())
72 .with_prompt("Password")
73 .validate_with(|input: &String| -> Result<(), &str> {
74 if input.len() >= 6 {
75 Ok(())
76 } else {
77 Err("Password must be at least 6 characters")
78 }
79 })
80 .with_confirmation("Confirm password", "Passwords do not match")
81 .interact()
82 .unwrap();
83 let password_confirmation = String::from(&new_password);
84
85 #[derive(Debug, Serialize)]
86 struct Param {
87 user: User,
88 }
89
90 #[derive(Debug, Serialize)]
91 struct User {
92 reset_password_token: String,
93 password: String,
94 password_confirmation: String,
95 }
96
97 let params = Param {
98 user: User {
99 reset_password_token: security_code,
100 password: new_password,
101 password_confirmation,
102 },
103 };
104
105 let spinner = Spinner::new(
106 spinners::Spinners::SimpleDotsScrolling,
107 style("Updating your password...")
108 .green()
109 .bold()
110 .to_string(),
111 );
112
113 let response = Client::new()
114 .put([BASE_URL, "/v1/users/password"].join(""))
115 .json(&params)
116 .send()
117 .await?;
118
119 #[derive(Debug, Serialize, Deserialize)]
120 struct Response {
121 status: i32,
122 email: Option<String>,
123 }
124
125 match response.status() {
126 StatusCode::OK => Ok(CommandResult {
127 spinner,
128 symbol: "✅".to_owned(),
129 msg: "Your password has been updated. Login with your new password.".to_owned(),
130 }),
131 StatusCode::NOT_FOUND => Ok(CommandResult {
132 spinner,
133 symbol: "😩".to_owned(),
134 msg: "URL not found.".to_owned(),
135 }),
136 _ => Ok(CommandResult {
137 spinner,
138 symbol: "😩".to_owned(),
139 msg: "Something wrong when trying to reset email.".to_owned(),
140 }),
141 }
142 }