9 files changed
+123
-19
crates/cli/src/account/login/process.rs
+16
-13
@@ -1,6 +1,6 @@
1
use crate::{
2
account::{
3
- lib::{authorize_github, save_token},
3
+ lib::{authorize_github, is_logged_in, save_token},
4
signup::{do_signup, SignupMethod},
5
},
6
cli::CommandResult,
@@ -23,18 +23,18 @@ use smbcloud_networking::{
23
PATH_USERS_PASSWORD, PATH_USERS_SIGN_IN,
24
},
25
environment::Environment,
26
- smb_base_url_builder, smb_token_file_path,
26
+ smb_base_url_builder,
27
};
28
use smbcloud_utils::email_validation;
29
use spinners::Spinner;
30
31
pub async fn process_login(env: Environment) -> Result<CommandResult> {
32
// Check if token file exists
33
- if smb_token_file_path(env).is_some() {
33
+ if is_logged_in(env) {
34
return Ok(CommandResult {
35
spinner: Spinner::new(
36
spinners::Spinners::SimpleDotsScrolling,
37
- style("Loading...").green().bold().to_string(),
37
+ succeed_message("Loading"),
38
),
39
symbol: fail_symbol(),
40
msg: fail_message("You are already logged in. Please logout first."),
@@ -298,6 +298,11 @@ async fn login_with_email(env: Environment) -> Result<CommandResult> {
298
}
299
300
async fn do_process_login(env: Environment, args: LoginArgs) -> Result<CommandResult> {
301
+ let mut spinner = Spinner::new(
302
+ spinners::Spinners::SimpleDotsScrolling,
303
+ succeed_message("Loading"),
304
+ );
305
+
306
let login_params = LoginParams {
307
user: UserParam {
308
email: args.username,
@@ -320,10 +325,7 @@ async fn do_process_login(env: Environment, args: LoginArgs) -> Result<CommandRe
325
// Login successful
326
save_token(env, &response).await?;
327
Ok(CommandResult {
323
- spinner: Spinner::new(
324
- spinners::Spinners::SimpleDotsScrolling,
325
- style("Loading...").green().bold().to_string(),
326
- ),
328
+ spinner,
329
symbol: succeed_symbol(),
330
msg: succeed_message("You are logged in!"),
331
})
@@ -331,15 +333,16 @@ async fn do_process_login(env: Environment, args: LoginArgs) -> Result<CommandRe
333
StatusCode::NOT_FOUND => {
334
// Account not found and we show signup option
335
Ok(CommandResult {
334
- spinner: Spinner::new(
335
- spinners::Spinners::SimpleDotsScrolling,
336
- style("Account not found.").green().bold().to_string(),
337
- ),
336
+ spinner,
337
symbol: fail_symbol(),
339
- msg: fail_message("Please signup!"),
338
+ msg: fail_message("Account not found. Please signup!"),
339
})
340
}
341
StatusCode::UNPROCESSABLE_ENTITY => {
342
+ spinner.stop_and_persist(
343
+ &succeed_symbol(),
344
+ succeed_message("Please complete registration"),
345
+ );
346
// Account found but email not verified / password not set
347
let result: SmbAuthorization = response.json().await?;
348
// println!("Result: {:#?}", &result);
crates/cli/src/account/me/mod.rs
new
+90
@@ -0,0 +1,90 @@
1
+use crate::{
2
+ cli::CommandResult,
3
+ ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
4
+};
5
+use anyhow::{anyhow, Result};
6
+use reqwest::{Client, StatusCode};
7
+use smbcloud_model::account::User;
8
+use smbcloud_networking::{
9
+ constants::PATH_USERS_ME, environment::Environment, get_smb_token, smb_base_url_builder,
10
+};
11
+use spinners::Spinner;
12
+use tabled::{Table, Tabled};
13
+
14
+#[derive(Tabled)]
15
+struct UserRow {
16
+ #[tabled(rename = "ID")]
17
+ id: i32,
18
+ #[tabled(rename = "Email")]
19
+ email: String,
20
+ #[tabled(rename = "Created At")]
21
+ created_at: String,
22
+ #[tabled(rename = "Updated At")]
23
+ updated_at: String,
24
+}
25
+
26
+fn show_user(user: &User) {
27
+ let row = UserRow {
28
+ id: user.id,
29
+ email: user.email.clone(),
30
+ created_at: user.created_at.format("%Y-%m-%d %H:%M:%S").to_string(),
31
+ updated_at: user.updated_at.format("%Y-%m-%d %H:%M:%S").to_string(),
32
+ };
33
+ let table = Table::new(vec![row]);
34
+ println!("{table}");
35
+}
36
+
37
+pub async fn process_me(env: Environment) -> Result<CommandResult> {
38
+ let mut spinner = Spinner::new(
39
+ spinners::Spinners::SimpleDotsScrolling,
40
+ succeed_message("Loading"),
41
+ );
42
+ match me(env).await {
43
+ Ok(user) => {
44
+ spinner.stop_and_persist(&succeed_symbol(), succeed_message("Loaded."));
45
+ show_user(&user);
46
+ Ok(CommandResult {
47
+ spinner: Spinner::new(
48
+ spinners::Spinners::SimpleDotsScrolling,
49
+ succeed_message("Loading"),
50
+ ),
51
+ symbol: succeed_symbol(),
52
+ msg: succeed_message("User info loaded."),
53
+ })
54
+ }
55
+ Err(e) => {
56
+ spinner.stop_and_persist(
57
+ &fail_symbol(),
58
+ fail_message("Error while requesting your information."),
59
+ );
60
+ Err(e)
61
+ }
62
+ }
63
+}
64
+
65
+pub async fn me(env: Environment) -> Result<User> {
66
+ let token = get_smb_token(env).await?;
67
+
68
+ let response = Client::new()
69
+ .get(build_smb_info_url(env))
70
+ .header("Authorization", token)
71
+ .header("Accept", "application/json")
72
+ .header("Content-Type", "application/x-www-form-urlencoded")
73
+ .send()
74
+ .await?;
75
+
76
+ match response.status() {
77
+ StatusCode::OK => {
78
+ let user: User = response.json().await?;
79
+ Ok(user)
80
+ }
81
+ StatusCode::UNAUTHORIZED => Err(anyhow!("Invalid token. Please logout first.")),
82
+ _ => Err(anyhow!("Error while requesting your information.")),
83
+ }
84
+}
85
+
86
+fn build_smb_info_url(env: Environment) -> String {
87
+ let mut url_builder = smb_base_url_builder(env);
88
+ url_builder.add_route(PATH_USERS_ME);
89
+ url_builder.build()
90
+}
crates/cli/src/account/mod.rs
+1
@@ -3,6 +3,7 @@ pub mod forgot;
3
pub mod lib;
4
pub mod login;
5
pub mod logout;
6
+pub mod me;
7
pub mod signup;
8
9
use self::{cli::Commands, forgot::process_forgot, login::process_login, signup::process_signup};
crates/cli/src/cli/mod.rs
+2
@@ -32,6 +32,8 @@ pub struct Cli {
32
33
#[derive(Subcommand)]
34
pub enum Commands {
35
+ #[clap(about = "Your account info.", display_order = 3)]
36
+ Me {},
37
#[clap(
38
about = "Deploy project. This is smb main command. Requires an smbCloud account.",
39
display_order = 0
crates/cli/src/deploy/config.rs
+5
-4
@@ -4,7 +4,7 @@ use crate::{
4
};
5
use git2::{Cred, CredentialType, Error};
6
use serde::{Deserialize, Serialize};
7
-use smbcloud_model::project::Project;
7
+use smbcloud_model::{account::User, project::Project};
8
use smbcloud_networking::environment::Environment;
9
use smbcloud_networking_project::get_project;
10
use spinners::Spinner;
@@ -80,18 +80,19 @@ pub struct Config {
80
impl Config {
81
pub fn credentials(
82
&self,
83
+ user: User,
84
) -> impl FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + '_ {
85
move |_url, _username_from_url, _allowed_types| {
85
- Cred::ssh_key("git", None, Path::new(&self.ssh_key_path()), None)
86
+ Cred::ssh_key("git", None, Path::new(&self.ssh_key_path(user.id)), None)
87
}
88
}
89
89
- fn ssh_key_path(&self) -> String {
90
+ fn ssh_key_path(&self, user_id: i32) -> String {
91
// Use the dirs crate to get the home directory
92
let home = dirs::home_dir().expect("Could not determine home directory");
93
let key_path = home
94
.join(".ssh")
94
- .join(format!("id_{}@smbcloud.xyz", self.project.repository));
95
+ .join(format!("id_{}@smbcloud.xyz", user_id));
96
let key_path_str = key_path.to_string_lossy().to_string();
97
println!("Use key path: {}", key_path_str);
98
key_path_str
crates/cli/src/deploy/mod.rs
+3
-2
@@ -4,7 +4,7 @@ mod remote_messages;
4
mod setup;
5
6
use crate::{
7
- account::{lib::is_logged_in, login::process_login},
7
+ account::{lib::is_logged_in, login::process_login, me::me},
8
cli::CommandResult,
9
deploy::config::check_project,
10
ui::{fail_message, succeed_message, succeed_symbol},
@@ -66,11 +66,12 @@ pub async fn process_deploy(env: Environment) -> Result<CommandResult> {
66
};
67
68
let _deployment = create(env, config.project.id, payload).await?;
69
+ let user = me(env).await?;
70
71
let mut push_opts = PushOptions::new();
72
let mut callbacks = RemoteCallbacks::new();
73
// Set the credentials
73
- callbacks.credentials(config.credentials());
74
+ callbacks.credentials(config.credentials(user));
75
callbacks.sideband_progress(|data| {
76
// Convert bytes to string, print line by line
77
if let Ok(text) = std::str::from_utf8(data) {
crates/cli/src/main.rs
+2
@@ -3,6 +3,7 @@ use clap::Parser;
3
use console::style;
4
use smbcloud_cli::account::login::process_login;
5
use smbcloud_cli::account::logout::process_logout;
6
+use smbcloud_cli::account::me::process_me;
7
use smbcloud_cli::cli::CommandResult;
8
use smbcloud_cli::project::crud_create::process_project_init;
9
use smbcloud_cli::{
@@ -111,6 +112,7 @@ async fn run() -> Result<CommandResult> {
112
}
113
114
match cli.command {
115
+ Some(Commands::Me {}) => process_me(cli.environment).await,
116
Some(Commands::Init {}) => process_project_init(cli.environment).await,
117
Some(Commands::Deploy {}) => process_deploy(cli.environment).await,
118
Some(Commands::Account { command }) => process_account(cli.environment, command).await,
crates/smbcloud-model/src/account.rs
+3
@@ -1,4 +1,5 @@
1
use crate::signup::GithubEmail;
2
+use chrono::{DateTime, Utc};
3
use serde::{Deserialize, Serialize};
4
use serde_repr::{Deserialize_repr, Serialize_repr};
5
use std::fmt::{Display, Formatter};
@@ -8,6 +9,8 @@ use std::fmt::{Display, Formatter};
9
pub struct User {
10
pub id: i32,
11
pub email: String,
12
+ pub created_at: DateTime<Utc>,
13
+ pub updated_at: DateTime<Utc>,
14
}
15
16
impl Display for User {
crates/smbcloud-networking/src/constants.rs
+1
@@ -10,6 +10,7 @@ pub const SMB_CLIENT_ID: &str = "cli";
10
pub const SMB_CLIENT_SECRET: &str = "secretttttttt";
11
12
// Paths
13
+pub const PATH_USERS_ME: &str = "v1/me";
14
pub const PATH_USERS: &str = "v1/users";
15
pub const PATH_USERS_PASSWORD: &str = "v1/users/password";
16
pub const PATH_USERS_SIGN_OUT: &str = "v1/users/sign_out";