@smbcloud / cli / commits / 3ba70e0

environment

Seto Elkahfi committed May 1, 2025 at 00:35 UTC 3ba70e0e8314e7ca6afd5a8666788284ed38d9fe
15 files changed +194 -159
Cargo.lock
+1
@@ -1683,6 +1683,7 @@ version = "0.3.8"
1683 dependencies = [
1684 "anyhow",
1685 "async-trait",
1686 + "clap",
1687 "dirs 5.0.1",
1688 "home",
1689 "log",
smbcloud-cli/src/account/forgot/mod.rs
+10 -8
@@ -5,11 +5,13 @@ use dialoguer::{theme::ColorfulTheme, Input, Password};
5 use reqwest::{Client, StatusCode};
6 use serde::{Deserialize, Serialize};
7 use smbcloud_model::forgot::{Args, Email, Param, UserUpdatePassword};
8 -use smbcloud_networking::{constants::PATH_USERS_PASSWORD, smb_base_url_builder};
8 +use smbcloud_networking::{
9 + constants::PATH_USERS_PASSWORD, environment::Environment, smb_base_url_builder,
10 +};
11 use smbcloud_utils::email_validation;
12 use spinners::Spinner;
13
12 -pub async fn process_forgot() -> Result<CommandResult> {
14 +pub async fn process_forgot(env: Environment) -> Result<CommandResult> {
15 println!("Provide your login credentials.");
16 let email = Input::<String>::with_theme(&ColorfulTheme::default())
17 .with_prompt("Email")
@@ -26,7 +28,7 @@ pub async fn process_forgot() -> Result<CommandResult> {
28 };
29
30 let response = Client::new()
29 - .post(build_smb_forgot_url())
31 + .post(build_smb_forgot_url(env))
32 .json(&params)
33 .send()
34 .await?;
@@ -37,7 +39,7 @@ pub async fn process_forgot() -> Result<CommandResult> {
39 "✅",
40 "Check your email and input your code here.".to_owned(),
41 );
40 - input_code().await
42 + input_code(env.clone()).await
43 }
44 _ => Ok(CommandResult {
45 spinner,
@@ -47,7 +49,7 @@ pub async fn process_forgot() -> Result<CommandResult> {
49 }
50 }
51
50 -async fn input_code() -> Result<CommandResult> {
52 +async fn input_code(env: Environment) -> Result<CommandResult> {
53 let security_code = Input::<String>::with_theme(&ColorfulTheme::default())
54 .with_prompt("Code")
55 .interact()
@@ -91,7 +93,7 @@ async fn input_code() -> Result<CommandResult> {
93 );
94
95 let response = Client::new()
94 - .put(build_smb_forgot_url())
96 + .put(build_smb_forgot_url(env))
97 .json(&params)
98 .send()
99 .await?;
@@ -121,8 +123,8 @@ async fn input_code() -> Result<CommandResult> {
123 }
124 }
125
124 -fn build_smb_forgot_url() -> String {
125 - let mut url_builder = smb_base_url_builder();
126 +fn build_smb_forgot_url(env: Environment) -> String {
127 + let mut url_builder = smb_base_url_builder(env);
128 url_builder.add_route(PATH_USERS_PASSWORD);
129 url_builder.build()
130 }
smbcloud-cli/src/account/lib.rs
+7 -6
@@ -8,6 +8,7 @@ use smbcloud_networking::{
8 constants::{
9 GH_OAUTH_CLIENT_ID, GH_OAUTH_REDIRECT_HOST, GH_OAUTH_REDIRECT_PORT, PATH_AUTHORIZE,
10 },
11 + environment::Environment,
12 smb_base_url_builder,
13 };
14 use spinners::Spinner;
@@ -19,7 +20,7 @@ use std::{
20 };
21 use url_builder::URLBuilder;
22
22 -pub async fn authorize_github() -> Result<SmbAuthorization> {
23 +pub async fn authorize_github(env: &Environment) -> Result<SmbAuthorization> {
24 // Spin up a simple localhost server to listen for the GitHub OAuth callback
25 // setup_oauth_callback_server();
26 // Open the GitHub OAuth URL in the user's browser
@@ -57,7 +58,7 @@ pub async fn authorize_github() -> Result<SmbAuthorization> {
58 Ok(code) => {
59 debug!("Got code from channel: {:#?}", &code);
60 //Err(anyhow!("Failed to get code from channel."))
60 - process_connect_github(code).await
61 + process_connect_github(env.clone(), code).await
62 }
63 Err(e) => {
64 let error = anyhow!("Failed to get code from channel: {e}");
@@ -140,9 +141,9 @@ fn handle_connection(mut stream: TcpStream, tx: Sender<String>) {
141 }
142
143 // Get access token
143 -pub async fn process_connect_github(code: String) -> Result<SmbAuthorization> {
144 +pub async fn process_connect_github(env: Environment, code: String) -> Result<SmbAuthorization> {
145 let response = Client::new()
145 - .post(build_authorize_smb_url())
146 + .post(build_authorize_smb_url(env))
147 .body(format!("gh_code={}", code))
148 .header("Accept", "application/json")
149 .header("Content-Type", "application/x-www-form-urlencoded")
@@ -187,8 +188,8 @@ pub async fn process_connect_github(code: String) -> Result<SmbAuthorization> {
188 }
189 }
190
190 -fn build_authorize_smb_url() -> String {
191 - let mut url_builder = smb_base_url_builder();
191 +fn build_authorize_smb_url(env: Environment) -> String {
192 + let mut url_builder = smb_base_url_builder(env);
193 url_builder.add_route(PATH_AUTHORIZE);
194 url_builder.build()
195 }
smbcloud-cli/src/account/login/process.rs
+53 -48
@@ -21,13 +21,14 @@ use smbcloud_networking::{
21 PATH_LINK_GITHUB_ACCOUNT, PATH_RESEND_CONFIRMATION, PATH_RESET_PASSWORD_INSTRUCTIONS,
22 PATH_USERS_PASSWORD, PATH_USERS_SIGN_IN, PATH_USERS_SIGN_OUT,
23 },
24 + environment::Environment,
25 get_smb_token, smb_base_url_builder, smb_token_file_path,
26 };
27 use smbcloud_utils::email_validation;
28 use spinners::Spinner;
29 use std::fs::{self};
30
30 -pub async fn process_login() -> Result<CommandResult> {
31 +pub async fn process_login(env: Environment) -> Result<CommandResult> {
32 // Check if token file exists
33 if smb_token_file_path().is_some() {
34 return Ok(CommandResult {
@@ -55,12 +56,12 @@ pub async fn process_login() -> Result<CommandResult> {
56 };
57
58 match selection {
58 - SignupMethod::Email => login_with_email().await,
59 - SignupMethod::GitHub => login_with_github().await,
59 + SignupMethod::Email => login_with_email(env).await,
60 + SignupMethod::GitHub => login_with_github(env).await,
61 }
62 }
63
63 -pub async fn process_logout() -> Result<CommandResult> {
64 +pub async fn process_logout(env: Environment) -> Result<CommandResult> {
65 // Logout if user confirms
66 if let Some(token_path) = smb_token_file_path() {
67 let confirm = match Confirm::with_theme(&ColorfulTheme::default())
@@ -90,7 +91,7 @@ pub async fn process_logout() -> Result<CommandResult> {
91 );
92
93 // Call backend
93 - match do_process_logout().await {
94 + match do_process_logout(env).await {
95 Ok(_) => {
96 spinner.stop_and_persist("✅", "Done.".to_owned());
97 fs::remove_file(token_path)?;
@@ -119,9 +120,9 @@ pub async fn process_logout() -> Result<CommandResult> {
120
121 // Private functions
122
122 -async fn login_with_github() -> Result<CommandResult> {
123 - match authorize_github().await {
124 - Ok(result) => process_authorization(result).await,
123 +async fn login_with_github(env: Environment) -> Result<CommandResult> {
124 + match authorize_github(&env).await {
125 + Ok(result) => process_authorization(env, result).await,
126 Err(err) => {
127 let error = anyhow!("Failed to authorize your GitHub account. {}", err);
128 Err(error)
@@ -129,22 +130,22 @@ async fn login_with_github() -> Result<CommandResult> {
130 }
131 }
132
132 -async fn process_authorization(auth: SmbAuthorization) -> Result<CommandResult> {
133 +async fn process_authorization(env: Environment, auth: SmbAuthorization) -> Result<CommandResult> {
134 // What to do if not logged in with GitHub?
135 // Check error_code first
136 if let Some(error_code) = auth.error_code {
137 debug!("{}", error_code);
138 match error_code {
139 ErrorCode::EmailNotFound => {
139 - return create_new_account(auth.user_email, auth.user_info).await
140 + return create_new_account(env, auth.user_email, auth.user_info).await
141 }
141 - ErrorCode::EmailUnverified => return send_email_verification(auth.user).await,
142 + ErrorCode::EmailUnverified => return send_email_verification(env, auth.user).await,
143 ErrorCode::PasswordNotSet => {
144 // Only for email and password login
145 let error = anyhow!("Password not set.");
146 return Err(error);
147 }
147 - ErrorCode::GithubNotLinked => return connect_github_account(auth).await,
148 + ErrorCode::GithubNotLinked => return connect_github_account(env, auth).await,
149 }
150 }
151
@@ -168,6 +169,7 @@ async fn process_authorization(auth: SmbAuthorization) -> Result<CommandResult>
169 }
170
171 async fn create_new_account(
172 + env: Environment,
173 user_email: Option<GithubEmail>,
174 user_info: Option<GithubInfo>,
175 ) -> Result<CommandResult> {
@@ -206,13 +208,13 @@ async fn create_new_account(
208 },
209 };
210
209 - return do_signup(&params).await;
211 + return do_signup(env, &params).await;
212 }
213
214 Err(anyhow!("Shouldn't be here."))
215 }
216
215 -async fn send_email_verification(user: Option<User>) -> Result<CommandResult> {
217 +async fn send_email_verification(env: Environment, user: Option<User>) -> Result<CommandResult> {
218 // Return early if user is null
219 if let Some(user) = user {
220 let confirm = match Confirm::with_theme(&ColorfulTheme::default())
@@ -238,14 +240,14 @@ async fn send_email_verification(user: Option<User>) -> Result<CommandResult> {
240 msg: "Doing nothing.".to_owned(),
241 });
242 }
241 - resend_email_verification(user).await
243 + resend_email_verification(env, user).await
244 } else {
245 let error = anyhow!("Failed to get user.");
246 Err(error)
247 }
248 }
249
248 -async fn resend_email_verification(user: User) -> Result<CommandResult> {
250 +async fn resend_email_verification(env: Environment, user: User) -> Result<CommandResult> {
251 let spinner = Spinner::new(
252 spinners::Spinners::SimpleDotsScrolling,
253 style("Sending verification email...")
@@ -255,7 +257,7 @@ async fn resend_email_verification(user: User) -> Result<CommandResult> {
257 );
258
259 let response = Client::new()
258 - .post(build_smb_resend_email_verification_url())
260 + .post(build_smb_resend_email_verification_url(env))
261 .body(format!("id={}", user.id))
262 .header("Accept", "application/json")
263 .header("Content-Type", "application/x-www-form-urlencoded")
@@ -275,7 +277,7 @@ async fn resend_email_verification(user: User) -> Result<CommandResult> {
277 }
278 }
279
278 -async fn connect_github_account(auth: SmbAuthorization) -> Result<CommandResult> {
280 +async fn connect_github_account(env: Environment, auth: SmbAuthorization) -> Result<CommandResult> {
281 let confirm = match Confirm::with_theme(&ColorfulTheme::default())
282 .with_prompt("Do you want to link your GitHub account?")
283 .interact()
@@ -309,7 +311,7 @@ async fn connect_github_account(auth: SmbAuthorization) -> Result<CommandResult>
311 );
312
313 let response = Client::new()
312 - .post(build_smb_connect_github_url())
314 + .post(build_smb_connect_github_url(env))
315 .json(&auth)
316 .header("Accept", "application/json")
317 .header("Content-Type", "application/x-www-form-urlencoded")
@@ -329,7 +331,7 @@ async fn connect_github_account(auth: SmbAuthorization) -> Result<CommandResult>
331 }
332 }
333
332 -async fn login_with_email() -> Result<CommandResult> {
334 +async fn login_with_email(env: Environment) -> Result<CommandResult> {
335 println!("Provide your login credentials.");
336 let username = match Input::<String>::with_theme(&ColorfulTheme::default())
337 .with_prompt("Email")
@@ -352,10 +354,10 @@ async fn login_with_email() -> Result<CommandResult> {
354 return Err(error);
355 }
356 };
355 - do_process_login(LoginArgs { username, password }).await
357 + do_process_login(env, LoginArgs { username, password }).await
358 }
359
358 -async fn do_process_login(args: LoginArgs) -> Result<CommandResult> {
360 +async fn do_process_login(env: Environment, args: LoginArgs) -> Result<CommandResult> {
361 let login_params = LoginParams {
362 user: UserParam {
363 email: args.username,
@@ -364,7 +366,7 @@ async fn do_process_login(args: LoginArgs) -> Result<CommandResult> {
366 };
367
368 let response = Client::new()
367 - .post(build_smb_login_url())
369 + .post(build_smb_login_url(env))
370 .json(&login_params)
371 .send()
372 .await?;
@@ -397,19 +399,22 @@ async fn do_process_login(args: LoginArgs) -> Result<CommandResult> {
399 // Account found but email not verified / password not set
400 let result: SmbAuthorization = response.json().await?;
401 // println!("Result: {:#?}", &result);
400 - verify_or_set_password(result).await
402 + verify_or_set_password(&env, result).await
403 }
404 _ => Err(anyhow!("Login failed. Check your username and password.")),
405 }
406 }
407
406 -async fn verify_or_set_password(result: SmbAuthorization) -> Result<CommandResult> {
408 +async fn verify_or_set_password(
409 + env: &Environment,
410 + result: SmbAuthorization,
411 +) -> Result<CommandResult> {
412 match result.error_code {
413 Some(error_code) => {
414 debug!("{}", error_code);
415 match error_code {
411 - ErrorCode::EmailUnverified => send_email_verification(result.user).await,
412 - ErrorCode::PasswordNotSet => send_reset_password(result.user).await,
416 + ErrorCode::EmailUnverified => send_email_verification(*env, result.user).await,
417 + ErrorCode::PasswordNotSet => send_reset_password(*env, result.user).await,
418 _ => Err(anyhow!("Shouldn't be here.")),
419 }
420 }
@@ -417,7 +422,7 @@ async fn verify_or_set_password(result: SmbAuthorization) -> Result<CommandResul
422 }
423 }
424
420 -async fn send_reset_password(user: Option<User>) -> Result<CommandResult> {
425 +async fn send_reset_password(env: Environment, user: Option<User>) -> Result<CommandResult> {
426 // Return early if user is null
427 if let Some(user) = user {
428 let confirm = match Confirm::with_theme(&ColorfulTheme::default())
@@ -443,14 +448,14 @@ async fn send_reset_password(user: Option<User>) -> Result<CommandResult> {
448 msg: "Doing nothing.".to_owned(),
449 });
450 }
446 - resend_reset_password_instruction(user).await
451 + resend_reset_password_instruction(env, user).await
452 } else {
453 let error = anyhow!("Failed to get user.");
454 Err(error)
455 }
456 }
457
453 -async fn resend_reset_password_instruction(user: User) -> Result<CommandResult> {
458 +async fn resend_reset_password_instruction(env: Environment, user: User) -> Result<CommandResult> {
459 let mut spinner = Spinner::new(
460 spinners::Spinners::SimpleDotsScrolling,
461 style("Sending reset password instruction...")
@@ -459,7 +464,7 @@ async fn resend_reset_password_instruction(user: User) -> Result<CommandResult>
464 .to_string(),
465 );
466 let response = Client::new()
462 - .post(build_smb_resend_reset_password_instructions_url())
467 + .post(build_smb_resend_reset_password_instructions_url(env))
468 .body(format!("id={}", user.id))
469 .header("Accept", "application/json")
470 .header("Content-Type", "application/x-www-form-urlencoded")
@@ -472,7 +477,7 @@ async fn resend_reset_password_instruction(user: User) -> Result<CommandResult>
477 "✅",
478 "Reset password instruction sent! Please check your email.".to_owned(),
479 );
475 - input_reset_password_token().await
480 + input_reset_password_token(env).await
481 }
482 _ => {
483 let error = anyhow!("Failed to send reset password instruction.");
@@ -481,7 +486,7 @@ async fn resend_reset_password_instruction(user: User) -> Result<CommandResult>
486 }
487 }
488
484 -async fn input_reset_password_token() -> Result<CommandResult> {
489 +async fn input_reset_password_token(env: Environment) -> Result<CommandResult> {
490 let token = match Input::<String>::with_theme(&ColorfulTheme::default())
491 .with_prompt("Input reset password token")
492 .interact()
@@ -520,7 +525,7 @@ async fn input_reset_password_token() -> Result<CommandResult> {
525 };
526
527 let response = Client::new()
523 - .put(build_smb_reset_password_url())
528 + .put(build_smb_reset_password_url(env))
529 .json(&params)
530 .header("Accept", "application/json")
531 .header("Content-Type", "application/x-www-form-urlencoded")
@@ -540,11 +545,11 @@ async fn input_reset_password_token() -> Result<CommandResult> {
545 }
546 }
547
543 -async fn do_process_logout() -> Result<()> {
548 +async fn do_process_logout(env: Environment) -> Result<()> {
549 let token = get_smb_token().await?;
550
551 let response = Client::new()
547 - .delete(build_smb_logout_url())
552 + .delete(build_smb_logout_url(env))
553 .header("Authorization", token)
554 .header("Accept", "application/json")
555 .header("Content-Type", "application/x-www-form-urlencoded")
@@ -557,38 +562,38 @@ async fn do_process_logout() -> Result<()> {
562 }
563 }
564
560 -fn build_smb_login_url() -> String {
561 - let mut url_builder = smb_base_url_builder();
565 +fn build_smb_login_url(env: Environment) -> String {
566 + let mut url_builder = smb_base_url_builder(env);
567 url_builder.add_route(PATH_USERS_SIGN_IN);
568 url_builder.build()
569 }
570
566 -fn build_smb_logout_url() -> String {
567 - let mut url_builder = smb_base_url_builder();
571 +fn build_smb_logout_url(env: Environment) -> String {
572 + let mut url_builder = smb_base_url_builder(env);
573 url_builder.add_route(PATH_USERS_SIGN_OUT);
574 url_builder.build()
575 }
576
572 -fn build_smb_resend_email_verification_url() -> String {
573 - let mut url_builder = smb_base_url_builder();
577 +fn build_smb_resend_email_verification_url(env: Environment) -> String {
578 + let mut url_builder = smb_base_url_builder(env);
579 url_builder.add_route(PATH_RESEND_CONFIRMATION);
580 url_builder.build()
581 }
582
578 -fn build_smb_resend_reset_password_instructions_url() -> String {
579 - let mut url_builder = smb_base_url_builder();
583 +fn build_smb_resend_reset_password_instructions_url(env: Environment) -> String {
584 + let mut url_builder = smb_base_url_builder(env);
585 url_builder.add_route(PATH_RESET_PASSWORD_INSTRUCTIONS);
586 url_builder.build()
587 }
588
584 -fn build_smb_reset_password_url() -> String {
585 - let mut url_builder = smb_base_url_builder();
589 +fn build_smb_reset_password_url(env: Environment) -> String {
590 + let mut url_builder = smb_base_url_builder(env);
591 url_builder.add_route(PATH_USERS_PASSWORD);
592 url_builder.build()
593 }
594
590 -fn build_smb_connect_github_url() -> String {
591 - let mut url_builder = smb_base_url_builder();
595 +fn build_smb_connect_github_url(env: Environment) -> String {
596 + let mut url_builder = smb_base_url_builder(env);
597 url_builder.add_route(PATH_LINK_GITHUB_ACCOUNT);
598 url_builder.build()
599 }
smbcloud-cli/src/account/mod.rs
+6 -5
@@ -12,12 +12,13 @@ use self::{
12 };
13 use crate::cli::CommandResult;
14 use anyhow::Result;
15 +use smbcloud_networking::environment::Environment;
16
16 -pub async fn process_account(commands: Commands) -> Result<CommandResult> {
17 +pub async fn process_account(env: Environment, commands: Commands) -> Result<CommandResult> {
18 match commands {
18 - Commands::Signup {} => process_signup().await,
19 - Commands::Login {} => process_login().await,
20 - Commands::Logout {} => process_logout().await,
21 - Commands::Forgot {} => process_forgot().await,
19 + Commands::Signup {} => process_signup(env).await,
20 + Commands::Login {} => process_login(env).await,
21 + Commands::Logout {} => process_logout(env).await,
22 + Commands::Forgot {} => process_forgot(env).await,
23 }
24 }
smbcloud-cli/src/account/signup/process.rs
+14 -12
@@ -7,11 +7,13 @@ use log::debug;
7 use reqwest::{Client, StatusCode};
8 use serde::Serialize;
9 use smbcloud_model::signup::{SignupEmailParams, SignupResult, SignupUserEmail};
10 -use smbcloud_networking::{constants::PATH_USERS, smb_base_url_builder, smb_token_file_path};
10 +use smbcloud_networking::{
11 + constants::PATH_USERS, environment::Environment, smb_base_url_builder, smb_token_file_path,
12 +};
13 use smbcloud_utils::email_validation;
14 use spinners::Spinner;
15
14 -pub async fn process_signup() -> Result<CommandResult> {
16 +pub async fn process_signup(env: Environment) -> Result<CommandResult> {
17 // Check if token file exists
18 if smb_token_file_path().is_some() {
19 return Ok(CommandResult {
@@ -33,12 +35,12 @@ pub async fn process_signup() -> Result<CommandResult> {
35 .unwrap();
36
37 match selection {
36 - SignupMethod::Email => signup_with_email(None).await,
37 - SignupMethod::GitHub => signup_with_github().await,
38 + SignupMethod::Email => signup_with_email(env, None).await,
39 + SignupMethod::GitHub => signup_with_github(env).await,
40 }
41 }
42
41 -pub async fn signup_with_email(email: Option<String>) -> Result<CommandResult> {
43 +pub async fn signup_with_email(env: Environment, email: Option<String>) -> Result<CommandResult> {
44 let email = if let Some(email) = email {
45 email
46 } else {
@@ -71,7 +73,7 @@ pub async fn signup_with_email(email: Option<String>) -> Result<CommandResult> {
73 user: SignupUserEmail { email, password },
74 };
75
74 - match do_signup(&params).await {
76 + match do_signup(env, &params).await {
77 Ok(_) => Ok(CommandResult {
78 spinner,
79 symbol: style("✅".to_string()).for_stderr().green().to_string(),
@@ -85,8 +87,8 @@ pub async fn signup_with_email(email: Option<String>) -> Result<CommandResult> {
87 }
88 }
89
88 -async fn signup_with_github() -> Result<CommandResult> {
89 - match authorize_github().await {
90 +async fn signup_with_github(env: Environment) -> Result<CommandResult> {
91 + match authorize_github(&env).await {
92 Ok(code) => {
93 debug!("Code: {:#?}", code);
94 Ok(CommandResult {
@@ -108,14 +110,14 @@ async fn signup_with_github() -> Result<CommandResult> {
110 }
111 }
112
111 -pub async fn do_signup<T: Serialize + ?Sized>(args: &T) -> Result<CommandResult> {
113 +pub 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()
118 - .post(build_smb_signup_url())
120 + .post(build_smb_signup_url(env))
121 .json(&args)
122 .send()
123 .await?;
@@ -138,8 +140,8 @@ pub async fn do_signup<T: Serialize + ?Sized>(args: &T) -> Result<CommandResult>
140 }
141 }
142
141 -fn build_smb_signup_url() -> String {
142 - let mut url_builder = smb_base_url_builder();
143 +fn 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 }
smbcloud-cli/src/cli/mod.rs
+1 -36
@@ -1,5 +1,6 @@
1 use crate::{account, project};
2 use clap::{Parser, Subcommand};
3 +use smbcloud_networking::environment::Environment;
4 use spinners::Spinner;
5
6 pub struct CommandResult {
@@ -8,42 +9,6 @@ pub struct CommandResult {
9 pub msg: String,
10 }
11
11 -#[derive(clap::ValueEnum, Clone)]
12 -pub enum Environment {
13 - Dev,
14 - Production,
15 -}
16 -
17 -impl std::fmt::Display for Environment {
18 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 - write!(f, "{}", self.to_str())
20 - }
21 -}
22 -
23 -impl Environment {
24 - pub fn from_str(env: &str) -> Self {
25 - match env.to_lowercase().as_str() {
26 - "dev" => Environment::Dev,
27 - "production" => Environment::Production,
28 - _ => panic!("Invalid environment: {}", env),
29 - }
30 - }
31 -
32 - pub fn to_str(&self) -> &str {
33 - match self {
34 - Environment::Dev => "dev",
35 - Environment::Production => "production",
36 - }
37 - }
38 -
39 - pub fn smb_dir(&self) -> String {
40 - match self {
41 - Environment::Dev => ".smb-dev".to_string(),
42 - Environment::Production => ".smb".to_string(),
43 - }
44 - }
45 -}
46 -
12 #[derive(Parser)]
13 #[clap(author, version, about)]
14 pub struct Cli {
smbcloud-cli/src/main.rs
+17 -6
@@ -1,7 +1,7 @@
1 use anyhow::{anyhow, Result};
2 use clap::Parser;
3 use console::style;
4 -use smbcloud_cli::cli::{CommandResult, Environment};
4 +use smbcloud_cli::cli::CommandResult;
5 use smbcloud_cli::project::init::process_project_init;
6 use smbcloud_cli::{
7 account::process_account,
@@ -9,6 +9,7 @@ use smbcloud_cli::{
9 deploy::process_deploy,
10 project::process_project,
11 };
12 +use smbcloud_networking::environment::Environment;
13 use std::{
14 fs::{create_dir_all, OpenOptions},
15 path::PathBuf,
@@ -18,15 +19,22 @@ use tracing::subscriber::set_global_default;
19 use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer};
20 use tracing_subscriber::{filter::LevelFilter, prelude::*, EnvFilter};
21
21 -fn setup_logging( env: Environment, level: Option<EnvFilter>) -> Result<()> {
22 +fn setup_logging(env: Environment, level: Option<EnvFilter>) -> Result<()> {
23 // Log in the current directory
24 let log_path = match home::home_dir() {
25 Some(path) => {
26 create_dir_all(path.join(env.smb_dir()))?;
26 - let log_path = [path.to_str().unwrap(), "/", &env.smb_dir(), "/smbcloud-cli.log"].join("");
27 + let log_path = [
28 + path.to_str().unwrap(),
29 + "/",
30 + &env.smb_dir(),
31 + "/smbcloud-cli.log",
32 + ]
33 + .join("");
34 // Create the file if it doesn't exist
35 let _file = OpenOptions::new()
36 .create(true)
37 + .truncate(true)
38 .write(true)
39 .open(&log_path)?;
40
@@ -39,6 +47,7 @@ fn setup_logging( env: Environment, level: Option<EnvFilter>) -> Result<()> {
47
48 let file = OpenOptions::new()
49 .create(true)
50 + .truncate(true)
51 .write(true)
52 .open(log_path)
53 .unwrap();
@@ -101,9 +110,11 @@ async fn run() -> Result<CommandResult> {
110 }
111
112 match cli.command {
104 - Commands::Account { command } => process_account(command).await,
105 - Commands::Project { command } => process_project(command).await,
106 - Commands::Init { name, description } => process_project_init(name, description).await,
113 + Commands::Account { command } => process_account(cli.environment, command).await,
114 + Commands::Project { command } => process_project(cli.environment, command).await,
115 + Commands::Init { name, description } => {
116 + process_project_init(cli.environment, name, description).await
117 + }
118 Commands::Deploy {} => process_deploy().await,
119 }
120 }
smbcloud-cli/src/project/init.rs
+4 -2
@@ -2,10 +2,12 @@ use crate::cli::CommandResult;
2 use anyhow::Result;
3 use console::style;
4 use smbcloud_model::project::ProjectCreate;
5 +use smbcloud_networking::environment::Environment;
6 use smbcloud_networking_project::create_project;
7 use spinners::Spinner;
8
9 pub async fn process_project_init(
10 + env: Environment,
11 name: Option<String>,
12 description: Option<String>,
13 ) -> Result<CommandResult> {
@@ -16,7 +18,7 @@ pub async fn process_project_init(
18 let name = name.unwrap_or_else(|| "New Project".to_string());
19 let description = description.unwrap_or_else(|| "No description".to_string());
20 let project = ProjectCreate { name, description };
19 - match create_project(project).await {
21 + match create_project(env, project).await {
22 Ok(p) => {
23 spinner.stop_and_persist("✅", "Done.".to_owned());
24 Ok(CommandResult {
@@ -30,7 +32,7 @@ pub async fn process_project_init(
32 Ok(CommandResult {
33 spinner,
34 symbol: "😩".to_owned(),
33 - msg: format!("Failed to initiate a project."),
35 + msg: "Failed to initiate a project.".to_string(),
36 })
37 }
38 }
smbcloud-cli/src/project/mod.rs
+13 -9
@@ -11,11 +11,12 @@ use smbcloud_model::{
11 self,
12 project::{Config, Project, ProjectCreate},
13 };
14 +use smbcloud_networking::environment::Environment;
15 use smbcloud_networking_project::{create_project, delete_project, get_all, get_project};
16 use spinners::Spinner;
17 use std::{fs::OpenOptions, io::Write};
18
18 -pub async fn process_project(commands: Commands) -> Result<CommandResult> {
19 +pub async fn process_project(env: Environment, commands: Commands) -> Result<CommandResult> {
20 match commands {
21 Commands::New {} => {
22 let project_name = Input::<String>::with_theme(&ColorfulTheme::default())
@@ -32,10 +33,13 @@ pub async fn process_project(commands: Commands) -> Result<CommandResult> {
33 style("Creating a project...").green().bold().to_string(),
34 );
35
35 - match create_project(ProjectCreate {
36 - name: project_name.clone(),
37 - description: description.clone(),
38 - })
36 + match create_project(
37 + env,
38 + ProjectCreate {
39 + name: project_name.clone(),
40 + description: description.clone(),
41 + },
42 + )
43 .await
44 {
45 Ok(_) => {
@@ -66,7 +70,7 @@ pub async fn process_project(commands: Commands) -> Result<CommandResult> {
70 );
71
72 // Get all
69 - match get_all().await {
73 + match get_all(env).await {
74 Ok(projects) => {
75 spinner.stop_and_persist("✅", "Loaded.".to_owned());
76 let msg = if projects.is_empty() {
@@ -100,7 +104,7 @@ pub async fn process_project(commands: Commands) -> Result<CommandResult> {
104 style("Loading...").green().bold().to_string(),
105 );
106 // Get Detail
103 - match get_project(id).await {
107 + match get_project(env, id).await {
108 Ok(project) => {
109 spinner.stop_and_persist("✅", "Loaded.".to_owned());
110 let message = format!("Showing project {}.", &project.name);
@@ -138,7 +142,7 @@ pub async fn process_project(commands: Commands) -> Result<CommandResult> {
142 msg: "Cancelled.".to_string(),
143 });
144 }
141 - match delete_project(id).await {
145 + match delete_project(env, id).await {
146 Ok(_) => {
147 spinner.stop_and_persist("✅", "Done.".to_string());
148 Ok(CommandResult {
@@ -157,7 +161,7 @@ pub async fn process_project(commands: Commands) -> Result<CommandResult> {
161 }
162 }
163 Commands::Use { id } => {
160 - let project = get_project(id).await?;
164 + let project = get_project(env, id).await?;
165
166 let config = Config {
167 current_project: Some(project),
smbcloud-networking-project/src/lib.rs
+13 -13
@@ -5,16 +5,16 @@ use smbcloud_model::{
5 self,
6 project::{Project, ProjectCreate},
7 };
8 -use smbcloud_networking::{get_smb_token, smb_base_url_builder};
8 +use smbcloud_networking::{environment::Environment, get_smb_token, smb_base_url_builder};
9
10 -pub async fn get_all() -> Result<Vec<Project>> {
10 +pub async fn get_all(env: Environment) -> Result<Vec<Project>> {
11 // Get current token
12 let token = get_smb_token().await?;
13
14 debug!("Current token: {}", token);
15
16 let response = Client::new()
17 - .get(build_project_url())
17 + .get(build_project_url(env))
18 .header("Authorization", token)
19 .header("User-agent", "smbcloud-cli")
20 .send()
@@ -29,12 +29,12 @@ pub async fn get_all() -> Result<Vec<Project>> {
29 }
30 }
31
32 -pub async fn create_project(project: ProjectCreate) -> Result<Project> {
32 +pub async fn create_project(env: Environment, project: ProjectCreate) -> Result<Project> {
33 // Get current token
34 let token = get_smb_token().await?;
35
36 let response = Client::new()
37 - .post(build_project_url())
37 + .post(build_project_url(env))
38 .json(&project)
39 .header("Authorization", token)
40 .send()
@@ -50,12 +50,12 @@ pub async fn create_project(project: ProjectCreate) -> Result<Project> {
50 }
51 }
52
53 -pub async fn get_project(id: String) -> Result<Project> {
53 +pub async fn get_project(env: Environment, id: String) -> Result<Project> {
54 // Get current token
55 let token = get_smb_token().await?;
56
57 let response = Client::new()
58 - .get(build_project_url_with_id(id))
58 + .get(build_project_url_with_id(env, id))
59 .header("Authorization", token)
60 .send()
61 .await?;
@@ -70,12 +70,12 @@ pub async fn get_project(id: String) -> Result<Project> {
70 }
71 }
72
73 -pub async fn delete_project(id: String) -> Result<()> {
73 +pub async fn delete_project(env: Environment, id: String) -> Result<()> {
74 // Get current token
75 let token = get_smb_token().await?;
76
77 let response = Client::new()
78 - .delete(build_project_url_with_id(id))
78 + .delete(build_project_url_with_id(env, id))
79 .header("Authorization", token)
80 .send()
81 .await?;
@@ -91,14 +91,14 @@ pub async fn delete_project(id: String) -> Result<()> {
91
92 // Private functions
93
94 -fn build_project_url() -> String {
95 - let mut url_builder = smb_base_url_builder();
94 +fn build_project_url(env: Environment) -> String {
95 + let mut url_builder = smb_base_url_builder(env);
96 url_builder.add_route("v1/projects");
97 url_builder.build()
98 }
99
100 -fn build_project_url_with_id(id: String) -> String {
101 - let mut url_builder = smb_base_url_builder();
100 +fn build_project_url_with_id(env: Environment, id: String) -> String {
101 + let mut url_builder = smb_base_url_builder(env);
102 url_builder.add_route("v1/projects");
103 url_builder.add_route(id.as_str());
104 url_builder.build()
smbcloud-networking/Cargo.toml
+1
@@ -14,6 +14,7 @@ categories = ["command-line-utilities", "command-line-interface", "cryptography:
14 [dependencies]
15 anyhow = { workspace = true }
16 async-trait = { workspace = true, optional = true }
17 +clap = { workspace = true, features = ["derive", "env"] }
18 dirs = { workspace = true }
19 home = { workspace = true }
20 log = { workspace = true }
smbcloud-networking/src/constants.rs
-10
@@ -11,16 +11,6 @@ pub const GH_OAUTH_REDIRECT_PORT: &str = "8808";
11 pub const SMB_CLIENT_ID: &str = "cli";
12 pub const SMB_CLIENT_SECRET: &str = "secretttttttt";
13
14 -#[cfg(debug_assertions)]
15 -pub const SMB_API_PROTOCOL: &str = "http";
16 -#[cfg(not(debug_assertions))]
17 -pub const SMB_API_PROTOCOL: &str = "https";
18 -
19 -#[cfg(debug_assertions)]
20 -pub const SMB_API_HOST: &str = "localhost:8088";
21 -#[cfg(not(debug_assertions))]
22 -pub const SMB_API_HOST: &str = "api.smbcloud.xyz";
23 -
14 // Paths
15 pub const PATH_USERS: &str = "v1/users";
16 pub const PATH_USERS_PASSWORD: &str = "v1/users/password";
smbcloud-networking/src/environment.rs new
+48
@@ -0,0 +1,48 @@
1 +#[derive(clap::ValueEnum, Clone, Copy)]
2 +pub enum Environment {
3 + Dev,
4 + Production,
5 +}
6 +
7 +impl std::fmt::Display for Environment {
8 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9 + write!(f, "{}", self.to_str())
10 + }
11 +}
12 +
13 +impl Environment {
14 + pub fn from_str(env: &str) -> Self {
15 + match env.to_lowercase().as_str() {
16 + "dev" => Environment::Dev,
17 + "production" => Environment::Production,
18 + _ => panic!("Invalid environment: {}", env),
19 + }
20 + }
21 +
22 + pub fn to_str(&self) -> &str {
23 + match self {
24 + Environment::Dev => "dev",
25 + Environment::Production => "production",
26 + }
27 + }
28 +
29 + pub fn smb_dir(&self) -> String {
30 + match self {
31 + Environment::Dev => ".smb-dev".to_string(),
32 + Environment::Production => ".smb".to_string(),
33 + }
34 + }
35 +
36 + pub fn api_protocol(&self) -> String {
37 + match self {
38 + Environment::Dev => "http".to_string(),
39 + Environment::Production => "https".to_string(),
40 + }
41 + }
42 + pub fn api_host(&self) -> String {
43 + match self {
44 + Environment::Dev => "localhost:8088".to_string(),
45 + Environment::Production => "api.smbcloud.xyz".to_string(),
46 + }
47 + }
48 +}
smbcloud-networking/src/lib.rs
+6 -4
@@ -1,8 +1,10 @@
1 pub mod constants;
2 +pub mod environment;
3
4 use crate::constants::TOKEN_PATH_STR;
5 use anyhow::{anyhow, Result};
5 -use constants::{SMB_API_HOST, SMB_API_PROTOCOL, SMB_CLIENT_ID, SMB_CLIENT_SECRET};
6 +use constants::{SMB_CLIENT_ID, SMB_CLIENT_SECRET};
7 +use environment::Environment;
8 use log::debug;
9 use std::path::PathBuf;
10 use url_builder::URLBuilder;
@@ -35,11 +37,11 @@ pub fn smb_token_file_path() -> Option<PathBuf> {
37 }
38 }
39
38 -pub fn smb_base_url_builder() -> URLBuilder {
40 +pub fn smb_base_url_builder(env: Environment) -> URLBuilder {
41 let mut url_builder = URLBuilder::new();
42 url_builder
41 - .set_protocol(SMB_API_PROTOCOL)
42 - .set_host(SMB_API_HOST)
43 + .set_protocol(&env.api_protocol())
44 + .set_host(&env.api_host())
45 .add_param("client_id", SMB_CLIENT_ID)
46 .add_param("client_secret", SMB_CLIENT_SECRET);
47 url_builder