diff options
19 files changed, 33 insertions, 530 deletions
@@ -289,7 +289,7 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "regex", - "reqwest 0.13.1", + "reqwest", "rmp", "rusty_paserk", "rusty_paseto", @@ -309,7 +309,7 @@ dependencies = [ "time", "tiny-bip39", "tokio", - "typed-builder 0.18.2", + "typed-builder", "urlencoding", "uuid", "whoami", @@ -330,7 +330,7 @@ dependencies = [ "sysinfo", "thiserror 1.0.69", "time", - "typed-builder 0.18.2", + "typed-builder", "uuid", ] @@ -401,7 +401,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "typed-builder 0.18.2", + "typed-builder", ] [[package]] @@ -422,7 +422,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "typed-builder 0.18.2", + "typed-builder", "uuid", ] @@ -440,9 +440,8 @@ dependencies = [ "fs-err", "metrics", "metrics-exporter-prometheus", - "postmark", "rand 0.8.5", - "reqwest 0.13.1", + "reqwest", "semver", "serde", "serde_json", @@ -1987,7 +1986,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.5", ] [[package]] @@ -3257,24 +3255,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] -name = "postmark" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "846751b682939565add1f69358a595fa6f3f7d4f1eb15d920b16478e0f981fe2" -dependencies = [ - "async-trait", - "bytes", - "http", - "reqwest 0.12.28", - "serde", - "serde_json", - "thiserror 2.0.18", - "time", - "typed-builder 0.21.2", - "url", -] - -[[package]] name = "potential_utf" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3796,44 +3776,6 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower 0.5.3", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 1.0.5", -] - -[[package]] -name = "reqwest" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04e9018c9d814e5f30cc16a0f03271aeab3571e609612d9fe78c1aa8d11c2f62" @@ -5351,16 +5293,7 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77739c880e00693faef3d65ea3aad725f196da38b22fdc7ea6ded6e1ce4d3add" dependencies = [ - "typed-builder-macro 0.18.2", -] - -[[package]] -name = "typed-builder" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef81aec2ca29576f9f6ae8755108640d0a86dd3161b2e8bca6cfa554e98f77d" -dependencies = [ - "typed-builder-macro 0.21.2", + "typed-builder-macro", ] [[package]] @@ -5375,17 +5308,6 @@ dependencies = [ ] [[package]] -name = "typed-builder-macro" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecb9ecf7799210407c14a8cfdfe0173365780968dc57973ed082211958e0b18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] name = "typenum" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/crates/atuin-client/src/api_client.rs b/crates/atuin-client/src/api_client.rs index 41f824a9..86452d50 100644 --- a/crates/atuin-client/src/api_client.rs +++ b/crates/atuin-client/src/api_client.rs @@ -15,9 +15,8 @@ use atuin_common::{ use atuin_common::{ api::{ AddHistoryRequest, ChangePasswordRequest, CountResponse, DeleteHistoryRequest, - ErrorResponse, LoginRequest, LoginResponse, MeResponse, RegisterResponse, - SendVerificationResponse, StatusResponse, SyncHistoryResponse, VerificationTokenRequest, - VerificationTokenResponse, + ErrorResponse, LoginRequest, LoginResponse, MeResponse, RegisterResponse, StatusResponse, + SyncHistoryResponse, }, record::RecordStatus, }; @@ -427,35 +426,4 @@ impl<'a> Client<'a> { bail!("Unknown error"); } } - - // Either request a verification email if token is null, or validate a token - pub async fn verify(&self, token: Option<String>) -> Result<(bool, bool)> { - // could dedupe this a bit, but it's simple at the moment - let (email_sent, verified) = if let Some(token) = token { - let url = make_url(self.sync_addr, "/api/v0/account/verify")?; - let url = Url::parse(url.as_str())?; - - let resp = self - .client - .post(url) - .json(&VerificationTokenRequest { token }) - .send() - .await?; - let resp = handle_resp_error(resp).await?; - let resp = resp.json::<VerificationTokenResponse>().await?; - - (false, resp.verified) - } else { - let url = make_url(self.sync_addr, "/api/v0/account/send-verification")?; - let url = Url::parse(url.as_str())?; - - let resp = self.client.post(url).send().await?; - let resp = handle_resp_error(resp).await?; - let resp = resp.json::<SendVerificationResponse>().await?; - - (resp.email_sent, resp.verified) - }; - - Ok((email_sent, verified)) - } } diff --git a/crates/atuin-common/src/api.rs b/crates/atuin-common/src/api.rs index 973bdc8e..5887424f 100644 --- a/crates/atuin-common/src/api.rs +++ b/crates/atuin-common/src/api.rs @@ -32,22 +32,6 @@ pub struct RegisterResponse { pub struct DeleteUserResponse {} #[derive(Debug, Serialize, Deserialize)] -pub struct SendVerificationResponse { - pub email_sent: bool, - pub verified: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct VerificationTokenRequest { - pub token: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct VerificationTokenResponse { - pub verified: bool, -} - -#[derive(Debug, Serialize, Deserialize)] pub struct ChangePasswordRequest { pub current_password: String, pub new_password: String, diff --git a/crates/atuin-server-database/src/lib.rs b/crates/atuin-server-database/src/lib.rs index a4ddf23c..6000a530 100644 --- a/crates/atuin-server-database/src/lib.rs +++ b/crates/atuin-server-database/src/lib.rs @@ -107,10 +107,6 @@ pub trait Database: Sized + Clone + Send + Sync + 'static { async fn get_user_session(&self, u: &User) -> DbResult<Session>; async fn add_user(&self, user: &NewUser) -> DbResult<i64>; - async fn user_verified(&self, id: i64) -> DbResult<bool>; - async fn verify_user(&self, id: i64) -> DbResult<()>; - async fn user_verification_token(&self, id: i64) -> DbResult<String>; - async fn update_user_password(&self, u: &User) -> DbResult<()>; async fn count_history(&self, user: &User) -> DbResult<i64>; diff --git a/crates/atuin-server-database/src/models.rs b/crates/atuin-server-database/src/models.rs index 894ac7f6..b71a9bc9 100644 --- a/crates/atuin-server-database/src/models.rs +++ b/crates/atuin-server-database/src/models.rs @@ -32,7 +32,6 @@ pub struct User { pub username: String, pub email: String, pub password: String, - pub verified: Option<OffsetDateTime>, } pub struct Session { diff --git a/crates/atuin-server-postgres/migrations/20260127000000_remove-email-verification.sql b/crates/atuin-server-postgres/migrations/20260127000000_remove-email-verification.sql new file mode 100644 index 00000000..15309920 --- /dev/null +++ b/crates/atuin-server-postgres/migrations/20260127000000_remove-email-verification.sql @@ -0,0 +1,2 @@ +drop table if exists user_verification_token; +alter table users drop column if exists verified_at; diff --git a/crates/atuin-server-postgres/src/lib.rs b/crates/atuin-server-postgres/src/lib.rs index 54ba2ee8..ce101d8d 100644 --- a/crates/atuin-server-postgres/src/lib.rs +++ b/crates/atuin-server-postgres/src/lib.rs @@ -5,7 +5,6 @@ use rand::Rng; use async_trait::async_trait; use atuin_common::record::{EncryptedData, HostId, Record, RecordIdx, RecordStatus}; -use atuin_common::utils::crypto_random_string; use atuin_server_database::models::{History, NewHistory, NewSession, NewUser, Session, User}; use atuin_server_database::{Database, DbError, DbResult, DbSettings}; use futures_util::TryStreamExt; @@ -13,7 +12,7 @@ use sqlx::Row; use sqlx::postgres::PgPoolOptions; use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset}; -use tracing::{instrument, trace}; +use tracing::instrument; use uuid::Uuid; use wrappers::{DbHistory, DbRecord, DbSession, DbUser}; @@ -121,100 +120,18 @@ impl Database for Postgres { #[instrument(skip_all)] async fn get_user(&self, username: &str) -> DbResult<User> { - sqlx::query_as( - "select id, username, email, password, verified_at from users where username = $1", - ) - .bind(username) - .fetch_one(self.read_pool()) - .await - .map_err(fix_error) - .map(|DbUser(user)| user) - } - - #[instrument(skip_all)] - async fn user_verified(&self, id: i64) -> DbResult<bool> { - let res: (bool,) = - sqlx::query_as("select verified_at is not null from users where id = $1") - .bind(id) - .fetch_one(self.read_pool()) - .await - .map_err(fix_error)?; - - Ok(res.0) - } - - #[instrument(skip_all)] - async fn verify_user(&self, id: i64) -> DbResult<()> { - sqlx::query( - "update users set verified_at = (current_timestamp at time zone 'utc') where id=$1", - ) - .bind(id) - .execute(&self.pool) - .await - .map_err(fix_error)?; - - Ok(()) - } - - /// Return a valid verification token for the user - /// If the user does not have any token, create one, insert it, and return - /// If the user has a token, but it's invalid, delete it, create a new one, return - /// If the user already has a valid token, return it - #[instrument(skip_all)] - async fn user_verification_token(&self, id: i64) -> DbResult<String> { - const TOKEN_VALID_MINUTES: i64 = 15; - - // First we check if there is a verification token - let token: Option<(String, sqlx::types::time::OffsetDateTime)> = sqlx::query_as( - "select token, valid_until from user_verification_token where user_id = $1", - ) - .bind(id) - .fetch_optional(&self.pool) - .await - .map_err(fix_error)?; - - let token = if let Some((token, valid_until)) = token { - trace!("Token for user {id} valid until {valid_until}"); - - // We have a token, AND it's still valid - if valid_until > time::OffsetDateTime::now_utc() { - token - } else { - // token has expired. generate a new one, return it - let token = crypto_random_string::<24>(); - - sqlx::query("update user_verification_token set token = $2, valid_until = $3 where user_id=$1") - .bind(id) - .bind(&token) - .bind(time::OffsetDateTime::now_utc() + time::Duration::minutes(TOKEN_VALID_MINUTES)) - .execute(&self.pool) - .await - .map_err(fix_error)?; - - token - } - } else { - // No token in the database! Generate one, insert it - let token = crypto_random_string::<24>(); - - sqlx::query("insert into user_verification_token (user_id, token, valid_until) values ($1, $2, $3)") - .bind(id) - .bind(&token) - .bind(time::OffsetDateTime::now_utc() + time::Duration::minutes(TOKEN_VALID_MINUTES)) - .execute(&self.pool) - .await - .map_err(fix_error)?; - - token - }; - - Ok(token) + sqlx::query_as("select id, username, email, password from users where username = $1") + .bind(username) + .fetch_one(self.read_pool()) + .await + .map_err(fix_error) + .map(|DbUser(user)| user) } #[instrument(skip_all)] async fn get_session_user(&self, token: &str) -> DbResult<User> { sqlx::query_as( - "select users.id, users.username, users.email, users.password, users.verified_at from users + "select users.id, users.username, users.email, users.password from users inner join sessions on users.id = sessions.user_id and sessions.token = $1", @@ -431,12 +348,6 @@ impl Database for Postgres { .await .map_err(fix_error)?; - sqlx::query("delete from user_verification_token where user_id = $1") - .bind(u.id) - .execute(&self.pool) - .await - .map_err(fix_error)?; - sqlx::query("delete from total_history_count_user where user_id = $1") .bind(u.id) .execute(&self.pool) diff --git a/crates/atuin-server-postgres/src/wrappers.rs b/crates/atuin-server-postgres/src/wrappers.rs index 0d6a0ee6..cde4134c 100644 --- a/crates/atuin-server-postgres/src/wrappers.rs +++ b/crates/atuin-server-postgres/src/wrappers.rs @@ -16,7 +16,6 @@ impl<'a> FromRow<'a, PgRow> for DbUser { username: row.try_get("username")?, email: row.try_get("email")?, password: row.try_get("password")?, - verified: row.try_get("verified_at")?, })) } } diff --git a/crates/atuin-server-sqlite/migrations/20260127000000_remove-email-verification.sql b/crates/atuin-server-sqlite/migrations/20260127000000_remove-email-verification.sql new file mode 100644 index 00000000..15309920 --- /dev/null +++ b/crates/atuin-server-sqlite/migrations/20260127000000_remove-email-verification.sql @@ -0,0 +1,2 @@ +drop table if exists user_verification_token; +alter table users drop column if exists verified_at; diff --git a/crates/atuin-server-sqlite/src/lib.rs b/crates/atuin-server-sqlite/src/lib.rs index 83d05ea5..d69258c4 100644 --- a/crates/atuin-server-sqlite/src/lib.rs +++ b/crates/atuin-server-sqlite/src/lib.rs @@ -1,10 +1,7 @@ use std::str::FromStr; use async_trait::async_trait; -use atuin_common::{ - record::{EncryptedData, HostId, Record, RecordIdx, RecordStatus}, - utils::crypto_random_string, -}; +use atuin_common::record::{EncryptedData, HostId, Record, RecordIdx, RecordStatus}; use atuin_server_database::{ Database, DbError, DbResult, DbSettings, models::{History, NewHistory, NewSession, NewUser, Session, User}, @@ -67,9 +64,9 @@ impl Database for Sqlite { #[instrument(skip_all)] async fn get_session_user(&self, token: &str) -> DbResult<User> { sqlx::query_as( - "select users.id, users.username, users.email, users.password, users.verified_at from users - inner join sessions - on users.id = sessions.user_id + "select users.id, users.username, users.email, users.password from users + inner join sessions + on users.id = sessions.user_id and sessions.token = $1", ) .bind(token) @@ -99,14 +96,12 @@ impl Database for Sqlite { #[instrument(skip_all)] async fn get_user(&self, username: &str) -> DbResult<User> { - sqlx::query_as( - "select id, username, email, password, verified_at from users where username = $1", - ) - .bind(username) - .fetch_one(&self.pool) - .await - .map_err(fix_error) - .map(|DbUser(user)| user) + sqlx::query_as("select id, username, email, password from users where username = $1") + .bind(username) + .fetch_one(&self.pool) + .await + .map_err(fix_error) + .map(|DbUser(user)| user) } #[instrument(skip_all)] @@ -142,80 +137,6 @@ impl Database for Sqlite { } #[instrument(skip_all)] - async fn user_verified(&self, id: i64) -> DbResult<bool> { - let res: (bool,) = - sqlx::query_as("select verified_at is not null from users where id = $1") - .bind(id) - .fetch_one(&self.pool) - .await - .map_err(fix_error)?; - - Ok(res.0) - } - - #[instrument(skip_all)] - async fn verify_user(&self, id: i64) -> DbResult<()> { - sqlx::query( - "update users set verified_at = (current_timestamp at time zone 'utc') where id=$1", - ) - .bind(id) - .execute(&self.pool) - .await - .map_err(fix_error)?; - - Ok(()) - } - - #[instrument(skip_all)] - async fn user_verification_token(&self, id: i64) -> DbResult<String> { - const TOKEN_VALID_MINUTES: i64 = 15; - - // First we check if there is a verification token - let token: Option<(String, sqlx::types::time::OffsetDateTime)> = sqlx::query_as( - "select token, valid_until from user_verification_token where user_id = $1", - ) - .bind(id) - .fetch_optional(&self.pool) - .await - .map_err(fix_error)?; - - let token = if let Some((token, valid_until)) = token { - // We have a token, AND it's still valid - if valid_until > time::OffsetDateTime::now_utc() { - token - } else { - // token has expired. generate a new one, return it - let token = crypto_random_string::<24>(); - - sqlx::query("update user_verification_token set token = $2, valid_until = $3 where user_id=$1") - .bind(id) - .bind(&token) - .bind(time::OffsetDateTime::now_utc() + time::Duration::minutes(TOKEN_VALID_MINUTES)) - .execute(&self.pool) - .await - .map_err(fix_error)?; - - token - } - } else { - // No token in the database! Generate one, insert it - let token = crypto_random_string::<24>(); - - sqlx::query("insert into user_verification_token (user_id, token, valid_until) values ($1, $2, $3)") - .bind(id) - .bind(&token) - .bind(time::OffsetDateTime::now_utc() + time::Duration::minutes(TOKEN_VALID_MINUTES)) - .execute(&self.pool) - .await - .map_err(fix_error)?; - - token - }; - - Ok(token) - } - - #[instrument(skip_all)] async fn update_user_password(&self, user: &User) -> DbResult<()> { sqlx::query( "update users diff --git a/crates/atuin-server-sqlite/src/wrappers.rs b/crates/atuin-server-sqlite/src/wrappers.rs index 3f2262c3..2f1230c2 100644 --- a/crates/atuin-server-sqlite/src/wrappers.rs +++ b/crates/atuin-server-sqlite/src/wrappers.rs @@ -15,7 +15,6 @@ impl<'a> FromRow<'a, SqliteRow> for DbUser { username: row.try_get("username")?, email: row.try_get("email")?, password: row.try_get("password")?, - verified: row.try_get("verified_at")?, })) } } diff --git a/crates/atuin-server/Cargo.toml b/crates/atuin-server/Cargo.toml index 915ceb14..ea647f38 100644 --- a/crates/atuin-server/Cargo.toml +++ b/crates/atuin-server/Cargo.toml @@ -32,4 +32,3 @@ argon2 = "0.5" semver = { workspace = true } metrics-exporter-prometheus = "0.18" metrics = "0.24" -postmark = {version= "0.11", features=["reqwest", "reqwest-rustls-tls"]} diff --git a/crates/atuin-server/src/handlers/user.rs b/crates/atuin-server/src/handlers/user.rs index 4edd1787..c6fec51e 100644 --- a/crates/atuin-server/src/handlers/user.rs +++ b/crates/atuin-server/src/handlers/user.rs @@ -13,8 +13,6 @@ use axum::{ }; use metrics::counter; -use postmark::{Query, reqwest::PostmarkClient}; - use rand::rngs::OsRng; use tracing::{debug, error, info, instrument}; @@ -178,109 +176,6 @@ pub async fn delete<DB: Database>( Ok(Json(DeleteUserResponse {})) } -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn send_verification<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, -) -> Result<Json<SendVerificationResponse>, ErrorResponseStatus<'static>> { - let settings = state.0.settings; - debug!("request to verify user {}", user.username); - - if !settings.mail.enabled { - return Ok(Json(SendVerificationResponse { - email_sent: false, - verified: false, - })); - } - - if user.verified.is_some() { - return Ok(Json(SendVerificationResponse { - email_sent: false, - verified: true, - })); - } - - // TODO: if we ever add another mail provider, can match on them all here. - let postmark_token = match settings.mail.postmark.token { - Some(token) => token, - _ => { - error!("Failed to verify email: got None for postmark token"); - return Err(ErrorResponse::reply("mail not configured") - .with_status(StatusCode::INTERNAL_SERVER_ERROR)); - } - }; - - let db = &state.0.database; - - let verification_token = db - .user_verification_token(user.id) - .await - .expect("Failed to verify"); - - debug!("Generated verification token, emailing user"); - - let client = PostmarkClient::builder() - .base_url("https://api.postmarkapp.com/") - .server_token(postmark_token) - .build(); - - let req = postmark::api::email::SendEmailRequest::builder() - .from(settings.mail.verification.from) - .subject(settings.mail.verification.subject) - .to(user.email) - .body(postmark::api::Body::text(format!( - "Please run the following command to finalize your Atuin account verification. It is valid for 15 minutes:\n\natuin account verify --token '{verification_token}'" - ))) - .build(); - - req.execute(&client) - .await - .expect("postmark email request failed"); - - debug!("Email sent"); - - Ok(Json(SendVerificationResponse { - email_sent: true, - verified: false, - })) -} - -#[instrument(skip_all, fields(user.id = user.id))] -pub async fn verify_user<DB: Database>( - UserAuth(user): UserAuth, - state: State<AppState<DB>>, - Json(token_request): Json<VerificationTokenRequest>, -) -> Result<Json<VerificationTokenResponse>, ErrorResponseStatus<'static>> { - let db = state.0.database; - - if user.verified.is_some() { - return Ok(Json(VerificationTokenResponse { verified: true })); - } - - let token = db.user_verification_token(user.id).await.map_err(|e| { - error!("Failed to read user token: {e}"); - - ErrorResponse::reply("Failed to verify").with_status(StatusCode::INTERNAL_SERVER_ERROR) - })?; - - if token_request.token == token { - db.verify_user(user.id).await.map_err(|e| { - error!("Failed to verify user: {e}"); - - ErrorResponse::reply("Failed to verify").with_status(StatusCode::INTERNAL_SERVER_ERROR) - })?; - } else { - info!( - "Incorrect verification token {} vs {}", - token_request.token, token - ); - - return Ok(Json(VerificationTokenResponse { verified: false })); - } - - Ok(Json(VerificationTokenResponse { verified: true })) -} - #[instrument(skip_all, fields(user.id = user.id, change_password))] pub async fn change_password<DB: Database>( UserAuth(mut user): UserAuth, diff --git a/crates/atuin-server/src/router.rs b/crates/atuin-server/src/router.rs index 9d4f7d44..0c41d5e6 100644 --- a/crates/atuin-server/src/router.rs +++ b/crates/atuin-server/src/router.rs @@ -134,11 +134,6 @@ pub fn router<DB: Database>(database: DB, settings: Settings) -> Router { .route("/record", get(handlers::record::index)) .route("/record/next", get(handlers::record::next)) .route("/api/v0/me", get(handlers::v0::me::get)) - .route("/api/v0/account/verify", post(handlers::user::verify_user)) - .route( - "/api/v0/account/send-verification", - post(handlers::user::send_verification), - ) .route("/api/v0/record", post(handlers::v0::record::post)) .route("/api/v0/record", get(handlers::v0::record::index)) .route("/api/v0/record/next", get(handlers::v0::record::next)) diff --git a/crates/atuin-server/src/settings.rs b/crates/atuin-server/src/settings.rs index 98d1d69f..3a612be9 100644 --- a/crates/atuin-server/src/settings.rs +++ b/crates/atuin-server/src/settings.rs @@ -8,33 +8,6 @@ use serde::{Deserialize, Serialize}; static EXAMPLE_CONFIG: &str = include_str!("../server.toml"); -#[derive(Default, Clone, Debug, Deserialize, Serialize)] -pub struct Mail { - #[serde(alias = "enable")] - pub enabled: bool, - - /// Configuration for the postmark api client - /// This is what we use for Atuin Cloud, the forum, etc. - #[serde(default)] - pub postmark: Postmark, - - #[serde(default)] - pub verification: MailVerification, -} - -#[derive(Default, Clone, Debug, Deserialize, Serialize)] -pub struct Postmark { - #[serde(alias = "token")] - pub token: Option<String>, -} - -#[derive(Default, Clone, Debug, Deserialize, Serialize)] -pub struct MailVerification { - #[serde(alias = "enable")] - pub from: String, - pub subject: String, -} - #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Metrics { #[serde(alias = "enabled")] @@ -65,7 +38,6 @@ pub struct Settings { pub register_webhook_url: Option<String>, pub register_webhook_username: String, pub metrics: Metrics, - pub mail: Mail, /// Enable legacy sync v1 routes (history-based sync) /// Set to false to use only the newer record-based sync @@ -108,7 +80,6 @@ impl Settings { .set_default("metrics.enable", false)? .set_default("metrics.host", "127.0.0.1")? .set_default("metrics.port", 9001)? - .set_default("mail.enable", false)? .set_default("sync_v1_enabled", true)? .add_source( Environment::with_prefix("atuin") diff --git a/crates/atuin/src/command/client/account.rs b/crates/atuin/src/command/client/account.rs index 011d7c69..e99c9593 100644 --- a/crates/atuin/src/command/client/account.rs +++ b/crates/atuin/src/command/client/account.rs @@ -9,7 +9,6 @@ pub mod delete; pub mod login; pub mod logout; pub mod register; -pub mod verify; #[derive(Args, Debug)] pub struct Cmd { @@ -33,9 +32,6 @@ pub enum Commands { /// Change your password ChangePassword(change_password::Cmd), - - /// Verify your account - Verify(verify::Cmd), } impl Cmd { @@ -46,7 +42,6 @@ impl Cmd { Commands::Logout => logout::run(&settings), Commands::Delete => delete::run(&settings).await, Commands::ChangePassword(c) => c.run(&settings).await, - Commands::Verify(c) => c.run(&settings).await, } } } diff --git a/crates/atuin/src/command/client/account/verify.rs b/crates/atuin/src/command/client/account/verify.rs deleted file mode 100644 index 1533c283..00000000 --- a/crates/atuin/src/command/client/account/verify.rs +++ /dev/null @@ -1,51 +0,0 @@ -use clap::Parser; -use eyre::Result; - -use atuin_client::{api_client, settings::Settings}; - -#[derive(Parser, Debug)] -pub struct Cmd { - #[clap(long, short)] - pub token: Option<String>, -} - -impl Cmd { - pub async fn run(self, settings: &Settings) -> Result<()> { - run(settings, self.token).await - } -} - -pub async fn run(settings: &Settings, token: Option<String>) -> Result<()> { - let client = api_client::Client::new( - &settings.sync_address, - settings.session_token()?.as_str(), - settings.network_connect_timeout, - settings.network_timeout, - )?; - - let (email_sent, verified) = client.verify(token).await?; - - match (email_sent, verified) { - (true, false) => { - println!("Verification sent! Please check your inbox"); - } - - (false, true) => { - println!("Your account is verified"); - } - - (false, false) => { - println!( - "Your Atuin server does not have mail set up. This is not required, though your account cannot be verified. Speak to your admin." - ); - } - - _ => { - println!( - "Invalid email and verification status. This is a bug. Please open an issue: https://github.com/atuinsh/atuin" - ); - } - } - - Ok(()) -} diff --git a/crates/atuin/tests/common/mod.rs b/crates/atuin/tests/common/mod.rs index 6cc4e443..fa663ef3 100644 --- a/crates/atuin/tests/common/mod.rs +++ b/crates/atuin/tests/common/mod.rs @@ -42,7 +42,6 @@ pub async fn start_server(path: &str) -> (String, oneshot::Sender<()>, JoinHandl read_db_uri: None, }, metrics: atuin_server::settings::Metrics::default(), - mail: atuin_server::settings::Mail::default(), fake_version: None, }; diff --git a/docs/docs/faq.md b/docs/docs/faq.md index d016ef29..c4f51c6f 100644 --- a/docs/docs/faq.md +++ b/docs/docs/faq.md @@ -32,11 +32,8 @@ This will delete your account, and all history from the remote server. It will n ## I've forgotten my password! How can I reset it? -We don't (yet) have a password reset system, as we don't verify emails. This -may change soon, but in the meantime so long as you're still logged in on at -least one account, it's safe to delete and re-create the account. - -We're aware this isn't optimal. +We don't currently have a password reset system. So long as you're still logged +in on at least one machine, it's safe to delete and re-create your account. ## I did not set up sync, and now I have to reinstall my system! |
