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
57 let password = Password::with_theme(&ColorfulTheme::default())
58 .with_prompt("Password")
59 .validate_with(|input: &String| -> Result<(), &str> {
60 if input.len() >= 6 {
61 Ok(())
62 } else {
63 Err("Password must be at least 6 characters")
64 }
65 })
66 .with_confirmation("Confirm password", "Passwords do not match")
67 .interact()
68 .unwrap();
69
70 let spinner = Spinner::new(
71 spinners::Spinners::BouncingBall,
72 succeed_message("Signing up"),
73 );
74
75 let params = SignupEmailParams {
76 user: SignupUserEmail { email, password },
77 };
78
79 match do_signup(env, ¶ms).await {
80 Ok(_) => Ok(CommandResult {
81 spinner,
82 symbol: succeed_symbol(),
83 msg: succeed_message("You are signed up! Check your email to confirm your account."),
84 }),
85 Err(e) => Ok(CommandResult {
86 spinner,
87 symbol: fail_symbol(),
88 msg: fail_message(&format!("{e}")),
89 }),
90 }
91}
92
93async fn signup_with_github(env: Environment) -> Result<CommandResult> {
94 match authorize_github(&env).await {
95 Ok(code) => {
96 debug!("Code: {:#?}", code);
97 Ok(CommandResult {
98 spinner: Spinner::new(
99 spinners::Spinners::BouncingBall,
100 succeed_message("Requesting GitHub token"),
101 ),
102 symbol: succeed_symbol(),
103 msg: succeed_message("Finished requesting GitHub token!"),
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 succeed_message("Signing you up"),
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: succeed_symbol(),
129 msg: succeed_message(
130 "Your account has been created. Check email for verification link.",
131 ),
132 }),
133 StatusCode::UNPROCESSABLE_ENTITY => {
134 let result: SignupResult = response.json().await?;
135 let error = anyhow!("Failed to signup: {}", result.status.message);
136 Err(error)
137 }
138 _ => {
139 let error = anyhow!("Failed to signup: {}", response.status());
140 Err(error)
141 }
142 }
143}
144
145fn build_smb_signup_url(env: Environment) -> String {
146 let mut url_builder = smb_base_url_builder(env);
147 url_builder.add_route(PATH_USERS);
148 url_builder.build()
149}