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