@smbcloud / cli / commits / 4d271b8

check email before showing password input

keypair34 committed Aug 27, 2025 at 13:27 UTC 4d271b8854011a49478e9465aaa5b035b5d4752b
4 files changed +84 -21
crates/cli/src/account/login/process.rs
+61 -21
@@ -25,8 +25,10 @@ use smbcloud_networking::{
25 },
26 smb_base_url_builder,
27 };
28 -use smbcloud_utils::email_validation;
29 -use spinners::Spinner;
28 +use {
29 + crate::account::signup::process_signup, smbcloud_networking_account::signup::check_email,
30 + smbcloud_utils::email_validation, spinners::Spinner,
31 +};
32
33 pub async fn process_login(env: Environment) -> Result<CommandResult> {
34 // Check if token file exists
@@ -284,21 +286,40 @@ async fn login_with_email(env: Environment) -> Result<CommandResult> {
286 return Err(error);
287 }
288 };
287 - let password = match Password::with_theme(&ColorfulTheme::default())
288 - .with_prompt("Password")
289 - .interact()
290 - {
291 - Ok(password) => password,
292 - Err(_) => {
293 - let error = anyhow!("Invalid password.");
294 - return Err(error);
289 + // Check if email is in the database, unconfirmed. Only presents password input if email is found and confirmed.
290 + let mut spinner = Spinner::new(
291 + spinners::Spinners::SimpleDotsScrolling,
292 + succeed_message("Checking email"),
293 + );
294 +
295 + match check_email(env, &username).await {
296 + Ok(auth) => {
297 + // Only continue with password input if email is found and confirmed.
298 + if let Some(_) = auth.error_code {
299 + verify_or_set_password(&env, spinner, auth).await
300 + } else {
301 + spinner.stop();
302 + let password = match Password::with_theme(&ColorfulTheme::default())
303 + .with_prompt("Password")
304 + .interact()
305 + {
306 + Ok(password) => password,
307 + Err(_) => {
308 + let error = anyhow!("Invalid password.");
309 + return Err(error);
310 + }
311 + };
312 + do_process_login(env, LoginArgs { username, password }).await
313 + }
314 }
296 - };
297 - do_process_login(env, LoginArgs { username, password }).await
315 + Err(_) => Err(anyhow!(fail_message(
316 + "Server error. Please try again later."
317 + ))),
318 + }
319 }
320
321 async fn do_process_login(env: Environment, args: LoginArgs) -> Result<CommandResult> {
301 - let mut spinner = Spinner::new(
322 + let spinner = Spinner::new(
323 spinners::Spinners::SimpleDotsScrolling,
324 succeed_message("Loading"),
325 );
@@ -339,14 +360,10 @@ async fn do_process_login(env: Environment, args: LoginArgs) -> Result<CommandRe
360 })
361 }
362 StatusCode::UNPROCESSABLE_ENTITY => {
342 - spinner.stop_and_persist(
343 - &succeed_symbol(),
344 - succeed_message("Please complete registration"),
345 - );
363 // Account found but email not verified / password not set
364 let result: SmbAuthorization = response.json().await?;
365 // println!("Result: {:#?}", &result);
349 - verify_or_set_password(&env, result).await
366 + verify_or_set_password(&env, spinner, result).await
367 }
368 _ => Err(anyhow!(fail_message(
369 "Login failed. Check your username and password."
@@ -356,15 +373,38 @@ async fn do_process_login(env: Environment, args: LoginArgs) -> Result<CommandRe
373
374 async fn verify_or_set_password(
375 env: &Environment,
376 + mut spinner: Spinner,
377 result: SmbAuthorization,
378 ) -> Result<CommandResult> {
379 match result.error_code {
380 Some(error_code) => {
381 debug!("{}", error_code);
382 match error_code {
365 - ErrorCode::EmailUnverified => send_email_verification(*env, result.user).await,
366 - ErrorCode::PasswordNotSet => send_reset_password(*env, result.user).await,
367 - _ => Err(anyhow!("Shouldn't be here.")),
383 + ErrorCode::EmailNotFound => {
384 + spinner.stop_and_persist(
385 + &succeed_symbol(),
386 + succeed_message("Email not found. Please register."),
387 + );
388 + process_signup(env.to_owned()).await
389 + }
390 + ErrorCode::EmailUnverified => {
391 + spinner.stop_and_persist(
392 + &succeed_symbol(),
393 + succeed_message("Email not verified. Please verify your email."),
394 + );
395 + send_email_verification(*env, result.user).await
396 + }
397 + ErrorCode::PasswordNotSet => {
398 + spinner.stop_and_persist(
399 + &succeed_symbol(),
400 + succeed_message("Password not set. Please reset your password."),
401 + );
402 + send_reset_password(*env, result.user).await
403 + }
404 + _ => {
405 + spinner.stop_and_persist(&fail_symbol(), fail_message("An error occurred."));
406 + Err(anyhow!("Idk what happened."))
407 + }
408 }
409 }
410 None => Err(anyhow!("Shouldn't be here.")),
crates/smbcloud-networking-account/src/lib.rs
+1
@@ -1,2 +1,3 @@
1 pub mod login;
2 pub mod me;
3 +pub mod signup;
crates/smbcloud-networking-account/src/signup.rs new
+21
@@ -0,0 +1,21 @@
1 +use {
2 + reqwest::Client,
3 + smbcloud_model::{account::SmbAuthorization, error_codes::ErrorResponse},
4 + smbcloud_network::{environment::Environment, network::request},
5 + smbcloud_networking::{constants::PATH_USERS_CHECK_EMAIL, smb_base_url_builder},
6 +};
7 +
8 +pub async fn check_email(env: Environment, email: &str) -> Result<SmbAuthorization, ErrorResponse> {
9 + let builder = Client::new()
10 + .get(build_url(env, email))
11 + .header("Accept", "application/json")
12 + .header("Content-Type", "application/x-www-form-urlencoded");
13 + request(builder).await
14 +}
15 +
16 +fn build_url(env: Environment, email: &str) -> String {
17 + let mut url_builder = smb_base_url_builder(env);
18 + url_builder.add_route(PATH_USERS_CHECK_EMAIL);
19 + url_builder.add_param("email", email);
20 + url_builder.build()
21 +}
crates/smbcloud-networking/src/constants.rs
+1
@@ -11,6 +11,7 @@ pub const SMB_CLIENT_SECRET: &str = "secretttttttt";
11 pub const SMB_USER_AGENT: &str = "smbcloud-cli";
12
13 // Paths
14 +pub const PATH_USERS_CHECK_EMAIL: &str = "v1/check_email";
15 pub const PATH_USERS_ME: &str = "v1/me";
16 pub const PATH_USERS: &str = "v1/users";
17 pub const PATH_USERS_PASSWORD: &str = "v1/users/password";