smbcloud_cli/account/signup/
process.rs1use super::SignupMethod;
2use crate::token::smb_token_file_path;
3use crate::{
4 account::lib::authorize_github,
5 cli::CommandResult,
6 ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
7};
8use anyhow::{anyhow, Result};
9use dialoguer::{console::Term, theme::ColorfulTheme, Input, Password, Select};
10use log::debug;
11use reqwest::{Client, StatusCode};
12use serde::Serialize;
13use smbcloud_model::signup::{SignupEmailParams, SignupResult, SignupUserEmail};
14use smbcloud_network::environment::Environment;
15use smbcloud_networking::{constants::PATH_USERS, smb_base_url_builder};
16use smbcloud_utils::email_validation;
17use spinners::Spinner;
18
19pub async fn process_signup(env: Environment) -> Result<CommandResult> {
20 if smb_token_file_path(env).is_some() {
22 return Ok(CommandResult {
23 spinner: Spinner::new(
24 spinners::Spinners::SimpleDotsScrolling,
25 succeed_message("Loading"),
26 ),
27 symbol: fail_symbol(),
28 msg: fail_message("You are already logged in. Please logout first."),
29 });
30 }
31
32 let signup_methods = vec![SignupMethod::Email, SignupMethod::GitHub];
33 let selection = Select::with_theme(&ColorfulTheme::default())
34 .items(&signup_methods)
35 .default(0)
36 .interact_on_opt(&Term::stderr())
37 .map(|i| signup_methods[i.unwrap()])
38 .unwrap();
39
40 match selection {
41 SignupMethod::Email => signup_with_email(env, None).await,
42 SignupMethod::GitHub => signup_with_github(env).await,
43 }
44}
45
46pub async fn signup_with_email(env: Environment, email: Option<String>) -> Result<CommandResult> {
47 let email = if let Some(email) = email {
48 email
49 } else {
50 Input::<String>::with_theme(&ColorfulTheme::default())
51 .with_prompt("Username")
52 .validate_with(|email: &String| email_validation(email))
53 .interact()
54 .unwrap()
55 };
56 signup_input_password_step(env, email).await
57}
58
59async fn signup_input_password_step(env: Environment, email: String) -> Result<CommandResult> {
60 let password = Password::with_theme(&ColorfulTheme::default())
61 .with_prompt("Password")
62 .validate_with(|input: &String| -> Result<(), &str> {
63 if input.len() >= 6 {
64 Ok(())
65 } else {
66 Err("Password must be at least 6 characters")
67 }
68 })
69 .with_confirmation("Confirm password", "Passwords do not match")
70 .interact()
71 .unwrap();
72
73 let spinner = Spinner::new(
74 spinners::Spinners::BouncingBall,
75 succeed_message("Signing up"),
76 );
77
78 let params = SignupEmailParams {
79 user: SignupUserEmail { email, password },
80 };
81
82 match do_signup(env, ¶ms).await {
83 Ok(_) => Ok(CommandResult {
84 spinner,
85 symbol: succeed_symbol(),
86 msg: succeed_message("You are signed up! Check your email to confirm your account."),
87 }),
88 Err(e) => Ok(CommandResult {
89 spinner,
90 symbol: fail_symbol(),
91 msg: fail_message(&format!("{e}")),
92 }),
93 }
94}
95
96async fn signup_with_github(env: Environment) -> Result<CommandResult> {
97 match authorize_github(&env).await {
98 Ok(code) => {
99 debug!("Code: {:#?}", code);
100 Ok(CommandResult {
101 spinner: Spinner::new(
102 spinners::Spinners::BouncingBall,
103 succeed_message("Requesting GitHub token"),
104 ),
105 symbol: succeed_symbol(),
106 msg: succeed_message("Finished requesting GitHub token!"),
107 })
108 }
109 Err(e) => {
110 let error = anyhow!("Failed to get code from channel: {e}");
111 Err(error)
112 }
113 }
114}
115
116pub async fn do_signup<T: Serialize + ?Sized>(env: Environment, args: &T) -> Result<CommandResult> {
117 let spinner = Spinner::new(
118 spinners::Spinners::BouncingBall,
119 succeed_message("Signing you up"),
120 );
121
122 let response = Client::new()
123 .post(build_smb_signup_url(env))
124 .json(&args)
125 .send()
126 .await?;
127
128 match response.status() {
129 StatusCode::OK => Ok(CommandResult {
130 spinner,
131 symbol: succeed_symbol(),
132 msg: succeed_message(
133 "Your account has been created. Check email for verification link.",
134 ),
135 }),
136 StatusCode::UNPROCESSABLE_ENTITY => {
137 let result: SignupResult = response.json().await?;
138 let error = anyhow!("Failed to signup: {}", result.status.message);
139 Err(error)
140 }
141 _ => {
142 let error = anyhow!("Failed to signup: {}", response.status());
143 Err(error)
144 }
145 }
146}
147
148fn build_smb_signup_url(env: Environment) -> String {
149 let mut url_builder = smb_base_url_builder(env);
150 url_builder.add_route(PATH_USERS);
151 url_builder.build()
152}