11 files changed
+49
-80
crates/cli/README.md
+6
-6
@@ -22,7 +22,7 @@
22
23
## Installation
24
25
-One can install this program in different ways.
25
+You can install the CLI a few different ways.
26
27
### With Cargo
28
@@ -51,7 +51,7 @@ pip install smbcloud-cli
51
52
## Update
53
54
-Simply rerun the installation command.
54
+To update the CLI, run the same install command again.
55
56
## Uninstall
57
@@ -78,16 +78,16 @@ smb --help
78
79
## Contribution
80
81
-- Setup your Rust tooling.
81
+- Set up your Rust tooling.
82
- Clone the repo.
83
-- Provide the environment variables in the .env.local.
83
+- Add the required environment variables to `.env.local`.
84
- Run `cargo run`.
85
86
## Credits
87
88
-This repo is inspired by [Sugar](https://github.com/metaplex-foundation/sugar).
88
+This repo draws inspiration from [Sugar](https://github.com/metaplex-foundation/sugar).
89
90
-This repo tries to follow [the 12 factor CLI app](https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46) principles by
90
+It also borrows ideas from [the 12 factor CLI app](https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46).
91
92
> Explore more on the [smbCloud Services](https://smbcloud.xyz/services) page.
93
crates/cli/src/account/cli.rs
+4
-4
@@ -2,12 +2,12 @@ use clap::Subcommand;
2
3
#[derive(Subcommand)]
4
pub enum Commands {
5
- #[clap(about = "Create an account. Use your email as your username.")]
5
+ #[clap(about = "Create an account with your email address.")]
6
Signup {},
7
- #[clap(about = "Login to your account. To create an account, use smb signup.")]
7
+ #[clap(about = "Log in to your account. If you need one first, run `smb signup`.")]
8
Login {},
9
- #[clap(about = "Logout all session.")]
9
+ #[clap(about = "Log out of your current session.")]
10
Logout {},
11
- #[clap(about = "Forgot email? Use this command to reset your password.")]
11
+ #[clap(about = "Start the password reset flow.")]
12
Forgot {},
13
}
crates/cli/src/account/forgot/mod.rs
+1
-1
@@ -13,7 +13,7 @@ use smbcloud_utils::email_validation;
13
use spinners::Spinner;
14
15
pub async fn process_forgot(env: Environment) -> Result<CommandResult> {
16
- println!("Provide your login credentials.");
16
+ println!("Enter your email address.");
17
let email = Input::<String>::with_theme(&ColorfulTheme::default())
18
.with_prompt("Email")
19
.validate_with(|email: &String| email_validation(email))
crates/cli/src/account/lib.rs
+6
-16
@@ -25,9 +25,7 @@ use {
25
};
26
27
pub async fn authorize_github(env: &Environment) -> Result<SmbAuthorization> {
28
- // Spin up a simple localhost server to listen for the GitHub OAuth callback
29
- // setup_oauth_callback_server();
30
- // Open the GitHub OAuth URL in the user's browser
28
+ // Listen for the GitHub OAuth callback on localhost, then open the browser.
29
let mut spinner = Spinner::new(
30
spinners::Spinners::BouncingBall,
31
style("🚀 Getting your GitHub information...")
@@ -61,7 +59,6 @@ pub async fn authorize_github(env: &Environment) -> Result<SmbAuthorization> {
59
match rx.recv() {
60
Ok(code) => {
61
debug!("Got code from channel: {:#?}", &code);
64
- //Err(anyhow!("Failed to get code from channel."))
62
process_connect_github(*env, code).await
63
}
64
Err(e) => {
@@ -144,7 +141,6 @@ fn handle_connection(mut stream: TcpStream, tx: Sender<String>) {
141
stream.flush().unwrap();
142
}
143
147
-// Get access token
144
pub async fn process_connect_github(env: Environment, code: String) -> Result<SmbAuthorization> {
145
let response = Client::new()
146
.post(build_authorize_smb_url(env))
@@ -160,10 +156,8 @@ pub async fn process_connect_github(env: Environment, code: String) -> Result<Sm
156
.bold()
157
.to_string(),
158
);
163
- // println!("Response: {:#?}", &response);
159
match response.status() {
160
StatusCode::OK => {
166
- // Account authorized and token received
161
spinner.stop_and_persist("✅", "You are logged in with your GitHub account!".into());
162
save_token(env, &response).await?;
163
let result = response.json().await?;
@@ -171,21 +165,16 @@ pub async fn process_connect_github(env: Environment, code: String) -> Result<Sm
165
Ok(result)
166
}
167
StatusCode::NOT_FOUND => {
174
- // Account not found and we show signup option
175
- spinner.stop_and_persist("🥲", "Account not found. Please signup!".into());
168
+ spinner.stop_and_persist("🥲", "Account not found. Please sign up.".into());
169
let result = response.json().await?;
177
- // println!("Result: {:#?}", &result);
170
Ok(result)
171
}
172
StatusCode::UNPROCESSABLE_ENTITY => {
181
- // Account found but email not verified
182
- spinner.stop_and_persist("🥹", "Unverified email!".into());
173
+ spinner.stop_and_persist("🥹", "Please verify your email address.".into());
174
let result = response.json().await?;
184
- // println!("Result: {:#?}", &result);
175
Ok(result)
176
}
177
_ => {
188
- // Other errors
178
let error = anyhow!("Error while authorizing with GitHub.");
179
Err(error)
180
}
@@ -222,13 +211,14 @@ fn github_base_url_builder() -> URLBuilder {
211
212
pub async fn save_token(env: Environment, response: &Response) -> Result<()> {
213
let headers = response.headers();
225
- // println!("Headers: {:#?}", &headers);
214
match headers.get("Authorization") {
215
Some(token) => {
216
debug!("{}", token.to_str()?);
217
store_token(env, token.to_str()?.to_string()).await
218
}
231
- None => Err(anyhow!("Failed to get token. Probably a backend issue.")),
219
+ None => Err(anyhow!(
220
+ "Failed to get the access token from the server response."
221
+ )),
222
}
223
}
224
crates/cli/src/account/login/process.rs
+13
-27
@@ -78,8 +78,6 @@ pub async fn process_login(env: Environment, is_logged_in: Option<bool>) -> Resu
78
}
79
}
80
81
-// Private functions
82
-
81
async fn login_with_github(env: Environment) -> Result<CommandResult> {
82
match authorize_github(&env).await {
83
Ok(result) => process_authorization(env, result).await,
@@ -91,15 +89,13 @@ async fn login_with_github(env: Environment) -> Result<CommandResult> {
89
}
90
91
async fn process_authorization(env: Environment, auth: SmbAuthorization) -> Result<CommandResult> {
94
- // What to do if not logged in with GitHub?
95
- // Check error_code first
92
+ // Handle the account state returned by the OAuth flow before treating it as a login.
93
if let Some(error_code) = auth.error_code {
94
debug!("{}", error_code);
95
match error_code {
96
EmailNotFound => return create_new_account(env, auth.user_email, auth.user_info).await,
97
EmailUnverified => return send_email_verification(env, auth.user).await,
98
PasswordNotSet => {
102
- // Only for email and password login
99
let error = anyhow!("Password not set.");
100
return Err(error);
101
}
@@ -107,14 +103,12 @@ async fn process_authorization(env: Environment, auth: SmbAuthorization) -> Resu
103
}
104
}
105
110
- // Logged in with GitHub!
111
- // Token handling is in the lib.rs account module.
106
+ // Token handling lives in the account module.
107
if let Some(user) = auth.user {
108
let spinner = Spinner::new(
109
spinners::Spinners::SimpleDotsScrolling,
110
style("Logging you in...").green().bold().to_string(),
111
);
117
- // We're logged in with GitHub.
112
return Ok(CommandResult {
113
spinner,
114
symbol: "✅".to_owned(),
@@ -142,7 +136,6 @@ async fn create_new_account(
136
}
137
};
138
145
- // Create account if user confirms
139
if !confirm {
140
let spinner = Spinner::new(
141
spinners::Spinners::SimpleDotsScrolling,
@@ -169,11 +162,10 @@ async fn create_new_account(
162
return do_signup(env, ¶ms).await;
163
}
164
172
- Err(anyhow!("Shouldn't be here."))
165
+ Err(anyhow!("GitHub returned incomplete account details."))
166
}
167
168
async fn send_email_verification(env: Environment, user: Option<User>) -> Result<CommandResult> {
176
- // Return early if user is null
169
if let Some(user) = user {
170
let confirm = match Confirm::with_theme(&ColorfulTheme::default())
171
.with_prompt("Do you want to send a new verification email?")
@@ -186,16 +178,15 @@ async fn send_email_verification(env: Environment, user: Option<User>) -> Result
178
}
179
};
180
189
- // Send verification email if user confirms
181
if !confirm {
182
let spinner = Spinner::new(
183
spinners::Spinners::SimpleDotsScrolling,
193
- style("Cancel operation.").green().bold().to_string(),
184
+ style("Cancelled.").green().bold().to_string(),
185
);
186
return Ok(CommandResult {
187
spinner,
188
symbol: succeed_symbol(),
198
- msg: succeed_message("Doing nothing."),
189
+ msg: succeed_message("Cancelled."),
190
});
191
}
192
resend_email_verification(env, user).await
@@ -238,16 +229,15 @@ async fn connect_github_account(env: Environment, auth: SmbAuthorization) -> Res
229
}
230
};
231
241
- // Link GitHub account if user confirms
232
if !confirm {
233
let spinner = Spinner::new(
234
spinners::Spinners::SimpleDotsScrolling,
245
- succeed_message("Cancel operation."),
235
+ succeed_message("Cancelled."),
236
);
237
return Ok(CommandResult {
238
spinner,
239
symbol: succeed_symbol(),
250
- msg: succeed_message("Doing nothing."),
240
+ msg: succeed_message("Cancelled."),
241
});
242
}
243
@@ -278,7 +268,7 @@ async fn connect_github_account(env: Environment, auth: SmbAuthorization) -> Res
268
}
269
270
async fn login_with_email(env: Environment) -> Result<CommandResult> {
281
- println!("Provide your login credentials.");
271
+ println!("Enter your login details.");
272
let username = match Input::<String>::with_theme(&ColorfulTheme::default())
273
.with_prompt("Email")
274
.validate_with(|email: &String| email_validation(email))
@@ -293,9 +283,7 @@ async fn login_with_email(env: Environment) -> Result<CommandResult> {
283
284
match check_email(env, client(), &username).await {
285
Ok(auth) => {
296
- // Only continue with password input if email is found and confirmed.
286
if auth.error_code.is_some() {
298
- // Check if email is in the database, unconfirmed. Only presents password input if email is found and confirmed.
287
let spinner = Spinner::new(
288
spinners::Spinners::SimpleDotsScrolling,
289
succeed_message("Checking email"),
@@ -382,11 +370,11 @@ async fn after_checking_email_step(
370
}
371
_ => {
372
spinner.stop_and_persist(&fail_symbol(), fail_message("An error occurred."));
385
- Err(anyhow!("Idk what happened."))
373
+ Err(anyhow!("The server returned an unexpected account state."))
374
}
375
}
376
}
389
- None => Err(anyhow!("Shouldn't be here.")),
377
+ None => Err(anyhow!("The server did not return an account state.")),
378
}
379
}
380
@@ -421,13 +409,12 @@ async fn action_on_account_status(
409
}
410
_ => {
411
spinner.stop_and_persist(&fail_symbol(), fail_message("An error occurred."));
424
- Err(anyhow!("Idk what happened."))
412
+ Err(anyhow!("The server returned an unexpected account state."))
413
}
414
}
415
}
416
417
async fn send_reset_password(env: Environment, user: Option<User>) -> Result<CommandResult> {
430
- // Return early if user is null
418
if let Some(user) = user {
419
let confirm = match Confirm::with_theme(&ColorfulTheme::default())
420
.with_prompt("Do you want to reset your password?")
@@ -440,16 +427,15 @@ async fn send_reset_password(env: Environment, user: Option<User>) -> Result<Com
427
}
428
};
429
443
- // Send verification email if user confirms
430
if !confirm {
431
let spinner = Spinner::new(
432
spinners::Spinners::SimpleDotsScrolling,
447
- style("Cancel operation.").green().bold().to_string(),
433
+ style("Cancelled.").green().bold().to_string(),
434
);
435
return Ok(CommandResult {
436
spinner,
437
symbol: succeed_symbol(),
452
- msg: succeed_message("Doing nothing."),
438
+ msg: succeed_message("Cancelled."),
439
});
440
}
441
resend_reset_password_instruction(env, user).await
crates/cli/src/account/logout/mod.rs
+2
-4
@@ -14,7 +14,6 @@ use {
14
};
15
16
pub async fn process_logout(env: Environment) -> Result<CommandResult> {
17
- // Logout if user confirms
17
if let Some(token_path) = smb_token_file_path(env) {
18
let confirm = match Confirm::with_theme(&ColorfulTheme::default())
19
.with_prompt("Do you want to logout? y/n")
@@ -30,10 +29,10 @@ pub async fn process_logout(env: Environment) -> Result<CommandResult> {
29
return Ok(CommandResult {
30
spinner: Spinner::new(
31
spinners::Spinners::SimpleDotsScrolling,
33
- succeed_message("Cancel operation"),
32
+ succeed_message("Cancelled."),
33
),
34
symbol: succeed_symbol(),
36
- msg: succeed_message("Doing nothing."),
35
+ msg: succeed_message("Cancelled."),
36
});
37
}
38
@@ -42,7 +41,6 @@ pub async fn process_logout(env: Environment) -> Result<CommandResult> {
41
succeed_message("Logging you out"),
42
);
43
45
- // Call backend
44
match do_process_logout(env).await {
45
Ok(_) => {
46
fs::remove_file(token_path)?;
crates/smbcloud-model/src/error_codes.rs
+2
-2
@@ -39,7 +39,7 @@ pub enum ErrorCode {
39
MissingConfig = 4,
40
// #[error("Missing id in repository. Please regenerate with 'smb init'.")]
41
// MissingId,
42
- #[error("Cancel operation.")]
42
+ #[error("Cancelled.")]
43
Cancel = 5,
44
// Account
45
#[error("Unauthorized access.")]
@@ -122,7 +122,7 @@ impl ErrorCode {
122
// CLI Generic errors
123
ErrorCode::InputError => "Input error.",
124
ErrorCode::MissingConfig => "Missing config.",
125
- ErrorCode::Cancel => "Cancelled operation.",
125
+ ErrorCode::Cancel => "Cancelled.",
126
// Projects
127
ErrorCode::ProjectNotFound => "Project not found.",
128
ErrorCode::UnsupportedRunner => "Unsupported runner.",
crates/smbcloud-networking/README.md
+1
-1
@@ -2,7 +2,7 @@
2
3
Low-level HTTP client helpers for smbCloud services.
4
5
-This crate was inspired by this excellent blog post: [Designing a Web API client in Rust](https://nullderef.com/blog/web-api-client/).
5
+This crate takes a few cues from [Designing a Web API client in Rust](https://nullderef.com/blog/web-api-client/).
6
7
> Explore more on the [smbCloud Services](https://smbcloud.xyz/services) page.
8
npm/smbcloud-cli/src/index.ts
+7
-5
@@ -20,17 +20,19 @@ function getExePath() {
20
}
21
22
try {
23
- // Since the binary will be located inside `node_modules`, we can simply call `require.resolve`
24
- return require.resolve(`@smbcloud/cli-${os}-${arch}/bin/smb${extension}`);
23
+ // The binary lives in `node_modules`, so `require.resolve` gives us the installed path.
24
+ return require.resolve(
25
+ `@smbcloud/cli-${os}-${arch}/bin/smb${extension}`,
26
+ );
27
} catch (e) {
28
throw new Error(
27
- `Couldn't find application binary inside node_modules for ${os}-${arch}`
29
+ `Couldn't find application binary inside node_modules for ${os}-${arch}`,
30
);
31
}
32
}
33
34
/**
33
- * Runs the application with args using nodejs spawn
35
+ * Runs the native CLI with the current process arguments.
36
*/
37
function run() {
38
const args = process.argv.slice(2);
@@ -38,4 +40,4 @@ function run() {
40
process.exit(processResult.status ?? 0);
41
}
42
41
-run();
\ No newline at end of file
43
+run();
sdk/gems/model/lib/model.rb
-1
@@ -5,5 +5,4 @@ require_relative 'model/model'
5
6
module Model
7
class Error < StandardError; end
8
- # Your code goes here...
8
end
sdk/gems/model/model.gemspec
+7
-13
@@ -5,12 +5,12 @@ require_relative 'lib/model/version'
5
Gem::Specification.new do |spec|
6
spec.name = 'smbcloud-model'
7
spec.version = Model::VERSION
8
- spec.authors = ["paydii"]
8
+ spec.authors = ["Seto Elkahfi"]
9
spec.email = ["hej@setoelkahfi.se"]
10
11
- spec.summary = "Ruby binding for smbcloud-cli model."
12
- spec.description = "Ruby binding for smbcloud-cli modellllllllllll."
13
- spec.homepage = "https://github.com/smbcloudXYZ/smbcloud-cli/tree/development/gems/model"
11
+ spec.summary = "Ruby bindings for the smbCloud model types."
12
+ spec.description = "Ruby bindings for shared smbCloud model types, backed by a native Rust extension."
13
+ spec.homepage = "https://github.com/smbcloudXYZ/smbcloud-cli/tree/main/sdk/gems/model"
14
spec.license = "MIT"
15
spec.required_ruby_version = ">= 3.1.0"
16
spec.required_rubygems_version = ">= 3.3.11"
@@ -18,11 +18,10 @@ Gem::Specification.new do |spec|
18
spec.metadata["allowed_push_host"] = "https://rubygems.org"
19
20
spec.metadata["homepage_uri"] = spec.homepage
21
- spec.metadata["source_code_uri"] = "https://github.com/smbcloudXYZ/smbcloud-cli/tree/development/gems/model"
22
- spec.metadata["changelog_uri"] = "https://github.com/smbcloudXYZ/smbcloud-cli/tree/development/gems/model/CHANGELOG.md"
21
+ spec.metadata["source_code_uri"] = "https://github.com/smbcloudXYZ/smbcloud-cli/tree/main/sdk/gems/model"
22
+ spec.metadata["changelog_uri"] = "https://github.com/smbcloudXYZ/smbcloud-cli/tree/main/sdk/gems/model/CHANGELOG.md"
23
24
- # Specify which files should be added to the gem when it is released.
25
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ # Build the release package from tracked files in git.
25
gemspec = File.basename(__FILE__)
26
spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
27
ls.readlines("\x0", chomp: true).reject do |f|
@@ -35,10 +34,5 @@ Gem::Specification.new do |spec|
34
spec.require_paths = ["lib"]
35
spec.extensions = ["ext/model/extconf.rb"]
36
38
- # Uncomment to register a new dependency of your gem
39
- # spec.add_dependency "example-gem", "~> 1.0"
37
spec.add_dependency "rb_sys", "~> 0.9.91"
41
-
42
- # For more information and examples about making a new gem, check out our
43
- # guide at: https://bundler.io/guides/creating_gem.html
38
end