From bbdf38018b47328b5faa2cef635c37095045be72 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Fri, 12 Jun 2026 01:54:21 +0200 Subject: feat(server): Really make users stateless (with tests) This commit also remove another load of unneeded features. --- crates/turtle/src/atuin_client/api_client.rs | 60 +++++++--------------------- 1 file changed, 15 insertions(+), 45 deletions(-) (limited to 'crates/turtle/src/atuin_client/api_client.rs') diff --git a/crates/turtle/src/atuin_client/api_client.rs b/crates/turtle/src/atuin_client/api_client.rs index b4657a47..15d96d93 100644 --- a/crates/turtle/src/atuin_client/api_client.rs +++ b/crates/turtle/src/atuin_client/api_client.rs @@ -2,52 +2,33 @@ use std::env; use std::time::Duration; use eyre::{Result, bail, eyre}; -use reqwest::{ - Response, StatusCode, Url, - header::{AUTHORIZATION, HeaderMap}, -}; +use reqwest::{Response, StatusCode, Url, header::HeaderMap}; use tracing::debug; +use uuid::Uuid; +use crate::atuin_common::{api::ErrorResponse, record::RecordStatus}; use crate::atuin_common::{ api::{ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ATUIN_VERSION}, record::{EncryptedData, HostId, Record, RecordIdx}, tls::ensure_crypto_provider, }; -use crate::atuin_common::{ - api::{ErrorResponse, MeResponse}, - record::RecordStatus, -}; use semver::Version; static APP_USER_AGENT: &str = concat!("atuin/", env!("CARGO_PKG_VERSION"),); -/// Authentication token for sync API requests. -/// -/// Used with `Token ` header. -#[derive(Debug, Clone)] -pub(crate) struct AuthToken(pub(crate) String); - -impl AuthToken { - /// Format the token as an Authorization header value - fn to_header_value(&self) -> String { - format!("Token {}", self.0) - } -} - pub(crate) struct Client<'a> { sync_addr: &'a str, client: reqwest::Client, + user_id: Uuid, } -fn make_url(address: &str, path: &str) -> Result { +fn make_url(address: &str, path: &str, user_id: Uuid) -> Result { + let address = address.strip_suffix('/').unwrap_or(address); + // `join()` expects a trailing `/` in order to join paths // e.g. it treats `http://host:port/subdir` as a file called `subdir` - let address = if address.ends_with('/') { - address - } else { - &format!("{address}/") - }; + let address = &format!("{address}/api/v0/{}/", user_id.to_string()); // passing a path with a leading `/` will cause `join()` to replace the entire URL path let path = path.strip_prefix("/").unwrap_or(path); @@ -123,18 +104,18 @@ async fn handle_resp_error(resp: Response) -> Result { impl<'a> Client<'a> { pub(crate) fn new( sync_addr: &'a str, - auth: AuthToken, connect_timeout: u64, timeout: u64, + user_id: Uuid, ) -> Result { ensure_crypto_provider(); let mut headers = HeaderMap::new(); - headers.insert(AUTHORIZATION, auth.to_header_value().parse()?); // used for semver server check headers.insert(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION.parse()?); Ok(Client { + user_id, sync_addr, client: reqwest::Client::builder() .user_agent(APP_USER_AGENT) @@ -145,20 +126,8 @@ impl<'a> Client<'a> { }) } - pub(crate) async fn me(&self) -> Result { - let url = make_url(self.sync_addr, "/api/v0/me")?; - let url = Url::parse(url.as_str())?; - - let resp = self.client.get(url).send().await?; - let resp = handle_resp_error(resp).await?; - - let status = resp.json::().await?; - - Ok(status) - } - pub(crate) async fn delete_store(&self) -> Result<()> { - let url = make_url(self.sync_addr, "/api/v0/store")?; + let url = make_url(self.sync_addr, "/store", self.user_id)?; let url = Url::parse(url.as_str())?; let resp = self.client.delete(url).send().await?; @@ -169,7 +138,7 @@ impl<'a> Client<'a> { } pub(crate) async fn post_records(&self, records: &[Record]) -> Result<()> { - let url = make_url(self.sync_addr, "/api/v0/record")?; + let url = make_url(self.sync_addr, "/record", self.user_id)?; let url = Url::parse(url.as_str())?; debug!("uploading {} records to {url}", records.len()); @@ -192,9 +161,10 @@ impl<'a> Client<'a> { let url = make_url( self.sync_addr, &format!( - "/api/v0/record/next?host={}&tag={}&count={}&start={}", + "/record/next?host={}&tag={}&count={}&start={}", host.0, tag, count, start ), + self.user_id, )?; let url = Url::parse(url.as_str())?; @@ -208,7 +178,7 @@ impl<'a> Client<'a> { } pub(crate) async fn record_status(&self) -> Result { - let url = make_url(self.sync_addr, "/api/v0/record")?; + let url = make_url(self.sync_addr, "/record", self.user_id)?; let url = Url::parse(url.as_str())?; let resp = self.client.get(url).send().await?; -- cgit v1.3.1