diff options
Diffstat (limited to 'atuin-server/src/handlers')
| -rw-r--r-- | atuin-server/src/handlers/history.rs | 237 | ||||
| -rw-r--r-- | atuin-server/src/handlers/mod.rs | 58 | ||||
| -rw-r--r-- | atuin-server/src/handlers/record.rs | 45 | ||||
| -rw-r--r-- | atuin-server/src/handlers/status.rs | 43 | ||||
| -rw-r--r-- | atuin-server/src/handlers/user.rs | 258 | ||||
| -rw-r--r-- | atuin-server/src/handlers/v0/me.rs | 16 | ||||
| -rw-r--r-- | atuin-server/src/handlers/v0/mod.rs | 3 | ||||
| -rw-r--r-- | atuin-server/src/handlers/v0/record.rs | 112 | ||||
| -rw-r--r-- | atuin-server/src/handlers/v0/store.rs | 37 |
9 files changed, 0 insertions, 809 deletions
diff --git a/atuin-server/src/handlers/history.rs b/atuin-server/src/handlers/history.rs deleted file mode 100644 index 05bbe740..00000000 --- a/atuin-server/src/handlers/history.rs +++ /dev/null @@ -1,237 +0,0 @@ -use std::{collections::HashMap, convert::TryFrom}; - -use axum::{ - extract::{Path, Query, State}, - http::{HeaderMap, StatusCode}, - Json, -}; -use metrics::counter; -use time::{Month, UtcOffset}; -use tracing::{debug, error, instrument}; - -use super::{ErrorResponse, ErrorResponseStatus, RespExt}; -use crate::{ - router::{AppState, UserAuth}, - utils::client_version_min, -}; -use atuin_server_database::{ - calendar::{TimePeriod, TimePeriodInfo}, - models::NewHistory, - Database, -}; - -use atuin_common::api::*; - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn count<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<Json<CountResponse>, ErrorResponseStatus<'static>> { - let db = &state.0.database; - match db.count_history_cached(&user).await { - // By default read out the cached value - Ok(count) => Ok(Json(CountResponse { count })), - - // If that fails, fallback on a full COUNT. Cache is built on a POST - // only - Err(_) => match db.count_history(&user).await { - Ok(count) => Ok(Json(CountResponse { count })), - Err(_) => Err(ErrorResponse::reply("failed to query history count") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)), - }, - } -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn list<DB: Database>( - req: Query<SyncHistoryRequest>, - UserAuth(user): UserAuth, - headers: HeaderMap, - state: State<AppState<DB>>, -) -> Result<Json<SyncHistoryResponse>, ErrorResponseStatus<'static>> { - let db = &state.0.database; - - let agent = headers - .get("user-agent") - .map_or("", |v| v.to_str().unwrap_or("")); - - let variable_page_size = client_version_min(agent, ">=15.0.0").unwrap_or(false); - - let page_size = if variable_page_size { - state.settings.page_size - } else { - 100 - }; - - if req.sync_ts.unix_timestamp_nanos() < 0 || req.history_ts.unix_timestamp_nanos() < 0 { - error!("client asked for history from < epoch 0"); - counter!("atuin_history_epoch_before_zero", 1); - - return Err( - ErrorResponse::reply("asked for history from before epoch 0") - .with_status(StatusCode::BAD_REQUEST), - ); - } - - let history = db - .list_history(&user, req.sync_ts, req.history_ts, &req.host, page_size) - .await; - - if let Err(e) = history { - error!("failed to load history: {}", e); - return Err(ErrorResponse::reply("failed to load history") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - - let history: Vec<String> = history - .unwrap() - .iter() - .map(|i| i.data.to_string()) - .collect(); - - debug!( - "loaded {} items of history for user {}", - history.len(), - user.id - ); - - counter!("atuin_history_returned", history.len() as u64); - - Ok(Json(SyncHistoryResponse { history })) -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn delete<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, - Json(req): Json<DeleteHistoryRequest>, -) -> Result<Json<MessageResponse>, ErrorResponseStatus<'static>> { - let db = &state.0.database; - - // user_id is the ID of the history, as set by the user (the server has its own ID) - let deleted = db.delete_history(&user, req.client_id).await; - - if let Err(e) = deleted { - error!("failed to delete history: {}", e); - return Err(ErrorResponse::reply("failed to delete history") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - - Ok(Json(MessageResponse { - message: String::from("deleted OK"), - })) -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn add<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, - Json(req): Json<Vec<AddHistoryRequest>>, -) -> Result<(), ErrorResponseStatus<'static>> { - let State(AppState { database, settings }) = state; - - debug!("request to add {} history items", req.len()); - counter!("atuin_history_uploaded", req.len() as u64); - - let mut history: Vec<NewHistory> = req - .into_iter() - .map(|h| NewHistory { - client_id: h.id, - user_id: user.id, - hostname: h.hostname, - timestamp: h.timestamp, - data: h.data, - }) - .collect(); - - history.retain(|h| { - // keep if within limit, or limit is 0 (unlimited) - let keep = h.data.len() <= settings.max_history_length || settings.max_history_length == 0; - - // Don't return an error here. We want to insert as much of the - // history list as we can, so log the error and continue going. - if !keep { - counter!("atuin_history_too_long", 1); - - tracing::warn!( - "history too long, got length {}, max {}", - h.data.len(), - settings.max_history_length - ); - } - - keep - }); - - if let Err(e) = database.add_history(&history).await { - error!("failed to add history: {}", e); - - return Err(ErrorResponse::reply("failed to add history") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - }; - - Ok(()) -} - -#[derive(serde::Deserialize, Debug)] -pub struct CalendarQuery { - #[serde(default = "serde_calendar::zero")] - year: i32, - #[serde(default = "serde_calendar::one")] - month: u8, - - #[serde(default = "serde_calendar::utc")] - tz: UtcOffset, -} - -mod serde_calendar { - use time::UtcOffset; - - pub fn zero() -> i32 { - 0 - } - - pub fn one() -> u8 { - 1 - } - - pub fn utc() -> UtcOffset { - UtcOffset::UTC - } -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn calendar<DB: Database>( - Path(focus): Path<String>, - Query(params): Query<CalendarQuery>, - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<Json<HashMap<u64, TimePeriodInfo>>, ErrorResponseStatus<'static>> { - let focus = focus.as_str(); - - let year = params.year; - let month = Month::try_from(params.month).map_err(|e| ErrorResponseStatus { - error: ErrorResponse { - reason: e.to_string().into(), - }, - status: StatusCode::BAD_REQUEST, - })?; - - let period = match focus { - "year" => TimePeriod::Year, - "month" => TimePeriod::Month { year }, - "day" => TimePeriod::Day { year, month }, - _ => { - return Err(ErrorResponse::reply("invalid focus: use year/month/day") - .with_status(StatusCode::BAD_REQUEST)) - } - }; - - let db = &state.0.database; - let focus = db.calendar(&user, period, params.tz).await.map_err(|_| { - ErrorResponse::reply("failed to query calendar") - .with_status(StatusCode::INTERNAL_SERVER_ERROR) - })?; - - Ok(Json(focus)) -} diff --git a/atuin-server/src/handlers/mod.rs b/atuin-server/src/handlers/mod.rs deleted file mode 100644 index 50f82336..00000000 --- a/atuin-server/src/handlers/mod.rs +++ /dev/null @@ -1,58 +0,0 @@ -use atuin_common::api::{ErrorResponse, IndexResponse}; -use atuin_server_database::Database; -use axum::{extract::State, http, response::IntoResponse, Json}; - -use crate::router::AppState; - -pub mod history; -pub mod record; -pub mod status; -pub mod user; -pub mod v0; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub async fn index<DB: Database>(state: State<AppState<DB>>) -> Json<IndexResponse> { - let homage = r#""Through the fathomless deeps of space swims the star turtle Great A'Tuin, bearing on its back the four giant elephants who carry on their shoulders the mass of the Discworld." -- Sir Terry Pratchett"#; - - // Error with a -1 response - // It's super unlikely this will happen - let count = state.database.total_history().await.unwrap_or(-1); - - Json(IndexResponse { - homage: homage.to_string(), - version: VERSION.to_string(), - total_history: count, - }) -} - -impl<'a> IntoResponse for ErrorResponseStatus<'a> { - fn into_response(self) -> axum::response::Response { - (self.status, Json(self.error)).into_response() - } -} - -pub struct ErrorResponseStatus<'a> { - pub error: ErrorResponse<'a>, - pub status: http::StatusCode, -} - -pub trait RespExt<'a> { - fn with_status(self, status: http::StatusCode) -> ErrorResponseStatus<'a>; - fn reply(reason: &'a str) -> Self; -} - -impl<'a> RespExt<'a> for ErrorResponse<'a> { - fn with_status(self, status: http::StatusCode) -> ErrorResponseStatus<'a> { - ErrorResponseStatus { - error: self, - status, - } - } - - fn reply(reason: &'a str) -> ErrorResponse { - Self { - reason: reason.into(), - } - } -} diff --git a/atuin-server/src/handlers/record.rs b/atuin-server/src/handlers/record.rs deleted file mode 100644 index bf454949..00000000 --- a/atuin-server/src/handlers/record.rs +++ /dev/null @@ -1,45 +0,0 @@ -use axum::{http::StatusCode, response::IntoResponse, Json}; -use serde_json::json; -use tracing::instrument; - -use super::{ErrorResponse, ErrorResponseStatus, RespExt}; -use crate::router::UserAuth; -use atuin_server_database::Database; - -use atuin_common::record::{EncryptedData, Record}; - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn post<DB: Database>( - UserAuth(user): UserAuth, -) -> Result<(), ErrorResponseStatus<'static>> { - // anyone who has actually used the old record store (a very small number) will see this error - // upon trying to sync. - // 1. The status endpoint will say that the server has nothing - // 2. The client will try to upload local records - // 3. Sync will fail with this error - - // If the client has no local records, they will see the empty index and do nothing. For the - // vast majority of users, this is the case. - return Err( - ErrorResponse::reply("record store deprecated; please upgrade") - .with_status(StatusCode::BAD_REQUEST), - ); -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn index<DB: Database>(UserAuth(user): UserAuth) -> axum::response::Response { - let ret = json!({ - "hosts": {} - }); - - ret.to_string().into_response() -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn next( - UserAuth(user): UserAuth, -) -> Result<Json<Vec<Record<EncryptedData>>>, ErrorResponseStatus<'static>> { - let records = Vec::new(); - - Ok(Json(records)) -} diff --git a/atuin-server/src/handlers/status.rs b/atuin-server/src/handlers/status.rs deleted file mode 100644 index 3c22232c..00000000 --- a/atuin-server/src/handlers/status.rs +++ /dev/null @@ -1,43 +0,0 @@ -use axum::{extract::State, http::StatusCode, Json}; -use tracing::instrument; - -use super::{ErrorResponse, ErrorResponseStatus, RespExt}; -use crate::router::{AppState, UserAuth}; -use atuin_server_database::Database; - -use atuin_common::api::*; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn status<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<Json<StatusResponse>, ErrorResponseStatus<'static>> { - let db = &state.0.database; - - let deleted = db.deleted_history(&user).await.unwrap_or(vec![]); - - let count = match db.count_history_cached(&user).await { - // By default read out the cached value - Ok(count) => count, - - // If that fails, fallback on a full COUNT. Cache is built on a POST - // only - Err(_) => match db.count_history(&user).await { - Ok(count) => count, - Err(_) => { - return Err(ErrorResponse::reply("failed to query history count") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)) - } - }, - }; - - Ok(Json(StatusResponse { - count, - deleted, - username: user.username, - version: VERSION.to_string(), - page_size: state.settings.page_size, - })) -} diff --git a/atuin-server/src/handlers/user.rs b/atuin-server/src/handlers/user.rs deleted file mode 100644 index e5651fe2..00000000 --- a/atuin-server/src/handlers/user.rs +++ /dev/null @@ -1,258 +0,0 @@ -use std::borrow::Borrow; -use std::collections::HashMap; -use std::time::Duration; - -use argon2::{ - password_hash::SaltString, Algorithm, Argon2, Params, PasswordHash, PasswordHasher, - PasswordVerifier, Version, -}; -use axum::{ - extract::{Path, State}, - http::StatusCode, - Json, -}; -use metrics::counter; -use rand::rngs::OsRng; -use tracing::{debug, error, info, instrument}; -use uuid::Uuid; - -use super::{ErrorResponse, ErrorResponseStatus, RespExt}; -use crate::router::{AppState, UserAuth}; -use atuin_server_database::{ - models::{NewSession, NewUser}, - Database, DbError, -}; - -use reqwest::header::CONTENT_TYPE; - -use atuin_common::api::*; - -pub fn verify_str(hash: &str, password: &str) -> bool { - let arg2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, Params::default()); - let Ok(hash) = PasswordHash::new(hash) else { - return false; - }; - arg2.verify_password(password.as_bytes(), &hash).is_ok() -} - -// Try to send a Discord webhook once - if it fails, we don't retry. "At most once", and best effort. -// Don't return the status because if this fails, we don't really care. -async fn send_register_hook(url: &str, username: String, registered: String) { - let hook = HashMap::from([ - ("username", username), - ("content", format!("{registered} has just signed up!")), - ]); - - let client = reqwest::Client::new(); - - let resp = client - .post(url) - .timeout(Duration::new(5, 0)) - .header(CONTENT_TYPE, "application/json") - .json(&hook) - .send() - .await; - - match resp { - Ok(_) => info!("register webhook sent ok!"), - Err(e) => error!("failed to send register webhook: {}", e), - } -} - -#[instrument(skip_all, fields(user.username = username.as_str()))] -pub async fn get<DB: Database>( - Path(username): Path<String>, - state: State<AppState<DB>>, -) -> Result<Json<UserResponse>, ErrorResponseStatus<'static>> { - let db = &state.0.database; - let user = match db.get_user(username.as_ref()).await { - Ok(user) => user, - Err(DbError::NotFound) => { - debug!("user not found: {}", username); - return Err(ErrorResponse::reply("user not found").with_status(StatusCode::NOT_FOUND)); - } - Err(DbError::Other(err)) => { - error!("database error: {}", err); - return Err(ErrorResponse::reply("database error") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - }; - - Ok(Json(UserResponse { - username: user.username, - })) -} - -#[instrument(skip_all)] -pub async fn register<DB: Database>( - state: State<AppState<DB>>, - Json(register): Json<RegisterRequest>, -) -> Result<Json<RegisterResponse>, ErrorResponseStatus<'static>> { - if !state.settings.open_registration { - return Err( - ErrorResponse::reply("this server is not open for registrations") - .with_status(StatusCode::BAD_REQUEST), - ); - } - - for c in register.username.chars() { - match c { - 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' => {} - _ => { - return Err(ErrorResponse::reply( - "Only alphanumeric and hyphens (-) are allowed in usernames", - ) - .with_status(StatusCode::BAD_REQUEST)) - } - } - } - - let hashed = hash_secret(®ister.password); - - let new_user = NewUser { - email: register.email.clone(), - username: register.username.clone(), - password: hashed, - }; - - let db = &state.0.database; - let user_id = match db.add_user(&new_user).await { - Ok(id) => id, - Err(e) => { - error!("failed to add user: {}", e); - return Err( - ErrorResponse::reply("failed to add user").with_status(StatusCode::BAD_REQUEST) - ); - } - }; - - let token = Uuid::new_v4().as_simple().to_string(); - - let new_session = NewSession { - user_id, - token: (&token).into(), - }; - - if let Some(url) = &state.settings.register_webhook_url { - // Could probs be run on another thread, but it's ok atm - send_register_hook( - url, - state.settings.register_webhook_username.clone(), - register.username, - ) - .await; - } - - counter!("atuin_users_registered", 1); - - match db.add_session(&new_session).await { - Ok(_) => Ok(Json(RegisterResponse { session: token })), - Err(e) => { - error!("failed to add session: {}", e); - Err(ErrorResponse::reply("failed to register user") - .with_status(StatusCode::BAD_REQUEST)) - } - } -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn delete<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<Json<DeleteUserResponse>, ErrorResponseStatus<'static>> { - debug!("request to delete user {}", user.id); - - let db = &state.0.database; - if let Err(e) = db.delete_user(&user).await { - error!("failed to delete user: {}", e); - - return Err(ErrorResponse::reply("failed to delete user") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - }; - - counter!("atuin_users_deleted", 1); - - Ok(Json(DeleteUserResponse {})) -} - -#[instrument(skip_all, fields(user.id = user.id, change_password))] -pub async fn change_password<DB: Database>( - UserAuth(mut user): UserAuth, - state: State<AppState<DB>>, - Json(change_password): Json<ChangePasswordRequest>, -) -> Result<Json<ChangePasswordResponse>, ErrorResponseStatus<'static>> { - let db = &state.0.database; - - let verified = verify_str( - user.password.as_str(), - change_password.current_password.borrow(), - ); - if !verified { - return Err( - ErrorResponse::reply("password is not correct").with_status(StatusCode::UNAUTHORIZED) - ); - } - - let hashed = hash_secret(&change_password.new_password); - user.password = hashed; - - if let Err(e) = db.update_user_password(&user).await { - error!("failed to change user password: {}", e); - - return Err(ErrorResponse::reply("failed to change user password") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - }; - Ok(Json(ChangePasswordResponse {})) -} - -#[instrument(skip_all, fields(user.username = login.username.as_str()))] -pub async fn login<DB: Database>( - state: State<AppState<DB>>, - login: Json<LoginRequest>, -) -> Result<Json<LoginResponse>, ErrorResponseStatus<'static>> { - let db = &state.0.database; - let user = match db.get_user(login.username.borrow()).await { - Ok(u) => u, - Err(DbError::NotFound) => { - return Err(ErrorResponse::reply("user not found").with_status(StatusCode::NOT_FOUND)); - } - Err(DbError::Other(e)) => { - error!("failed to get user {}: {}", login.username.clone(), e); - - return Err(ErrorResponse::reply("database error") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - }; - - let session = match db.get_user_session(&user).await { - Ok(u) => u, - Err(DbError::NotFound) => { - debug!("user session not found for user id={}", user.id); - return Err(ErrorResponse::reply("user not found").with_status(StatusCode::NOT_FOUND)); - } - Err(DbError::Other(err)) => { - error!("database error for user {}: {}", login.username, err); - return Err(ErrorResponse::reply("database error") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - }; - - let verified = verify_str(user.password.as_str(), login.password.borrow()); - - if !verified { - return Err( - ErrorResponse::reply("password is not correct").with_status(StatusCode::UNAUTHORIZED) - ); - } - - Ok(Json(LoginResponse { - session: session.token, - })) -} - -fn hash_secret(password: &str) -> String { - let arg2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, Params::default()); - let salt = SaltString::generate(&mut OsRng); - let hash = arg2.hash_password(password.as_bytes(), &salt).unwrap(); - hash.to_string() -} diff --git a/atuin-server/src/handlers/v0/me.rs b/atuin-server/src/handlers/v0/me.rs deleted file mode 100644 index 7960b479..00000000 --- a/atuin-server/src/handlers/v0/me.rs +++ /dev/null @@ -1,16 +0,0 @@ -use axum::Json; -use tracing::instrument; - -use crate::handlers::ErrorResponseStatus; -use crate::router::UserAuth; - -use atuin_common::api::*; - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn get( - UserAuth(user): UserAuth, -) -> Result<Json<MeResponse>, ErrorResponseStatus<'static>> { - Ok(Json(MeResponse { - username: user.username, - })) -} diff --git a/atuin-server/src/handlers/v0/mod.rs b/atuin-server/src/handlers/v0/mod.rs deleted file mode 100644 index d6f880f2..00000000 --- a/atuin-server/src/handlers/v0/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod me; -pub(crate) mod record; -pub(crate) mod store; diff --git a/atuin-server/src/handlers/v0/record.rs b/atuin-server/src/handlers/v0/record.rs deleted file mode 100644 index 321c34c2..00000000 --- a/atuin-server/src/handlers/v0/record.rs +++ /dev/null @@ -1,112 +0,0 @@ -use axum::{extract::Query, extract::State, http::StatusCode, Json}; -use metrics::counter; -use serde::Deserialize; -use tracing::{error, instrument}; - -use crate::{ - handlers::{ErrorResponse, ErrorResponseStatus, RespExt}, - router::{AppState, UserAuth}, -}; -use atuin_server_database::Database; - -use atuin_common::record::{EncryptedData, HostId, Record, RecordIdx, RecordStatus}; - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn post<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, - Json(records): Json<Vec<Record<EncryptedData>>>, -) -> Result<(), ErrorResponseStatus<'static>> { - let State(AppState { database, settings }) = state; - - tracing::debug!( - count = records.len(), - user = user.username, - "request to add records" - ); - - counter!("atuin_record_uploaded", records.len() as u64); - - let keep = records - .iter() - .all(|r| r.data.data.len() <= settings.max_record_size || settings.max_record_size == 0); - - if !keep { - counter!("atuin_record_too_large", 1); - - return Err( - ErrorResponse::reply("could not add records; record too large") - .with_status(StatusCode::BAD_REQUEST), - ); - } - - if let Err(e) = database.add_records(&user, &records).await { - error!("failed to add record: {}", e); - - return Err(ErrorResponse::reply("failed to add record") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - }; - - Ok(()) -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn index<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<Json<RecordStatus>, ErrorResponseStatus<'static>> { - let State(AppState { - database, - settings: _, - }) = state; - - let record_index = match database.status(&user).await { - Ok(index) => index, - Err(e) => { - error!("failed to get record index: {}", e); - - return Err(ErrorResponse::reply("failed to calculate record index") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - }; - - Ok(Json(record_index)) -} - -#[derive(Deserialize)] -pub struct NextParams { - host: HostId, - tag: String, - start: Option<RecordIdx>, - count: u64, -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn next<DB: Database>( - params: Query<NextParams>, - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<Json<Vec<Record<EncryptedData>>>, ErrorResponseStatus<'static>> { - let State(AppState { - database, - settings: _, - }) = state; - let params = params.0; - - let records = match database - .next_records(&user, params.host, params.tag, params.start, params.count) - .await - { - Ok(records) => records, - Err(e) => { - error!("failed to get record index: {}", e); - - return Err(ErrorResponse::reply("failed to calculate record index") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - }; - - counter!("atuin_record_downloaded", records.len() as u64); - - Ok(Json(records)) -} diff --git a/atuin-server/src/handlers/v0/store.rs b/atuin-server/src/handlers/v0/store.rs deleted file mode 100644 index 941f2487..00000000 --- a/atuin-server/src/handlers/v0/store.rs +++ /dev/null @@ -1,37 +0,0 @@ -use axum::{extract::Query, extract::State, http::StatusCode}; -use metrics::counter; -use serde::Deserialize; -use tracing::{error, instrument}; - -use crate::{ - handlers::{ErrorResponse, ErrorResponseStatus, RespExt}, - router::{AppState, UserAuth}, -}; -use atuin_server_database::Database; - -#[derive(Deserialize)] -pub struct DeleteParams {} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn delete<DB: Database>( - _params: Query<DeleteParams>, - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<(), ErrorResponseStatus<'static>> { - let State(AppState { - database, - settings: _, - }) = state; - - if let Err(e) = database.delete_store(&user).await { - counter!("atuin_store_delete_failed", 1); - error!("failed to delete store {e:?}"); - - return Err(ErrorResponse::reply("failed to delete store") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - - counter!("atuin_store_deleted", 1); - - Ok(()) -} |
