diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-06-10 22:26:10 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-06-10 22:26:10 +0200 |
| commit | 989f01ad230423c5a5105d6c9ff8580020e902ed (patch) | |
| tree | b750d06a9a39dcc76a1da7898b0748b0dad500cf /crates/atuin-client | |
| parent | chore: Remove some unused rust code (diff) | |
| download | atuin-989f01ad230423c5a5105d6c9ff8580020e902ed.zip | |
chore: Remove more useless code
Diffstat (limited to 'crates/atuin-client')
| -rw-r--r-- | crates/atuin-client/Cargo.toml | 3 | ||||
| -rw-r--r-- | crates/atuin-client/src/auth.rs | 269 | ||||
| -rw-r--r-- | crates/atuin-client/src/hub.rs | 304 | ||||
| -rw-r--r-- | crates/atuin-client/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/atuin-client/src/logout.rs | 1 | ||||
| -rw-r--r-- | crates/atuin-client/src/meta.rs | 21 | ||||
| -rw-r--r-- | crates/atuin-client/src/settings.rs | 213 | ||||
| -rw-r--r-- | crates/atuin-client/src/settings/dotfiles.rs | 7 | ||||
| -rw-r--r-- | crates/atuin-client/src/settings/kv.rs | 17 | ||||
| -rw-r--r-- | crates/atuin-client/src/settings/scripts.rs | 17 |
10 files changed, 16 insertions, 838 deletions
diff --git a/crates/atuin-client/Cargo.toml b/crates/atuin-client/Cargo.toml index 1b007f2e..b5ac49a4 100644 --- a/crates/atuin-client/Cargo.toml +++ b/crates/atuin-client/Cargo.toml @@ -13,9 +13,8 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["sync", "hub", "daemon"] +default = ["sync", "daemon"] sync = ["urlencoding", "reqwest", "sha2", "hex"] -hub = ["reqwest"] daemon = [] check-update = [] diff --git a/crates/atuin-client/src/auth.rs b/crates/atuin-client/src/auth.rs index 8ea4b8ab..1031c11f 100644 --- a/crates/atuin-client/src/auth.rs +++ b/crates/atuin-client/src/auth.rs @@ -1,13 +1,9 @@ use async_trait::async_trait; use eyre::{Context, Result, bail}; -use reqwest::{StatusCode, Url, header::USER_AGENT}; -use serde::Deserialize; +use reqwest::{Url, header::USER_AGENT}; use atuin_common::{ - api::{ - ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ChangePasswordRequest, LoginRequest, - LoginResponse, RegisterResponse, - }, + api::{ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ChangePasswordRequest, LoginRequest}, tls::ensure_crypto_provider, }; @@ -74,20 +70,12 @@ pub trait AuthClient: Send + Sync { /// Resolve the appropriate [`AuthClient`] for the current settings. pub async fn auth_client(settings: &Settings) -> Box<dyn AuthClient> { - if settings.is_hub_sync() { - let endpoint = settings.active_hub_endpoint().unwrap_or_default(); - Box::new(HubAuthClient::new( - endpoint.as_ref(), - settings.hub_session_token().await.ok(), - )) as Box<dyn AuthClient> - } else { - Box::new(LegacyAuthClient::new( - &settings.sync_address, - settings.session_token().await.ok(), - settings.network_connect_timeout, - settings.network_timeout, - )) as Box<dyn AuthClient> - } + Box::new(LegacyAuthClient::new( + &settings.sync_address, + settings.session_token().await.ok(), + settings.network_connect_timeout, + settings.network_timeout, + )) as Box<dyn AuthClient> } // --------------------------------------------------------------------------- @@ -221,247 +209,6 @@ impl AuthClient for LegacyAuthClient { } // --------------------------------------------------------------------------- -// Hub backend — talks to the Hub v0 API endpoints -// --------------------------------------------------------------------------- - -pub struct HubAuthClient { - address: String, - hub_token: Option<String>, -} - -impl HubAuthClient { - pub fn new(address: &str, hub_token: Option<String>) -> Self { - Self { - address: address.trim_end_matches('/').to_string(), - hub_token, - } - } -} - -/// Hub v0 error/status response — includes an optional `code` field for -/// machine-readable status like `"2fa_required"`. -#[derive(Debug, Deserialize)] -struct HubErrorResponse { - reason: String, - code: Option<String>, -} - -#[async_trait] -impl AuthClient for HubAuthClient { - async fn login( - &self, - username: &str, - password: &str, - totp_code: Option<&str>, - ) -> Result<AuthResponse> { - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/login")?; - let client = reqwest::Client::new(); - - let mut body = serde_json::json!({ - "username": username, - "password": password, - }); - if let Some(code) = totp_code { - body["totp_code"] = serde_json::Value::String(code.to_string()); - } - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .json(&body) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - let login: LoginResponse = resp.json().await?; - return Ok(AuthResponse::Success { - session: login.session, - auth_type: login.auth, - }); - } - - if status == StatusCode::FORBIDDEN - && let Ok(err) = resp.json::<HubErrorResponse>().await - { - if err.code.as_deref() == Some("2fa_required") { - return Ok(AuthResponse::TwoFactorRequired); - } - bail!("{}", err.reason); - } - - if status == StatusCode::UNAUTHORIZED { - bail!("invalid credentials"); - } - - bail!("Hub login failed with status {status}"); - } - - async fn register(&self, username: &str, email: &str, password: &str) -> Result<AuthResponse> { - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/register")?; - let client = reqwest::Client::new(); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .json(&serde_json::json!({ - "email": email, - "username": username, - "password": password, - })) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - let reg: RegisterResponse = resp.json().await?; - return Ok(AuthResponse::Success { - session: reg.session, - auth_type: reg.auth, - }); - } - - if let Ok(err) = resp.json::<HubErrorResponse>().await { - bail!("{}", err.reason); - } - - bail!("Hub registration failed with status {status}"); - } - - async fn change_password( - &self, - current_password: &str, - new_password: &str, - totp_code: Option<&str>, - ) -> Result<MutateResponse> { - let hub_token = self.hub_token.as_deref().ok_or_else(|| { - eyre::eyre!( - "Not logged in to Atuin Hub. \ - Please run 'atuin login' to authenticate." - ) - })?; - - if !hub_token.starts_with("atapi_") { - bail!( - "Your Hub session token is invalid. \ - Please run 'atuin login' to re-authenticate with Atuin Hub." - ); - } - - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/account/password")?; - let client = reqwest::Client::new(); - - let mut body = serde_json::json!({ - "current_password": current_password, - "new_password": new_password, - }); - if let Some(code) = totp_code { - body["totp_code"] = serde_json::Value::String(code.to_string()); - } - - let resp = client - .patch(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .bearer_auth(hub_token) - .json(&body) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - return Ok(MutateResponse::Success); - } - - if let Ok(err) = resp.json::<HubErrorResponse>().await { - match err.code.as_deref() { - Some("2fa_required") => return Ok(MutateResponse::TwoFactorRequired), - Some("invalid_2fa_code") => bail!("invalid two-factor code"), - _ => bail!("{}", err.reason), - } - } - - match status { - StatusCode::UNAUTHORIZED => bail!("current password is incorrect"), - StatusCode::FORBIDDEN => bail!("invalid login details"), - _ => bail!("Hub password change failed with status {status}"), - } - } - - async fn delete_account( - &self, - password: &str, - totp_code: Option<&str>, - ) -> Result<MutateResponse> { - let hub_token = self.hub_token.as_deref().ok_or_else(|| { - eyre::eyre!( - "Not logged in to Atuin Hub. \ - Please run 'atuin login' to authenticate." - ) - })?; - - if !hub_token.starts_with("atapi_") { - bail!( - "Your Hub session token is invalid. \ - Please run 'atuin login' to re-authenticate with Atuin Hub." - ); - } - - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/account")?; - let client = reqwest::Client::new(); - - let mut body = serde_json::json!({ - "password": password, - }); - if let Some(code) = totp_code { - body["totp_code"] = serde_json::Value::String(code.to_string()); - } - - let resp = client - .delete(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .bearer_auth(hub_token) - .json(&body) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - return Ok(MutateResponse::Success); - } - - if let Ok(err) = resp.json::<HubErrorResponse>().await { - match err.code.as_deref() { - Some("2fa_required") => return Ok(MutateResponse::TwoFactorRequired), - Some("invalid_2fa_code") => bail!("invalid two-factor code"), - _ => bail!("{}", err.reason), - } - } - - match status { - StatusCode::UNAUTHORIZED => bail!("password is incorrect"), - StatusCode::FORBIDDEN => bail!("invalid login details"), - _ => bail!("Hub account deletion failed with status {status}"), - } - } -} - -// --------------------------------------------------------------------------- // Shared helpers // --------------------------------------------------------------------------- diff --git a/crates/atuin-client/src/hub.rs b/crates/atuin-client/src/hub.rs deleted file mode 100644 index 2e40aad4..00000000 --- a/crates/atuin-client/src/hub.rs +++ /dev/null @@ -1,304 +0,0 @@ -//! Hub authentication support for Atuin -//! -//! This module provides programmatic access to the Atuin Hub authentication flow. -//! It can be used by other crates (like atuin-ai) to authenticate with the Hub -//! and obtain session tokens. -//! -//! Hub authentication is separate from sync authentication - users can have both -//! a sync session (for history sync) and a hub session (for Hub-specific features -//! like AI). - -use std::time::Duration; - -use eyre::{Context, Result, bail}; -use reqwest::{StatusCode, Url, header::USER_AGENT}; - -use atuin_common::{ - api::{ - ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, CliCodeResponse, CliVerifyResponse, - ErrorResponse, - }, - tls::ensure_crypto_provider, -}; - -use crate::settings::Settings; - -static APP_USER_AGENT: &str = concat!("atuin/", env!("CARGO_PKG_VERSION")); - -/// The result of starting a hub authentication flow -#[derive(Debug, Clone)] -pub struct HubAuthSession { - /// The code to be verified - pub code: String, - /// The URL the user should visit to authenticate - pub auth_url: String, - /// The hub address being used - pub hub_address: String, -} - -/// The result of polling for hub auth completion -#[derive(Debug, Clone)] -pub enum HubAuthStatus { - /// Still waiting for user authorization - Pending, - /// Authorization complete, contains the session token - Complete(String), - /// Authorization failed with an error - Failed(String), -} - -/// Default poll interval for checking auth status -pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(2); - -/// Default timeout for the entire auth flow -pub const DEFAULT_AUTH_TIMEOUT: Duration = Duration::from_secs(600); - -impl HubAuthSession { - /// Start a new hub authentication session - /// - /// Returns a session containing the code and auth URL that the user should visit. - pub async fn start(hub_address: &str) -> Result<Self> { - debug!("Starting Hub authentication process..."); - - let hub_address = hub_address.trim_end_matches('/'); - let code_response = request_code(hub_address) - .await - .context("Failed to request authentication code from Hub")?; - - debug!("Received code from Hub"); - - let code = code_response.code; - let auth_url = format!("{}/auth/cli?code={}", hub_address, code); - - Ok(Self { - code, - auth_url, - hub_address: hub_address.to_string(), - }) - } - - /// Poll for the authentication status - /// - /// Returns the current status of the authentication flow. - pub async fn poll(&self) -> Result<HubAuthStatus> { - match verify_code(&self.hub_address, &self.code).await { - Ok(response) => { - if let Some(token) = response.token { - debug!("Authentication complete, received token"); - Ok(HubAuthStatus::Complete(token)) - } else if let Some(error) = response.error { - error!("Authentication failed: {}", error); - Ok(HubAuthStatus::Failed(error)) - } else { - Ok(HubAuthStatus::Pending) - } - } - Err(e) => { - // Transient errors shouldn't fail the whole flow - log::debug!("Verification poll failed: {}", e); - Ok(HubAuthStatus::Pending) - } - } - } - - /// Poll until completion or timeout - /// - /// This is a convenience method that polls repeatedly until the auth completes - /// or times out. - pub async fn wait_for_completion( - &self, - timeout: Duration, - poll_interval: Duration, - ) -> Result<String> { - let start = std::time::Instant::now(); - - debug!("Polling for Hub authentication completion..."); - - loop { - if start.elapsed() > timeout { - warn!("Authentication loop exited due to timeout"); - bail!("Authentication timed out. Please try again."); - } - - match self.poll().await? { - HubAuthStatus::Complete(token) => return Ok(token), - HubAuthStatus::Failed(error) => bail!("Authentication failed: {}", error), - HubAuthStatus::Pending => { - tokio::time::sleep(poll_interval).await; - } - } - } - } -} - -/// Save a hub session token -/// -/// This saves the token to the meta store so it can be used for subsequent Hub API calls. -/// Note: This is separate from the sync session token. -pub async fn save_session(token: &str) -> Result<()> { - Settings::meta_store() - .await? - .save_hub_session(token) - .await - .context("Failed to save hub session") -} - -/// Delete the hub session token (logout from Hub) -pub async fn delete_session() -> Result<()> { - Settings::meta_store() - .await? - .delete_hub_session() - .await - .context("Failed to delete hub session") -} - -/// Check if the user is logged in with Hub authentication -/// -/// Returns true if the user has a valid Hub session token. -/// This is independent of whether they have a sync session. -pub async fn is_logged_in() -> Result<bool> { - Settings::meta_store().await?.hub_logged_in().await -} - -/// Get the hub session token if available -/// -/// Returns the Hub session token if the user is logged in with Hub auth, -/// or None if not logged in. -pub async fn get_session_token() -> Result<Option<String>> { - Settings::meta_store().await?.hub_session_token().await -} - -/// Link an existing CLI sync account to the current Hub user. -/// -/// This associates the CLI's sync records with the Hub account, enabling -/// unified authentication. After linking: -/// - The Hub token can be used for sync operations -/// - Records are migrated to be accessible via Hub auth -/// -/// Requires: -/// - A valid Hub session (user must be logged in to Hub) -/// - A valid CLI session token to link -/// -/// Returns Ok(()) on success, or an error if: -/// - Not logged in to Hub -/// - CLI token is invalid -/// - CLI account is already linked to a different Hub account -pub async fn link_account(hub_address: &str, cli_token: &str) -> Result<()> { - let hub_token = get_session_token() - .await? - .ok_or_else(|| eyre::eyre!("Not logged in to Hub - cannot link account"))?; - - let url = make_url(hub_address, "/api/v0/account/link")?; - - debug!("Linking CLI account to Hub at {}", hub_address); - - ensure_crypto_provider(); - let client = reqwest::Client::new(); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .bearer_auth(&hub_token) - .json(&serde_json::json!({ "token": cli_token })) - .send() - .await?; - - let status = resp.status(); - - if status == StatusCode::CONFLICT { - // 409 means CLI account is already linked to a (possibly different) Hub account - debug!("CLI account already linked to a Hub account"); - return Ok(()); - } - - handle_resp_error(resp).await?; - - info!("Successfully linked CLI account to Hub"); - Ok(()) -} - -// --- Internal HTTP functions --- - -fn make_url(address: &str, path: &str) -> Result<String> { - let address = if address.ends_with('/') { - address.to_string() - } else { - format!("{address}/") - }; - - let path = path.strip_prefix('/').unwrap_or(path); - - let url = Url::parse(&address) - .context("failed to parse hub address")? - .join(path) - .context("failed to join hub URL path")?; - - Ok(url.to_string()) -} - -async fn handle_resp_error(resp: reqwest::Response) -> Result<reqwest::Response> { - let status = resp.status(); - - if status == StatusCode::SERVICE_UNAVAILABLE { - error!("Service unavailable: check https://status.atuin.sh"); - bail!("Service unavailable: check https://status.atuin.sh"); - } - - if status == StatusCode::TOO_MANY_REQUESTS { - error!("Rate limited; please wait before trying again"); - bail!("Rate limited; please wait before trying again"); - } - - if !status.is_success() { - if let Ok(error) = resp.json::<ErrorResponse>().await { - error!("Hub error: {} - {}", status, error.reason); - bail!("Hub error: {} - {}", status, error.reason); - } - error!("Hub request failed with status: {}", status); - bail!("Hub request failed with status: {}", status); - } - - Ok(resp) -} - -/// Request a CLI auth code from the Atuin Hub -async fn request_code(address: &str) -> Result<CliCodeResponse> { - ensure_crypto_provider(); - let url = make_url(address, "/auth/cli/code")?; - let client = reqwest::Client::new(); - - debug!("Requesting code from Hub at {url}"); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .send() - .await?; - let resp = handle_resp_error(resp).await?; - - let code_response = resp.json::<CliCodeResponse>().await?; - Ok(code_response) -} - -/// Poll to verify the CLI auth code and get the session token -async fn verify_code(address: &str, code: &str) -> Result<CliVerifyResponse> { - ensure_crypto_provider(); - let base = make_url(address, "/auth/cli/verify")?; - let url = format!("{base}?code={code}"); - let client = reqwest::Client::new(); - - debug!("Verifying code with Hub at {base}?code=******"); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .send() - .await?; - let resp = handle_resp_error(resp).await?; - - let verify_response = resp.json::<CliVerifyResponse>().await?; - Ok(verify_response) -} diff --git a/crates/atuin-client/src/lib.rs b/crates/atuin-client/src/lib.rs index 7938176a..cd7785e1 100644 --- a/crates/atuin-client/src/lib.rs +++ b/crates/atuin-client/src/lib.rs @@ -7,8 +7,6 @@ extern crate log; pub mod api_client; #[cfg(feature = "sync")] pub mod auth; -#[cfg(feature = "hub")] -pub mod hub; #[cfg(feature = "sync")] pub mod login; #[cfg(feature = "sync")] diff --git a/crates/atuin-client/src/logout.rs b/crates/atuin-client/src/logout.rs index 80f0ad73..f720b302 100644 --- a/crates/atuin-client/src/logout.rs +++ b/crates/atuin-client/src/logout.rs @@ -7,7 +7,6 @@ pub async fn logout() -> Result<()> { if meta.logged_in().await? { meta.delete_session().await?; - meta.delete_hub_session().await?; println!("You have logged out!"); } else { println!("You are not logged in"); diff --git a/crates/atuin-client/src/meta.rs b/crates/atuin-client/src/meta.rs index eb6dd8cf..870f36d0 100644 --- a/crates/atuin-client/src/meta.rs +++ b/crates/atuin-client/src/meta.rs @@ -21,7 +21,6 @@ const KEY_LAST_SYNC: &str = "last_sync_time"; const KEY_LAST_VERSION_CHECK: &str = "last_version_check_time"; const KEY_LATEST_VERSION: &str = "latest_version"; const KEY_SESSION: &str = "session"; -const KEY_HUB_SESSION: &str = "hub_session"; const KEY_FILES_MIGRATED: &str = "files_migrated"; pub struct MetaStore { @@ -187,25 +186,7 @@ impl MetaStore { } pub async fn logged_in(&self) -> Result<bool> { - Ok(self.session_token().await?.is_some() || self.hub_session_token().await?.is_some()) - } - - // Hub session methods (separate from sync session, used for Hub-specific features like AI) - - pub async fn hub_session_token(&self) -> Result<Option<String>> { - self.get(KEY_HUB_SESSION).await - } - - pub async fn save_hub_session(&self, token: &str) -> Result<()> { - self.set(KEY_HUB_SESSION, token).await - } - - pub async fn delete_hub_session(&self) -> Result<()> { - self.delete(KEY_HUB_SESSION).await - } - - pub async fn hub_logged_in(&self) -> Result<bool> { - Ok(self.hub_session_token().await?.is_some()) + Ok(self.session_token().await?.is_some()) } // File migration: on first open, migrate old plain-text files into the database. diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index 1be6f363..ce84c053 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -11,7 +11,6 @@ use eyre::{Context, Error, Result, bail, eyre}; use fs_err::{File, create_dir_all}; use humantime::parse_duration; use regex::RegexSet; -use semver::Version; use serde::{Deserialize, Serialize}; use serde_with::DeserializeFromStr; use time::{OffsetDateTime, UtcOffset, format_description::FormatItem, macros::format_description}; @@ -23,32 +22,12 @@ static DATA_DIR: OnceLock<PathBuf> = OnceLock::new(); static META_CONFIG: OnceLock<(String, f64)> = OnceLock::new(); static META_STORE: OnceCell<crate::meta::MetaStore> = OnceCell::const_new(); -mod dotfiles; -mod kv; pub(crate) mod meta; -mod scripts; pub mod watcher; -pub struct HubEndpoint(String); - /// Default sync address for Atuin's hosted service pub const DEFAULT_SYNC_ADDRESS: &str = "https://api.atuin.sh"; -/// Default Hub web/API endpoint for Atuin's hosted service -pub const DEFAULT_HUB_ENDPOINT: &str = "https://hub.atuin.sh"; - -impl Default for HubEndpoint { - fn default() -> Self { - HubEndpoint(DEFAULT_HUB_ENDPOINT.to_string()) - } -} - -impl AsRef<str> for HubEndpoint { - fn as_ref(&self) -> &str { - &self.0 - } -} - #[derive(Clone, Debug, Deserialize, Copy, ValueEnum, PartialEq, Serialize)] pub enum SearchMode { #[serde(rename = "prefix")] @@ -374,13 +353,9 @@ pub struct Sync { #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, Default)] #[serde(rename_all = "lowercase")] pub enum SyncProtocol { - /// Use Hub authentication (Bearer token from Hub OAuth flow) - Hub, /// Use legacy CLI authentication (Token from CLI register/login) - Legacy, - /// Infer from sync_address (default behavior) #[default] - Auto, + Legacy, } /// Resolved authentication state for sync operations. @@ -394,14 +369,7 @@ pub enum SyncAuth { /// Self-hosted Rust server. Uses `Authorization: Token <session>` and /// legacy endpoints. Legacy { token: String }, - /// Hub with a valid Hub API token (`atapi_*`). Uses - /// `Authorization: Bearer <token>` and v0 endpoints. - Hub { token: String }, - /// Targeting Hub but only has a CLI session token. Uses - /// `Authorization: Token <session>` against compat/record endpoints. - /// Sync, password change, and account deletion still work, but the user - /// should be nudged to run `atuin login` for full Hub auth. - HubViaCli { token: String }, + /// Not authenticated at all. Contains an actionable user-facing message. NotLoggedIn { reason: String }, } @@ -415,8 +383,6 @@ impl SyncAuth { use crate::api_client::AuthToken; match self { SyncAuth::Legacy { token } => Ok(AuthToken::Token(token)), - SyncAuth::Hub { token } => Ok(AuthToken::Bearer(token)), - SyncAuth::HubViaCli { token } => Ok(AuthToken::Token(token)), SyncAuth::NotLoggedIn { reason } => Err(eyre!(reason)), } } @@ -1082,9 +1048,6 @@ pub struct Settings { /// The sync address for atuin. pub sync_address: String, - /// Sync protocol for authentication. When set to "auto" (default), the protocol - /// is inferred from sync_address. Set to "hub" to force Hub auth with a custom - /// sync_address (useful for local development). #[serde(default)] pub sync_protocol: SyncProtocol, @@ -1152,9 +1115,6 @@ pub struct Settings { pub preview: Preview, #[serde(default)] - pub dotfiles: dotfiles::Settings, - - #[serde(default)] pub daemon: Daemon, #[serde(default)] @@ -1167,12 +1127,6 @@ pub struct Settings { pub ui: Ui, #[serde(default)] - pub scripts: scripts::Settings, - - #[serde(default)] - pub kv: kv::Settings, - - #[serde(default)] pub tmux: Tmux, #[serde(default)] @@ -1180,9 +1134,6 @@ pub struct Settings { #[serde(default)] pub meta: meta::Settings, - - #[serde(default)] - pub ai: Ai, } impl Settings { @@ -1266,56 +1217,6 @@ impl Settings { } } - pub async fn hub_session_token(&self) -> Result<String> { - match Self::meta_store().await?.hub_session_token().await? { - Some(token) => Ok(token), - None => Err(eyre!("Tried to load hub session; not logged in")), - } - } - - /// Normalize a URL for comparison by trimming trailing slashes - fn normalize_url(url: &str) -> &str { - url.trim_end_matches('/') - } - - /// Check if a URL matches one of Atuin's official hosted addresses - fn is_official_address(url: &str) -> bool { - let normalized = Self::normalize_url(url); - normalized == Self::normalize_url(DEFAULT_SYNC_ADDRESS) - || normalized == Self::normalize_url(DEFAULT_HUB_ENDPOINT) - } - - /// Returns whether this configuration uses Hub-style sync. - /// - /// Hub sync uses Bearer token authentication and is the default for - /// Atuin's hosted service. This returns true when: - /// - `sync_protocol` is explicitly set to `Hub`, OR - /// - `sync_protocol` is `Auto` and `sync_address` is an official Atuin address - pub fn is_hub_sync(&self) -> bool { - match self.sync_protocol { - SyncProtocol::Hub => true, - SyncProtocol::Legacy => false, - SyncProtocol::Auto => Self::is_official_address(&self.sync_address), - } - } - - /// Returns the base URL for the Hub endpoint. - /// - /// For Atuin's official hosted service, this always returns `https://hub.atuin.sh` - /// regardless of whether `sync_address` is `api.atuin.sh` or `hub.atuin.sh`. - /// For self-hosted instances, returns the configured `sync_address`. - pub fn active_hub_endpoint(&self) -> Option<HubEndpoint> { - if self.is_hub_sync() { - if Self::is_official_address(&self.sync_address) { - Some(HubEndpoint::default()) - } else { - Some(HubEndpoint(self.sync_address.clone())) - } - } else { - None - } - } - /// Examines the configured sync target and available tokens to determine /// the correct auth strategy. Also performs cleanup of mis-stored tokens /// (e.g. a CLI token incorrectly saved in the Hub session slot). @@ -1330,45 +1231,12 @@ impl Settings { } }; - if !self.is_hub_sync() { - // Self-hosted / legacy server - return match meta.session_token().await { - Ok(Some(token)) => SyncAuth::Legacy { token }, - _ => SyncAuth::NotLoggedIn { - reason: "Not logged in. Run 'atuin login' to authenticate \ - with your sync server." - .into(), - }, - }; - } - - // Targeting Hub — check for a valid Hub API token first - if let Ok(Some(hub_token)) = meta.hub_session_token().await { - if hub_token.starts_with("atapi_") { - return SyncAuth::Hub { token: hub_token }; - } - - // A non-atapi_ token in the hub_session slot is a mis-stored CLI - // token (from the migration-fallback bug). Move it to the CLI - // session slot if that slot is empty, then clear hub_session - // only if the move succeeded. - if let Ok(None) = meta.session_token().await { - if meta.save_session(&hub_token).await.is_ok() { - let _ = meta.delete_hub_session().await; - } - } else { - // CLI slot already has a token; just clear the bad hub_session - let _ = meta.delete_hub_session().await; - } - // Fall through to check CLI token below - } - - // No valid Hub token — check for a CLI session token + // Self-hosted / legacy server match meta.session_token().await { - Ok(Some(token)) => SyncAuth::HubViaCli { token }, + Ok(Some(token)) => SyncAuth::Legacy { token }, _ => SyncAuth::NotLoggedIn { - reason: "Not logged in. Run 'atuin login' or 'atuin register' \ - to authenticate." + reason: "Not logged in. Run 'atuin login' to authenticate \ + with your sync server." .into(), }, } @@ -1384,70 +1252,6 @@ impl Settings { self.resolve_sync_auth().await.into_auth_token() } - #[cfg(feature = "check-update")] - async fn needs_update_check(&self) -> Result<bool> { - let last_check = Settings::last_version_check().await?; - let diff = OffsetDateTime::now_utc() - last_check; - - // Check a max of once per hour - Ok(diff.whole_hours() >= 1) - } - - #[cfg(feature = "check-update")] - async fn latest_version(&self) -> Result<Version> { - // Default to the current version, and if that doesn't parse, a version so high it's unlikely to ever - // suggest upgrading. - let current = - Version::parse(env!("CARGO_PKG_VERSION")).unwrap_or(Version::new(100000, 0, 0)); - - if !self.needs_update_check().await? { - let meta = Self::meta_store().await?; - let version = match meta.latest_version().await? { - Some(v) => Version::parse(&v).unwrap_or(current), - None => current, - }; - - return Ok(version); - } - - #[cfg(feature = "sync")] - let latest = crate::api_client::latest_version().await.unwrap_or(current); - - #[cfg(not(feature = "sync"))] - let latest = current; - - let meta = Self::meta_store().await?; - Settings::save_version_check_time().await?; - meta.save_latest_version(&latest.to_string()).await?; - - Ok(latest) - } - - // Return Some(latest version) if an update is needed. Otherwise, none. - #[cfg(feature = "check-update")] - pub async fn needs_update(&self) -> Option<Version> { - if !self.update_check { - return None; - } - - let current = - Version::parse(env!("CARGO_PKG_VERSION")).unwrap_or(Version::new(100000, 0, 0)); - - let latest = self.latest_version().await; - - if latest.is_err() { - return None; - } - - let latest = latest.unwrap(); - - if latest > current { - return Some(latest); - } - - None - } - pub fn default_filter_mode(&self, git_root: bool) -> FilterMode { self.filter_mode .filter(|x| self.search.filters.contains(x)) @@ -1465,11 +1269,6 @@ impl Settings { .unwrap_or(FilterMode::Global) } - #[cfg(not(feature = "check-update"))] - pub async fn needs_update(&self) -> Option<Version> { - None - } - pub fn builder() -> Result<ConfigBuilder<DefaultState>> { Self::builder_with_data_dir(&atuin_common::utils::data_dir()) } diff --git a/crates/atuin-client/src/settings/dotfiles.rs b/crates/atuin-client/src/settings/dotfiles.rs deleted file mode 100644 index bbaf914f..00000000 --- a/crates/atuin-client/src/settings/dotfiles.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone, Default)] -pub struct Settings { - #[serde(alias = "enable")] - pub enabled: bool, -} diff --git a/crates/atuin-client/src/settings/kv.rs b/crates/atuin-client/src/settings/kv.rs deleted file mode 100644 index afc24a35..00000000 --- a/crates/atuin-client/src/settings/kv.rs +++ /dev/null @@ -1,17 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Settings { - pub db_path: String, -} - -impl Default for Settings { - fn default() -> Self { - let dir = atuin_common::utils::data_dir(); - let path = dir.join("kv.db"); - - Self { - db_path: path.to_string_lossy().to_string(), - } - } -} diff --git a/crates/atuin-client/src/settings/scripts.rs b/crates/atuin-client/src/settings/scripts.rs deleted file mode 100644 index a1a5ed6a..00000000 --- a/crates/atuin-client/src/settings/scripts.rs +++ /dev/null @@ -1,17 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Settings { - pub db_path: String, -} - -impl Default for Settings { - fn default() -> Self { - let dir = atuin_common::utils::data_dir(); - let path = dir.join("scripts.db"); - - Self { - db_path: path.to_string_lossy().to_string(), - } - } -} |
