feat: add delete account option (attempt 2) (#980)
* Added DELETE register endpoint * Added remove function to database * Added unregister to client * Updated docs * Renamed functions * Reformatting * Used execute instead of fetch in delete_user
This commit is contained in:
parent
dc523416f6
commit
7b9dea72e3
8 changed files with 94 additions and 1 deletions
|
@ -217,4 +217,19 @@ impl<'a> Client<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(&self) -> Result<()> {
|
||||
let url = format!("{}/register", self.sync_addr);
|
||||
let url = Url::parse(url.as_str())?;
|
||||
|
||||
let resp = self.client.delete(url).send().await?;
|
||||
|
||||
if resp.status() == 403 {
|
||||
bail!("invalid login details");
|
||||
} else if resp.status() == 200 {
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("Unknown error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ pub struct RegisterResponse {
|
|||
pub session: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DeleteUserResponse {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct LoginRequest {
|
||||
pub username: String,
|
||||
|
|
|
@ -27,6 +27,7 @@ pub trait Database {
|
|||
async fn get_user(&self, username: &str) -> Result<User>;
|
||||
async fn get_user_session(&self, u: &User) -> Result<Session>;
|
||||
async fn add_user(&self, user: &NewUser) -> Result<i64>;
|
||||
async fn delete_user(&self, u: &User) -> Result<()>;
|
||||
|
||||
async fn count_history(&self, user: &User) -> Result<i64>;
|
||||
async fn count_history_cached(&self, user: &User) -> Result<i64>;
|
||||
|
@ -336,6 +337,26 @@ impl Database for Postgres {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn delete_user(&self, u: &User) -> Result<()> {
|
||||
sqlx::query("delete from sessions where user_id = $1")
|
||||
.bind(u.id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query("delete from users where id = $1")
|
||||
.bind(u.id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query("delete from history where user_id = $1")
|
||||
.bind(u.id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn add_user(&self, user: &NewUser) -> Result<i64> {
|
||||
let email: &str = &user.email;
|
||||
|
|
|
@ -18,7 +18,7 @@ use uuid::Uuid;
|
|||
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
|
||||
use crate::{
|
||||
database::Database,
|
||||
models::{NewSession, NewUser},
|
||||
models::{NewSession, NewUser, User},
|
||||
router::AppState,
|
||||
};
|
||||
|
||||
|
@ -138,6 +138,23 @@ pub async fn register<DB: Database>(
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(user.id = user.id))]
|
||||
pub async fn delete<DB: Database>(
|
||||
user: User,
|
||||
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));
|
||||
};
|
||||
Ok(Json(DeleteUserResponse {}))
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(user.username = login.username.as_str()))]
|
||||
pub async fn login<DB: Database>(
|
||||
state: State<AppState<DB>>,
|
||||
|
|
|
@ -72,6 +72,7 @@ pub fn router<DB: Database + Clone + Send + Sync + 'static>(
|
|||
.route("/history", post(handlers::history::add))
|
||||
.route("/history", delete(handlers::history::delete))
|
||||
.route("/user/:username", get(handlers::user::get))
|
||||
.route("/account", delete(handlers::user::delete))
|
||||
.route("/register", post(handlers::user::register))
|
||||
.route("/login", post(handlers::user::login));
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use eyre::{Result, WrapErr};
|
|||
|
||||
use atuin_client::{database::Database, settings::Settings};
|
||||
|
||||
mod delete;
|
||||
mod login;
|
||||
mod logout;
|
||||
mod register;
|
||||
|
@ -27,6 +28,9 @@ pub enum Cmd {
|
|||
/// Register with the configured server
|
||||
Register(register::Cmd),
|
||||
|
||||
/// Unregister with the configured server
|
||||
Unregister,
|
||||
|
||||
/// Print the encryption key for transfer to another machine
|
||||
Key {
|
||||
/// Switch to base64 output of the key
|
||||
|
@ -44,6 +48,7 @@ impl Cmd {
|
|||
Self::Login(l) => l.run(&settings).await,
|
||||
Self::Logout => logout::run(&settings),
|
||||
Self::Register(r) => r.run(&settings).await,
|
||||
Self::Unregister => delete::run(&settings).await,
|
||||
Self::Status => status::run(&settings, db).await,
|
||||
Self::Key { base64 } => {
|
||||
use atuin_client::encryption::{encode_key, load_key};
|
||||
|
|
23
atuin/src/command/client/sync/delete.rs
Normal file
23
atuin/src/command/client/sync/delete.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use atuin_client::{api_client, encryption::load_encoded_key, settings::Settings};
|
||||
use eyre::{bail, Result};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub async fn run(settings: &Settings) -> Result<()> {
|
||||
let session_path = settings.session_path.as_str();
|
||||
|
||||
if !PathBuf::from(session_path).exists() {
|
||||
bail!("You are not logged in");
|
||||
}
|
||||
|
||||
let client = api_client::Client::new(
|
||||
&settings.sync_address,
|
||||
&settings.session_token,
|
||||
load_encoded_key(settings)?,
|
||||
)?;
|
||||
|
||||
client.delete().await?;
|
||||
|
||||
println!("Your account is deleted");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -32,6 +32,14 @@ notifications (security breaches, changes to service, etc).
|
|||
Upon success, you are also logged in :) Syncing should happen automatically from
|
||||
here!
|
||||
|
||||
## Delete
|
||||
|
||||
You can delete your sync account with
|
||||
|
||||
```
|
||||
atuin unregister
|
||||
```
|
||||
|
||||
## Key
|
||||
|
||||
As all your data is encrypted, Atuin generates a key for you. It's stored in the
|
||||
|
|
Loading…
Reference in a new issue