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