@smbcloud / cli / commits / dcc138c

add me

Seto Elkahfi committed May 31, 2025 at 11:32 UTC dcc138c15e321549e66fd3c8ac16cac3d5f6f90c
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";