mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(user): add email apis and new enums for metadata (#3053)
Co-authored-by: Rachit Naithani <rachit.naithani@juspay.in> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Rachit Naithani <81706961+racnan@users.noreply.github.com>
This commit is contained in:
@ -11,7 +11,7 @@ use router_env::logger;
|
||||
|
||||
use super::errors::{UserErrors, UserResponse};
|
||||
#[cfg(feature = "email")]
|
||||
use crate::services::email::types as email_types;
|
||||
use crate::services::email::{types as email_types, types::EmailToken};
|
||||
use crate::{
|
||||
consts,
|
||||
db::user::UserInterface,
|
||||
@ -235,8 +235,7 @@ pub async fn change_password(
|
||||
user.compare_password(request.old_password)
|
||||
.change_context(UserErrors::InvalidOldPassword)?;
|
||||
|
||||
let new_password_hash =
|
||||
crate::utils::user::password::generate_password_hash(request.new_password)?;
|
||||
let new_password_hash = utils::user::password::generate_password_hash(request.new_password)?;
|
||||
|
||||
let _ = UserInterface::update_user_by_user_id(
|
||||
&*state.store,
|
||||
@ -253,6 +252,182 @@ pub async fn change_password(
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn forgot_password(
|
||||
state: AppState,
|
||||
request: user_api::ForgotPasswordRequest,
|
||||
) -> UserResponse<()> {
|
||||
let user_email = domain::UserEmail::from_pii_email(request.email)?;
|
||||
|
||||
let user_from_db = state
|
||||
.store
|
||||
.find_user_by_email(user_email.get_secret().expose().as_str())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if e.current_context().is_db_not_found() {
|
||||
e.change_context(UserErrors::UserNotFound)
|
||||
} else {
|
||||
e.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
})
|
||||
.map(domain::UserFromStorage::from)?;
|
||||
|
||||
let email_contents = email_types::ResetPassword {
|
||||
recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?,
|
||||
settings: state.conf.clone(),
|
||||
user_name: domain::UserName::new(user_from_db.get_name())?,
|
||||
subject: "Get back to Hyperswitch - Reset Your Password Now",
|
||||
};
|
||||
|
||||
state
|
||||
.email_client
|
||||
.compose_and_send_email(
|
||||
Box::new(email_contents),
|
||||
state.conf.proxy.https_url.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.change_context(UserErrors::InternalServerError))?;
|
||||
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn reset_password(
|
||||
state: AppState,
|
||||
request: user_api::ResetPasswordRequest,
|
||||
) -> UserResponse<()> {
|
||||
let token = auth::decode_jwt::<EmailToken>(request.token.expose().as_str(), &state)
|
||||
.await
|
||||
.change_context(UserErrors::LinkInvalid)?;
|
||||
|
||||
let password = domain::UserPassword::new(request.password)?;
|
||||
|
||||
let hash_password = utils::user::password::generate_password_hash(password.get_secret())?;
|
||||
|
||||
//TODO: Create Update by email query
|
||||
let user_id = state
|
||||
.store
|
||||
.find_user_by_email(token.get_email())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.user_id;
|
||||
|
||||
state
|
||||
.store
|
||||
.update_user_by_user_id(
|
||||
user_id.as_str(),
|
||||
storage_user::UserUpdate::AccountUpdate {
|
||||
name: None,
|
||||
password: Some(hash_password),
|
||||
is_verified: Some(true),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
//TODO: Update User role status for invited user
|
||||
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn invite_user(
|
||||
state: AppState,
|
||||
request: user_api::InviteUserRequest,
|
||||
user_from_token: auth::UserFromToken,
|
||||
) -> UserResponse<user_api::InviteUserResponse> {
|
||||
let inviter_user = state
|
||||
.store
|
||||
.find_user_by_id(user_from_token.user_id.as_str())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
if inviter_user.email == request.email {
|
||||
return Err(UserErrors::InvalidRoleOperation.into())
|
||||
.attach_printable("User Inviting themself");
|
||||
}
|
||||
|
||||
utils::user_role::validate_role_id(request.role_id.as_str())?;
|
||||
let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?;
|
||||
|
||||
let invitee_user = state
|
||||
.store
|
||||
.find_user_by_email(invitee_email.clone().get_secret().expose().as_str())
|
||||
.await;
|
||||
|
||||
if let Ok(invitee_user) = invitee_user {
|
||||
let invitee_user_from_db = domain::UserFromStorage::from(invitee_user);
|
||||
|
||||
let now = common_utils::date_time::now();
|
||||
use diesel_models::user_role::UserRoleNew;
|
||||
state
|
||||
.store
|
||||
.insert_user_role(UserRoleNew {
|
||||
user_id: invitee_user_from_db.get_user_id().to_owned(),
|
||||
merchant_id: user_from_token.merchant_id,
|
||||
role_id: request.role_id,
|
||||
org_id: user_from_token.org_id,
|
||||
status: UserStatus::Active,
|
||||
created_by: user_from_token.user_id.clone(),
|
||||
last_modified_by: user_from_token.user_id,
|
||||
created_at: now,
|
||||
last_modified_at: now,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if e.current_context().is_db_unique_violation() {
|
||||
e.change_context(UserErrors::UserExists)
|
||||
} else {
|
||||
e.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(ApplicationResponse::Json(user_api::InviteUserResponse {
|
||||
is_email_sent: false,
|
||||
}))
|
||||
} else if invitee_user
|
||||
.as_ref()
|
||||
.map_err(|e| e.current_context().is_db_not_found())
|
||||
.err()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let new_user = domain::NewUser::try_from((request.clone(), user_from_token))?;
|
||||
|
||||
new_user
|
||||
.insert_user_in_db(state.store.as_ref())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
new_user
|
||||
.clone()
|
||||
.insert_user_role_in_db(state.clone(), request.role_id, UserStatus::InvitationSent)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
let email_contents = email_types::InviteUser {
|
||||
recipient_email: invitee_email,
|
||||
user_name: domain::UserName::new(new_user.get_name())?,
|
||||
settings: state.conf.clone(),
|
||||
subject: "You have been invited to join Hyperswitch Community!",
|
||||
};
|
||||
|
||||
let send_email_result = state
|
||||
.email_client
|
||||
.compose_and_send_email(
|
||||
Box::new(email_contents),
|
||||
state.conf.proxy.https_url.as_ref(),
|
||||
)
|
||||
.await;
|
||||
|
||||
logger::info!(?send_email_result);
|
||||
|
||||
Ok(ApplicationResponse::Json(user_api::InviteUserResponse {
|
||||
is_email_sent: send_email_result.is_ok(),
|
||||
}))
|
||||
} else {
|
||||
Err(UserErrors::InternalServerError.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_internal_user(
|
||||
state: AppState,
|
||||
request: user_api::CreateInternalUserRequest,
|
||||
|
||||
Reference in New Issue
Block a user