From c7e8eafd5f5261a5c94ac362c83e8b1f9e86e6d2 Mon Sep 17 00:00:00 2001 From: Paul Thurlow Date: Wed, 1 Apr 2026 09:22:28 -0700 Subject: [PATCH 1/2] fix auth flow bugs --- src/auth.rs | 28 ++++++++++++++++++++++++++++ src/config.rs | 15 +++++++++++---- src/init.rs | 27 --------------------------- 3 files changed, 39 insertions(+), 31 deletions(-) delete mode 100644 src/init.rs diff --git a/src/auth.rs b/src/auth.rs index 8004272..a85353e 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -83,6 +83,30 @@ pub fn login() { let api_url = profile_config.api_url.to_string(); let app_url = profile_config.app_url.to_string(); + // Check if already authenticated + if let Some(api_key) = &profile_config.api_key { + if api_key != "PLACEHOLDER" { + let client = reqwest::blocking::Client::new(); + if let Ok(resp) = client + .get(format!("{api_url}/workspaces")) + .header("Authorization", format!("Bearer {api_key}")) + .send() + { + if resp.status().is_success() { + println!("{}", "You are already signed in.".green()); + print!("Do you want to log in again? [y/N] "); + use std::io::Write; + std::io::stdout().flush().unwrap(); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + if !input.trim().eq_ignore_ascii_case("y") { + return; + } + } + } + } + } + let code_verifier = generate_code_verifier(); let code_challenge = generate_code_challenge(&code_verifier); let state = generate_random_string(32); @@ -261,6 +285,10 @@ pub fn login() { None => print_row("Workspace", &"None".dark_grey().to_string()), } } + Ok(r) if r.status() == reqwest::StatusCode::FORBIDDEN => { + eprintln!("{}", "You are not authorized to create a new API token.".red()); + std::process::exit(1); + } Ok(r) => { eprintln!("token exchange failed: HTTP {}", r.status()); std::process::exit(1); diff --git a/src/config.rs b/src/config.rs index 650df5e..ebe4da4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -99,6 +99,13 @@ pub struct ConfigFile { pub profiles: HashMap, } +fn write_config(config_path: &std::path::Path, content: &str) -> Result<(), String> { + if let Some(parent) = config_path.parent() { + fs::create_dir_all(parent).map_err(|e| format!("error creating config directory: {e}"))?; + } + fs::write(config_path, content).map_err(|e| format!("error writing config file: {e}")) +} + pub fn save_api_key(profile: &str, api_key: &str) -> Result<(), String> { let user_dirs = UserDirs::new().ok_or("could not determine home directory")?; let config_path = user_dirs.home_dir().join(".hotdata").join("config.yml"); @@ -122,7 +129,7 @@ pub fn save_api_key(profile: &str, api_key: &str) -> Result<(), String> { let content = serde_yaml::to_string(&config_file) .map_err(|e| format!("error serializing config: {e}"))?; - fs::write(&config_path, content).map_err(|e| format!("error writing config file: {e}")) + write_config(&config_path, &content) } pub fn remove_api_key(profile: &str) -> Result<(), String> { @@ -145,7 +152,7 @@ pub fn remove_api_key(profile: &str) -> Result<(), String> { let content = serde_yaml::to_string(&config_file) .map_err(|e| format!("error serializing config: {e}"))?; - fs::write(&config_path, content).map_err(|e| format!("error writing config file: {e}")) + write_config(&config_path, &content) } pub fn save_workspaces(profile: &str, workspaces: Vec) -> Result<(), String> { @@ -171,7 +178,7 @@ pub fn save_workspaces(profile: &str, workspaces: Vec) -> Result let content = serde_yaml::to_string(&config_file) .map_err(|e| format!("error serializing config: {e}"))?; - fs::write(&config_path, content).map_err(|e| format!("error writing config file: {e}")) + write_config(&config_path, &content) } pub fn save_default_workspace(profile: &str, workspace: WorkspaceEntry) -> Result<(), String> { @@ -192,7 +199,7 @@ pub fn save_default_workspace(profile: &str, workspace: WorkspaceEntry) -> Resul let content = serde_yaml::to_string(&config_file) .map_err(|e| format!("error serializing config: {e}"))?; - fs::write(&config_path, content).map_err(|e| format!("error writing config file: {e}")) + write_config(&config_path, &content) } pub fn resolve_workspace_id(provided: Option, profile_config: &ProfileConfig) -> Result { diff --git a/src/init.rs b/src/init.rs deleted file mode 100644 index 68a19a1..0000000 --- a/src/init.rs +++ /dev/null @@ -1,27 +0,0 @@ -use directories::UserDirs; -use std::fs; - -pub fn run() { - let user_dirs = UserDirs::new().expect("could not determine home directory"); - let config_dir = user_dirs.home_dir().join(".hotdata"); - let config_file = config_dir.join("config.yml"); - - if config_file.exists() { - eprintln!("config file already exists at {}", config_file.display()); - std::process::exit(1); - } - - if let Err(e) = fs::create_dir_all(&config_dir) { - eprintln!("error creating config directory: {e}"); - std::process::exit(1); - } - - let content = "profiles:\n default:\n api_key: PLACEHOLDER\n"; - - if let Err(e) = fs::write(&config_file, content) { - eprintln!("error writing config file: {e}"); - std::process::exit(1); - } - - println!("created config file at {}", config_file.display()); -} From d6399767025789a5bae52eedbf658aa792baf4b7 Mon Sep 17 00:00:00 2001 From: Paul Thurlow Date: Wed, 1 Apr 2026 15:28:15 -0700 Subject: [PATCH 2/2] fix upload url command --- src/datasets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datasets.rs b/src/datasets.rs index f866ded..82dd1fe 100644 --- a/src/datasets.rs +++ b/src/datasets.rs @@ -324,7 +324,7 @@ pub fn create_from_url( } }; let api = ApiClient::new(Some(workspace_id)); - create_dataset(&api, label, table_name, json!({ "Url": { "url": url } }), None); + create_dataset(&api, label, table_name, json!({ "url": url }), None); } pub fn create_from_query(