diff --git a/src/accounts.rs b/src/accounts.rs index cda26f7..7b3731a 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -105,4 +105,15 @@ impl Account { Err(_) => Ok(false), } } + + pub async fn delete(&self, pool: &PgPool) -> Result<()> { + sqlx::query!(r#"DELETE FROM AuthTokens WHERE account = $1;"#, self.id) + .execute(pool) + .await?; + + sqlx::query!(r#"DELETE FROM Accounts WHERE id = $1;"#, self.id) + .execute(pool) + .await?; + Ok(()) + } } diff --git a/src/api/account/handlers.rs b/src/api/account/handlers.rs index 4809625..f8f040b 100644 --- a/src/api/account/handlers.rs +++ b/src/api/account/handlers.rs @@ -41,19 +41,34 @@ pub async fn register( return Ok(data::RegisterResponse::MalformedEmail); } - if Account::from_username(pool, &request.username) - .await? - .is_some() - { - return Ok(data::RegisterResponse::Conflict( - data::RegisterConflict::Username, - )); + // Check if the usernam is already taken + if let Some(account) = Account::from_username(pool, &request.username).await? { + // Check if the account that has taken the username is verified or has an open verification request. + if account.verified + || VerificationToken::from_id(pool, account.id) + .await? + .is_some() + { + return Ok(data::RegisterResponse::Conflict( + data::RegisterConflict::Username, + )); + } + // The found account is a Zombie account, we can delete it and continue. + account.delete(pool).await?; } - if Account::from_email(pool, &request.email).await?.is_some() { - return Ok(data::RegisterResponse::Conflict( - data::RegisterConflict::Email, - )); + // The same stuff for the email + if let Some(account) = Account::from_email(pool, &request.email).await? { + if account.verified + || VerificationToken::from_id(pool, account.id) + .await? + .is_some() + { + return Ok(data::RegisterResponse::Conflict( + data::RegisterConflict::Email, + )); + } + account.delete(pool).await?; } let account = Account::new(pool, &request.username, &request.email, &request.password).await?; @@ -137,16 +152,14 @@ pub async fn delete(pool: &PgPool, auth: String) -> Result None => return Ok(data::DeleteResponse::Unauthorized), }; - sqlx::query!( - r#"DELETE FROM AuthTokens WHERE account = $1;"#, - token.account - ) - .execute(pool) - .await?; - - sqlx::query!(r#"DELETE FROM Accounts WHERE id = $1;"#, token.account) - .execute(pool) - .await?; + match Account::from_id(pool, token.account).await? { + Some(a) => a.delete(pool).await?, + None => { + return Err(anyhow::Error::msg( + "Failed to delete account. Account not found in database", + )) + } + } Ok(data::DeleteResponse::Success) } diff --git a/src/tokens.rs b/src/tokens.rs index 5fba91f..e86e3cf 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -156,6 +156,29 @@ impl VerificationToken { } } + pub async fn from_id(pool: &PgPool, account_id: i64) -> Result> { + let query_result = sqlx::query_as!( + Self, + r#"SELECT * FROM VerificationTokens WHERE account = $1;"#, + account_id + ) + .fetch_one(pool) + .await; + + match query_result { + Ok(token) => { + if token.expire.timestamp() > chrono::Utc::now().timestamp() { + Ok(Some(token)) + } else { + token.delete(pool).await?; + Ok(None) + } + } + Err(sqlx::Error::RowNotFound) => Ok(None), + Err(e) => Err(Error::new(e)), + } + } + pub async fn delete(&self, pool: &PgPool) -> Result<()> { sqlx::query!( r#"DELETE FROM VerificationTokens WHERE token = $1;"#,