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