11 files changed
+285
-221
crates/cli/src/deploy/config.rs
+1
-1
@@ -1,6 +1,6 @@
1
use {
2
crate::{
3
- deploy::setup::setup_project,
3
+ deploy::setup_project::setup_project,
4
ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
5
},
6
git2::{Cred, CredentialType, Error},
crates/cli/src/deploy/mod.rs
+3
-1
@@ -3,4 +3,6 @@ mod detect_runner;
3
mod git;
4
pub mod process_deploy;
5
mod remote_messages;
6
-mod setup;
6
+pub(crate) mod setup_create_new_project;
7
+pub(crate) mod setup_project;
8
+pub(crate) mod setup_select_project;
crates/cli/src/deploy/setup.rs
deleted
-210
@@ -1,210 +0,0 @@
1
-use {
2
- crate::token::get_smb_token,
3
- crate::{deploy::config::Config, ui::highlight},
4
- dialoguer::{theme::ColorfulTheme, Confirm, Input, Select},
5
- regex::Regex,
6
- smbcloud_model::{
7
- error_codes::{ErrorCode, ErrorResponse},
8
- project::{Project, ProjectCreate},
9
- },
10
- smbcloud_network::environment::Environment,
11
- smbcloud_networking_project::{
12
- crud_project_create::create_project, crud_project_read::get_projects,
13
- },
14
- std::{env, fs, path::Path},
15
-};
16
-
17
-pub async fn setup_project(env: Environment) -> Result<Config, ErrorResponse> {
18
- let path = env::current_dir().ok();
19
- let path_str = path
20
- .as_ref()
21
- .map(|p| p.display().to_string())
22
- .unwrap_or_else(|| ".".to_string());
23
-
24
- let confirm = Confirm::with_theme(&ColorfulTheme::default())
25
- .with_prompt(format!("Setup project in {}? y/n", highlight(&path_str)))
26
- .interact()
27
- .map_err(|_| ErrorResponse::Error {
28
- error_code: ErrorCode::InputError,
29
- message: ErrorCode::InputError.message(None).to_string(),
30
- })?;
31
-
32
- if !confirm {
33
- return Err(ErrorResponse::Error {
34
- error_code: ErrorCode::Cancel,
35
- message: ErrorCode::Cancel.message(None).to_string(),
36
- });
37
- }
38
-
39
- let access_token = match get_smb_token(env).await {
40
- Ok(token) => token,
41
- Err(_) => {
42
- return Err(ErrorResponse::Error {
43
- error_code: ErrorCode::Unauthorized,
44
- message: ErrorCode::Unauthorized.message(None).to_string(),
45
- })
46
- }
47
- };
48
-
49
- let projects = match get_projects(env, access_token).await {
50
- Ok(x) => x,
51
- Err(_) => {
52
- return Err(ErrorResponse::Error {
53
- error_code: ErrorCode::InputError,
54
- message: ErrorCode::InputError.message(None).to_string(),
55
- })
56
- }
57
- };
58
-
59
- let project: Project = if !projects.is_empty() {
60
- select_project(env, projects, &path_str).await?
61
- } else {
62
- create_new_project(env, &path_str).await?
63
- };
64
-
65
- let name = project.name.clone();
66
- let description = project.description.clone();
67
-
68
- // Create config struct
69
- let config = Config {
70
- project,
71
- name,
72
- description,
73
- };
74
-
75
- // Ensure .smb directory exists
76
- let smb_dir = Path::new(".smb");
77
- if !smb_dir.exists() {
78
- fs::create_dir(smb_dir).map_err(|_| ErrorResponse::Error {
79
- error_code: ErrorCode::MissingConfig,
80
- message: ErrorCode::MissingConfig.message(None).to_string(),
81
- })?;
82
- }
83
-
84
- // Write config to .smb/config.toml
85
- let config_toml = toml::to_string(&config).map_err(|_| ErrorResponse::Error {
86
- error_code: ErrorCode::MissingConfig,
87
- message: ErrorCode::MissingConfig.message(None).to_string(),
88
- })?;
89
- fs::write(".smb/config.toml", config_toml).map_err(|_| ErrorResponse::Error {
90
- error_code: ErrorCode::MissingConfig,
91
- message: ErrorCode::MissingConfig.message(None).to_string(),
92
- })?;
93
-
94
- Ok(config)
95
-}
96
-
97
-async fn select_project(
98
- env: Environment,
99
- projects: Vec<Project>,
100
- path: &str,
101
-) -> Result<Project, ErrorResponse> {
102
- let confirm = Confirm::with_theme(&ColorfulTheme::default())
103
- .with_prompt("Use existing project? y/n")
104
- .interact()
105
- .map_err(|_| ErrorResponse::Error {
106
- error_code: ErrorCode::InputError,
107
- message: ErrorCode::InputError.message(None).to_string(),
108
- })?;
109
-
110
- if !confirm {
111
- return create_new_project(env, path).await;
112
- }
113
- let selection = Select::with_theme(&ColorfulTheme::default())
114
- .items(&projects)
115
- .default(0)
116
- .interact()
117
- .map_err(|_| ErrorResponse::Error {
118
- error_code: ErrorCode::InputError,
119
- message: ErrorCode::InputError.message(None).to_string(),
120
- })?;
121
-
122
- let project = projects[selection].clone();
123
-
124
- Ok(project)
125
-}
126
-
127
-async fn create_new_project(env: Environment, path: &str) -> Result<Project, ErrorResponse> {
128
- let default_name = Path::new(path)
129
- .file_name()
130
- .and_then(|os_str| os_str.to_str())
131
- .unwrap_or("project")
132
- .to_lowercase()
133
- .replace([' ', '-'], "")
134
- .replace('-', "");
135
-
136
- let name = match Input::<String>::with_theme(&ColorfulTheme::default())
137
- .with_prompt("Project name")
138
- .default(default_name.to_string())
139
- .interact()
140
- {
141
- Ok(project_name) => project_name,
142
- Err(_) => {
143
- return Err(ErrorResponse::Error {
144
- error_code: ErrorCode::InputError,
145
- message: ErrorCode::InputError.message(None).to_string(),
146
- });
147
- }
148
- };
149
-
150
- // Create a repository name: lowercased, remove spaces and special characters
151
- let re = Regex::new(r"[^a-zA-Z0-9_-]").unwrap();
152
- let default_repository = name
153
- .clone()
154
- .to_lowercase()
155
- .replace(' ', "_")
156
- .replace('-', "");
157
- let default_repo = re.replace_all(&default_repository, "");
158
-
159
- let repository = match Input::<String>::with_theme(&ColorfulTheme::default())
160
- .default(default_repo.to_string())
161
- .with_prompt("Repository")
162
- .interact()
163
- {
164
- Ok(repo) => repo,
165
- Err(_) => {
166
- return Err(ErrorResponse::Error {
167
- error_code: ErrorCode::InputError,
168
- message: ErrorCode::InputError.message(None).to_string(),
169
- });
170
- }
171
- };
172
-
173
- let description = match Input::<String>::with_theme(&ColorfulTheme::default())
174
- .with_prompt("Description")
175
- .interact()
176
- {
177
- Ok(description) => description,
178
- Err(_) => {
179
- return Err(ErrorResponse::Error {
180
- error_code: ErrorCode::InputError,
181
- message: ErrorCode::InputError.message(None).to_string(),
182
- });
183
- }
184
- };
185
-
186
- let access_token = match get_smb_token(env).await {
187
- Ok(token) => token,
188
- Err(_) => {
189
- return Err(ErrorResponse::Error {
190
- error_code: ErrorCode::Unauthorized,
191
- message: ErrorCode::Unauthorized.message(None).to_string(),
192
- })
193
- }
194
- };
195
-
196
- match create_project(
197
- env,
198
- access_token,
199
- ProjectCreate {
200
- name,
201
- repository,
202
- description,
203
- },
204
- )
205
- .await
206
- {
207
- Ok(project) => Ok(project),
208
- Err(e) => Err(e),
209
- }
210
-}
crates/cli/src/deploy/setup_create_new_project.rs
new
+100
@@ -0,0 +1,100 @@
1
+use {
2
+ crate::token::get_smb_token,
3
+ dialoguer::{theme::ColorfulTheme, Input},
4
+ regex::Regex,
5
+ smbcloud_model::{
6
+ error_codes::{ErrorCode, ErrorResponse},
7
+ project::{Project, ProjectCreate},
8
+ },
9
+ smbcloud_network::environment::Environment,
10
+ smbcloud_networking_project::crud_project_create::create_project,
11
+ std::path::Path,
12
+};
13
+
14
+pub(crate) async fn create_new_project(
15
+ env: Environment,
16
+ path: &str,
17
+) -> Result<Project, ErrorResponse> {
18
+ let default_name = Path::new(path)
19
+ .file_name()
20
+ .and_then(|os_str| os_str.to_str())
21
+ .unwrap_or("project")
22
+ .to_lowercase()
23
+ .replace([' ', '-'], "")
24
+ .replace('-', "");
25
+
26
+ let name = match Input::<String>::with_theme(&ColorfulTheme::default())
27
+ .with_prompt("Project name")
28
+ .default(default_name.to_string())
29
+ .interact()
30
+ {
31
+ Ok(project_name) => project_name,
32
+ Err(_) => {
33
+ return Err(ErrorResponse::Error {
34
+ error_code: ErrorCode::InputError,
35
+ message: ErrorCode::InputError.message(None).to_string(),
36
+ });
37
+ }
38
+ };
39
+
40
+ // Create a repository name: lowercased, remove spaces and special characters
41
+ let re = Regex::new(r"[^a-zA-Z0-9_-]").unwrap();
42
+ let default_repository = name
43
+ .clone()
44
+ .to_lowercase()
45
+ .replace(' ', "_")
46
+ .replace('-', "");
47
+ let default_repo = re.replace_all(&default_repository, "");
48
+
49
+ let repository = match Input::<String>::with_theme(&ColorfulTheme::default())
50
+ .default(default_repo.to_string())
51
+ .with_prompt("Repository")
52
+ .interact()
53
+ {
54
+ Ok(repo) => repo,
55
+ Err(_) => {
56
+ return Err(ErrorResponse::Error {
57
+ error_code: ErrorCode::InputError,
58
+ message: ErrorCode::InputError.message(None).to_string(),
59
+ });
60
+ }
61
+ };
62
+
63
+ let description = match Input::<String>::with_theme(&ColorfulTheme::default())
64
+ .with_prompt("Description")
65
+ .interact()
66
+ {
67
+ Ok(description) => description,
68
+ Err(_) => {
69
+ return Err(ErrorResponse::Error {
70
+ error_code: ErrorCode::InputError,
71
+ message: ErrorCode::InputError.message(None).to_string(),
72
+ });
73
+ }
74
+ };
75
+
76
+ let access_token = match get_smb_token(env).await {
77
+ Ok(token) => token,
78
+ Err(_) => {
79
+ return Err(ErrorResponse::Error {
80
+ error_code: ErrorCode::Unauthorized,
81
+ message: ErrorCode::Unauthorized.message(None).to_string(),
82
+ })
83
+ }
84
+ };
85
+
86
+ match create_project(
87
+ env,
88
+ access_token,
89
+ ProjectCreate {
90
+ name,
91
+ repository,
92
+ description,
93
+ },
94
+ )
95
+ .await
96
+ {
97
+ Ok(project) => Ok(project),
98
+ Err(e) => Err(e),
99
+ }
100
+}
crates/cli/src/deploy/setup_project.rs
new
+90
@@ -0,0 +1,90 @@
1
+use {
2
+ crate::{
3
+ deploy::{
4
+ config::Config, setup_create_new_project::create_new_project,
5
+ setup_select_project::select_project,
6
+ },
7
+ token::get_smb_token,
8
+ ui::highlight,
9
+ },
10
+ dialoguer::{theme::ColorfulTheme, Confirm},
11
+ smbcloud_model::{
12
+ error_codes::{ErrorCode, ErrorResponse},
13
+ project::Project,
14
+ },
15
+ smbcloud_network::environment::Environment,
16
+ smbcloud_networking_project::crud_project_read::get_projects,
17
+ std::{env, fs, path::Path},
18
+};
19
+
20
+pub(crate) async fn setup_project(env: Environment) -> Result<Config, ErrorResponse> {
21
+ let path = env::current_dir().ok();
22
+ let path_str = path
23
+ .as_ref()
24
+ .map(|p| p.display().to_string())
25
+ .unwrap_or_else(|| ".".to_string());
26
+
27
+ let confirm = Confirm::with_theme(&ColorfulTheme::default())
28
+ .with_prompt(format!("Setup project in {}? y/n", highlight(&path_str)))
29
+ .interact()
30
+ .map_err(|_| ErrorResponse::Error {
31
+ error_code: ErrorCode::InputError,
32
+ message: ErrorCode::InputError.message(None).to_string(),
33
+ })?;
34
+
35
+ if !confirm {
36
+ return Err(ErrorResponse::Error {
37
+ error_code: ErrorCode::Cancel,
38
+ message: ErrorCode::Cancel.message(None).to_string(),
39
+ });
40
+ }
41
+
42
+ let access_token = match get_smb_token(env).await {
43
+ Ok(token) => token,
44
+ Err(_) => {
45
+ return Err(ErrorResponse::Error {
46
+ error_code: ErrorCode::Unauthorized,
47
+ message: ErrorCode::Unauthorized.message(None).to_string(),
48
+ })
49
+ }
50
+ };
51
+
52
+ let projects = get_projects(env, access_token).await?;
53
+
54
+ let project: Project = if !projects.is_empty() {
55
+ select_project(env, projects, &path_str).await?
56
+ } else {
57
+ create_new_project(env, &path_str).await?
58
+ };
59
+
60
+ let name = project.name.clone();
61
+ let description = project.description.clone();
62
+
63
+ // Create config struct
64
+ let config = Config {
65
+ project,
66
+ name,
67
+ description,
68
+ };
69
+
70
+ // Ensure .smb directory exists
71
+ let smb_dir = Path::new(".smb");
72
+ if !smb_dir.exists() {
73
+ fs::create_dir(smb_dir).map_err(|_| ErrorResponse::Error {
74
+ error_code: ErrorCode::MissingConfig,
75
+ message: ErrorCode::MissingConfig.message(None).to_string(),
76
+ })?;
77
+ }
78
+
79
+ // Write config to .smb/config.toml
80
+ let config_toml = toml::to_string(&config).map_err(|_| ErrorResponse::Error {
81
+ error_code: ErrorCode::MissingConfig,
82
+ message: ErrorCode::MissingConfig.message(None).to_string(),
83
+ })?;
84
+ fs::write(".smb/config.toml", config_toml).map_err(|_| ErrorResponse::Error {
85
+ error_code: ErrorCode::MissingConfig,
86
+ message: ErrorCode::MissingConfig.message(None).to_string(),
87
+ })?;
88
+
89
+ Ok(config)
90
+}
crates/cli/src/deploy/setup_select_project.rs
new
+39
@@ -0,0 +1,39 @@
1
+use {
2
+ crate::deploy::setup_create_new_project::create_new_project,
3
+ dialoguer::{theme::ColorfulTheme, Confirm, Select},
4
+ smbcloud_model::{
5
+ error_codes::{ErrorCode, ErrorResponse},
6
+ project::Project,
7
+ },
8
+ smbcloud_network::environment::Environment,
9
+};
10
+
11
+pub(crate) async fn select_project(
12
+ env: Environment,
13
+ projects: Vec<Project>,
14
+ path: &str,
15
+) -> Result<Project, ErrorResponse> {
16
+ let confirm = Confirm::with_theme(&ColorfulTheme::default())
17
+ .with_prompt("Use existing project? y/n")
18
+ .interact()
19
+ .map_err(|_| ErrorResponse::Error {
20
+ error_code: ErrorCode::InputError,
21
+ message: ErrorCode::InputError.message(None).to_string(),
22
+ })?;
23
+
24
+ if !confirm {
25
+ return create_new_project(env, path).await;
26
+ }
27
+ let selection = Select::with_theme(&ColorfulTheme::default())
28
+ .items(&projects)
29
+ .default(0)
30
+ .interact()
31
+ .map_err(|_| ErrorResponse::Error {
32
+ error_code: ErrorCode::InputError,
33
+ message: ErrorCode::InputError.message(None).to_string(),
34
+ })?;
35
+
36
+ let project = projects[selection].clone();
37
+
38
+ Ok(project)
39
+}
crates/cli/src/token/mod.rs
+20
-5
@@ -1,8 +1,12 @@
1
-use anyhow::{anyhow, Result};
2
-use dirs::home_dir;
3
-use log::debug;
4
-use smbcloud_network::environment::Environment;
5
-use std::path::{Path, PathBuf};
1
+use {
2
+ anyhow::{anyhow, Result},
3
+ dirs::home_dir,
4
+ log::debug,
5
+ smbcloud_network::environment::Environment,
6
+ std::path::{Path, PathBuf},
7
+};
8
+
9
+pub(crate) mod test_token_validity;
10
11
pub async fn get_smb_token(env: Environment) -> Result<String> {
12
if let Some(path) = smb_token_file_path(env) {
@@ -31,3 +35,14 @@ pub fn smb_token_file_path(env: Environment) -> Option<PathBuf> {
35
}
36
}
37
}
38
+
39
+pub fn remove_smb_token(env: Environment) -> Result<()> {
40
+ if let Some(path) = smb_token_file_path(env) {
41
+ std::fs::remove_file(path).map_err(|e| {
42
+ debug!("Error while removing token: {}", &e);
43
+ anyhow!("Error while removing token. Are you logged in?")
44
+ })
45
+ } else {
46
+ Err(anyhow!("Failed to get home directory. Are you logged in?"))
47
+ }
48
+}
crates/cli/src/token/test_token_validity.rs
new
+25
@@ -0,0 +1,25 @@
1
+use crate::token::{get_smb_token, smb_token_file_path};
2
+use anyhow::{anyhow, Result};
3
+use dialoguer::{theme::ColorfulTheme, Confirm};
4
+use reqwest::{Client, StatusCode};
5
+use smbcloud_network::environment::Environment;
6
+use smbcloud_networking::{constants::PATH_USERS_SIGN_OUT, smb_base_url_builder};
7
+use spinners::Spinner;
8
+use std::fs;
9
+
10
+async fn do_process_logout(env: Environment) -> Result<()> {
11
+ let token = get_smb_token(env).await?;
12
+
13
+ let response = Client::new()
14
+ .delete(build_smb_logout_url(env))
15
+ .header("Authorization", token)
16
+ .header("Accept", "application/json")
17
+ .header("Content-Type", "application/x-www-form-urlencoded")
18
+ .send()
19
+ .await?;
20
+
21
+ match response.status() {
22
+ StatusCode::OK => Ok(()),
23
+ _ => Err(anyhow!("Failed to logout.")),
24
+ }
25
+}
crates/smbcloud-network/src/network.rs
+1
-1
@@ -4,7 +4,7 @@ use serde::de::DeserializeOwned;
4
use smbcloud_model::error_codes::{ErrorCode, ErrorResponse};
5
//use std::time::Duration;
6
#[cfg(debug_assertions)]
7
-const LOG_RESPONSE_BODY: bool = false; // You know what to do here.
7
+const LOG_RESPONSE_BODY: bool = true; // You know what to do here.
8
#[cfg(not(debug_assertions))]
9
const LOG_RESPONSE_BODY: bool = false;
10
crates/smbcloud-networking/src/lib.rs
+6
-3
@@ -1,8 +1,11 @@
1
pub mod constants;
2
+pub mod valid_token;
3
3
-use constants::{SMB_CLIENT_ID, SMB_CLIENT_SECRET};
4
-use smbcloud_network::environment::Environment;
5
-use url_builder::URLBuilder;
4
+use {
5
+ constants::{SMB_CLIENT_ID, SMB_CLIENT_SECRET},
6
+ smbcloud_network::environment::Environment,
7
+ url_builder::URLBuilder,
8
+};
9
10
pub fn smb_base_url_builder(env: Environment) -> URLBuilder {
11
let mut url_builder = URLBuilder::new();