chore: cleaning code

This commit is contained in:
Maulana Sodiqin
2025-05-20 00:26:24 +07:00
parent e48507901e
commit d97095a38d
16 changed files with 139 additions and 251 deletions
+16 -46
View File
@@ -1,25 +1,29 @@
use crate::RolesItemDto;
use lazy_static::lazy_static;
use regex::Regex;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use validator::{Validate, ValidationError};
use crate::UsersDetailItemDto;
lazy_static! {
static ref PASSWORD_REGEX: Regex = Regex::new(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$").unwrap();
static ref PASSWORD_REGEX: Regex = Regex::new(
r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$"
)
.unwrap();
}
fn validate_password_complexity(password: &str) -> Result<(), ValidationError> {
let has_uppercase = password.chars().any(|c| c.is_ascii_uppercase());
let has_lowercase = password.chars().any(|c| c.is_ascii_lowercase());
let has_digit = password.chars().any(|c| c.is_ascii_digit());
let has_special = password.chars().any(|c| "@$!%*?&".contains(c));
let has_uppercase = password.chars().any(|c| c.is_ascii_uppercase());
let has_lowercase = password.chars().any(|c| c.is_ascii_lowercase());
let has_digit = password.chars().any(|c| c.is_ascii_digit());
let has_special = password.chars().any(|c| "@$!%*?&".contains(c));
if has_uppercase && has_lowercase && has_digit && has_special {
Ok(())
} else {
Err(ValidationError::new("complexity"))
}
if has_uppercase && has_lowercase && has_digit && has_special {
Ok(())
} else {
Err(ValidationError::new("complexity"))
}
}
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
@@ -33,23 +37,10 @@ pub struct AuthLoginRequestDto {
pub password: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
pub struct AuthUserItemDto {
pub id: String,
pub role: RolesItemDto,
pub fullname: String,
pub email: String,
pub avatar: Option<String>,
pub phone_number: String,
pub is_active: bool,
pub gender: Option<String>,
pub birthdate: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
pub struct AuthLoginResponsetDto {
pub token: TokenDto,
pub user: AuthUserItemDto,
pub user: UsersDetailItemDto,
}
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
@@ -77,28 +68,7 @@ pub struct AuthRegisterRequestDto {
#[validate(length(min = 2, message = "Fullname at least have 2 character"))]
pub fullname: String,
#[validate(length(min = 1, message = "Student type is required"))]
pub student_type: String,
#[validate(length(
min = 10,
message = "Phone number at least have 10 character"
))]
pub phone_number: String,
#[validate(length(
max = 4,
message = "Referal code cannot be more than 4 character"
))]
pub referral_code: Option<String>,
pub referred_by: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
pub struct AuthActiveInactiveRequestDto {
pub is_active: bool,
#[validate(
length(min = 1, message = "Email cannot be empty"),
email(message = "Email not valid")
)]
pub email: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
+8 -8
View File
@@ -1,6 +1,6 @@
use super::AuthOtpSchema;
use crate::{make_thing, AppState, ResourceEnum, UsersItemDtoRaw};
use anyhow::{anyhow, bail, Result};
use crate::{AppState, ResourceEnum, UsersDetailQueryDto, make_thing};
use anyhow::{Result, anyhow, bail};
use chrono::{Duration, Utc};
pub struct AuthRepository<'a> {
@@ -12,7 +12,7 @@ impl<'a> AuthRepository<'a> {
Self { state }
}
pub async fn query_store_user(&self, user: UsersItemDtoRaw) -> Result<String> {
pub async fn query_store_user(&self, user: UsersDetailQueryDto) -> Result<String> {
if user.email.trim().is_empty() {
bail!("Email is required");
}
@@ -22,11 +22,11 @@ impl<'a> AuthRepository<'a> {
let _ = self
.state
.surrealdb_mem
.delete::<Option<UsersItemDtoRaw>>((table.clone(), user_id.clone()))
.delete::<Option<UsersDetailQueryDto>>((table.clone(), user_id.clone()))
.await?;
let mut user_to_store = user.clone();
user_to_store.id = id.clone();
let record: Option<UsersItemDtoRaw> = self
let record: Option<UsersDetailQueryDto> = self
.state
.surrealdb_mem
.create((table, user_id))
@@ -41,8 +41,8 @@ impl<'a> AuthRepository<'a> {
pub async fn query_get_stored_user(
&self,
email: String,
) -> Result<UsersItemDtoRaw> {
let user: Option<UsersItemDtoRaw> = self
) -> Result<UsersDetailQueryDto> {
let user: Option<UsersDetailQueryDto> = self
.state
.surrealdb_mem
.select((ResourceEnum::UsersCache.to_string(), email))
@@ -54,7 +54,7 @@ impl<'a> AuthRepository<'a> {
}
pub async fn query_delete_stored_user(&self, email: String) -> Result<String> {
let record: Option<UsersItemDtoRaw> = self
let record: Option<UsersDetailQueryDto> = self
.state
.surrealdb_mem
.delete((ResourceEnum::UsersCache.to_string(), email))
+48 -52
View File
@@ -1,15 +1,15 @@
use super::{
AuthLoginRequestDto, AuthLoginResponsetDto, AuthNewPasswordRequestDto,
AuthRefreshTokenRequestDto, AuthRegisterRequestDto, AuthRepository,
AuthResendOtpRequestDto, AuthUserItemDto, AuthVerifyEmailRequestDto, TokenDto,
AuthResendOtpRequestDto, AuthVerifyEmailRequestDto, TokenDto,
};
use crate::{
common_response, decode_refresh_token, encode_access_token, encode_refresh_token,
AppState, Env, ResourceEnum, ResponseSuccessDto, RolesEnum, RolesRepository,
UsersDetailItemDto, UsersRepository, UsersSchema, common_response,
decode_refresh_token, encode_access_token, encode_refresh_token,
encode_reset_password_token, extract_email_token, generate_otp, get_iso_date,
hash_password, make_thing, send_email, success_response, validate_request,
verify_password, AppState, Env, ResourceEnum, ResponseSuccessDto, RolesEnum,
RolesItemDto, RolesRepository, UsersActiveInactiveSchema, UsersRepository,
UsersSchema, UsersSetNewPasswordSchema,
verify_password,
};
use axum::{http::StatusCode, response::Response};
use surrealdb::Uuid;
@@ -27,7 +27,6 @@ impl AuthService {
let user_repo = UsersRepository::new(state);
let auth_repo = AuthRepository::new(state);
let role_repo = RolesRepository::new(state);
match user_repo.query_user_by_email(payload.email.clone()).await {
Ok(user) => {
@@ -54,7 +53,7 @@ impl AuthService {
return common_response(
StatusCode::INTERNAL_SERVER_ERROR,
"Failed to generate access token",
)
);
}
};
@@ -64,34 +63,13 @@ impl AuthService {
return common_response(
StatusCode::INTERNAL_SERVER_ERROR,
"Failed to generate refresh token",
)
);
}
};
let role_response = role_repo
.query_role_by_id(user.role.id.id.to_raw())
.await
.unwrap();
let response = ResponseSuccessDto {
data: AuthLoginResponsetDto {
user: AuthUserItemDto {
id: user.id.id.to_raw(),
fullname: user.fullname.clone(),
email: user.email.clone(),
is_active: user.is_active.clone(),
avatar: user.avatar.clone(),
phone_number: user.phone_number.clone(),
gender: user.gender.clone(),
birthdate: user.birthdate.clone(),
role: RolesItemDto {
id: role_response.id,
name: role_response.name,
permissions: role_response.permissions,
created_at: role_response.created_at,
updated_at: role_response.updated_at,
},
},
user: UsersDetailItemDto::from(&user),
token: TokenDto {
access_token,
refresh_token,
@@ -116,6 +94,7 @@ impl AuthService {
if let Err((status, message)) = validate_request(&payload) {
return common_response(status, &message);
}
let user_repo = UsersRepository::new(state);
let auth_repo = AuthRepository::new(state);
let role_repo = RolesRepository::new(state);
@@ -126,6 +105,7 @@ impl AuthService {
Ok(role) => role,
Err(_) => return common_response(StatusCode::BAD_REQUEST, "Role Not Found"),
};
if user_repo
.query_user_by_email(payload.email.clone())
.await
@@ -133,6 +113,7 @@ impl AuthService {
{
return common_response(StatusCode::BAD_REQUEST, "User already exists");
}
let hashed_password = match hash_password(&payload.password) {
Ok(hash) => hash,
Err(_) => {
@@ -142,16 +123,16 @@ impl AuthService {
);
}
};
let new_user = AuthRegisterRequestDto {
email: payload.email,
password: hashed_password,
fullname: payload.fullname,
student_type: payload.student_type,
phone_number: payload.phone_number,
referral_code: payload.referral_code,
referred_by: payload.referred_by,
};
let otp = generate_otp::OtpManager::generate_otp();
match auth_repo
.query_store_otp(new_user.email.clone(), otp.clone())
.await
@@ -169,11 +150,13 @@ impl AuthService {
return common_response(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string());
}
}
let role_thing = make_thing(&ResourceEnum::Roles.to_string(), &role.id);
let user_thing = make_thing(
&ResourceEnum::Users.to_string(),
&Uuid::new_v4().to_string(),
);
match user_repo
.query_create_user(UsersSchema {
id: user_thing,
@@ -202,14 +185,18 @@ impl AuthService {
if let Err((status, message)) = validate_request(&payload) {
return common_response(status, &message);
}
let user_repo = UsersRepository::new(state);
if user_repo.query_user_by_email(payload.email.clone()).await.is_err() {
if user_repo
.query_user_by_email(payload.email.clone())
.await
.is_err()
{
return common_response(StatusCode::BAD_REQUEST, "User not found");
}
let auth_repo = AuthRepository::new(state);
let _ = auth_repo.query_get_stored_otp(payload.email.clone()).await;
let _ = auth_repo.query_get_stored_otp(payload.email.clone()).await;
let otp = generate_otp::OtpManager::generate_otp();
let message = format!("Your OTP code is {}", otp);
@@ -273,10 +260,10 @@ impl AuthService {
let user = match user_result {
Ok(user) => user,
Err(err) if err.to_string().contains("User not found") => {
return common_response(StatusCode::BAD_REQUEST, "User not found")
return common_response(StatusCode::BAD_REQUEST, "User not found");
}
Err(err) => {
return common_response(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string())
return common_response(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string());
}
};
let token = match encode_reset_password_token(user.email) {
@@ -285,7 +272,7 @@ impl AuthService {
return common_response(
StatusCode::INTERNAL_SERVER_ERROR,
"Failed to generate access token",
)
);
}
};
let env = Env::new();
@@ -310,15 +297,18 @@ impl AuthService {
let user_repo = UsersRepository::new(state);
let auth_repo = AuthRepository::new(state);
let email = payload.email.clone();
let user = match user_repo.query_user_by_email(email.clone()).await {
Ok(user) if !user.is_deleted => user,
_ => return common_response(StatusCode::NOT_FOUND, "User not found"),
};
let patch = UsersSchema {
id: user.id.clone(),
is_active: true,
..UsersSchema::from(user)
};
match auth_repo.query_get_stored_otp(email.clone()).await {
Ok(stored_otp) => match stored_otp == payload.otp {
true => match user_repo
.query_active_inactive_user(
email.clone(),
UsersActiveInactiveSchema { is_active: true },
)
.await
{
true => match user_repo.query_update_user(patch).await {
Ok(_) => match auth_repo.query_delete_stored_otp(email).await {
Ok(_) => common_response(StatusCode::OK, "Email verified successfully"),
Err(e) => {
@@ -346,7 +336,7 @@ impl AuthService {
if let Err((status, message)) = validate_request(&payload) {
return common_response(status, &message);
}
let user_repo = UsersRepository::new(state);
let repo = UsersRepository::new(state);
let email = match extract_email_token(payload.token.clone()) {
Some(email) => email,
None => {
@@ -362,12 +352,18 @@ impl AuthService {
);
}
};
match user_repo
.query_update_password_user(email, UsersSetNewPasswordSchema { password })
.await
{
let user = match repo.query_user_by_email(email.clone()).await {
Ok(user) if !user.is_deleted => user,
_ => return common_response(StatusCode::NOT_FOUND, "User not found"),
};
let patch = UsersSchema {
id: user.id.clone(),
password,
..Default::default()
};
match repo.query_update_user(patch).await {
Ok(msg) => common_response(StatusCode::OK, &msg),
Err(err) => common_response(StatusCode::BAD_REQUEST, &err.to_string()),
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
}
}
}
@@ -1,9 +1,9 @@
use super::{PermissionsItemDto, PermissionsItemDtoRaw, PermissionsSchema};
use super::{PermissionsItemDto, PermissionsSchema};
use crate::{
get_id, make_thing, query_list_with_meta, AppState, MetaRequestDto, ResourceEnum,
ResponseListSuccessDto,
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id,
make_thing, query_list_with_meta,
};
use anyhow::{bail, Result};
use anyhow::{Result, bail};
use imphnen_utils::extract_id;
pub struct PermissionsRepository<'a> {
@@ -27,14 +27,17 @@ impl<'a> PermissionsRepository<'a> {
let filter_by = meta.filter_by.as_ref().unwrap();
conditions.push(format!("{} = $filter", filter_by));
}
let raw_result: ResponseListSuccessDto<Vec<PermissionsItemDtoRaw>> = query_list_with_meta(
&self.state.surrealdb_ws,
&ResourceEnum::Permissions.to_string(),
&meta,
conditions,
None,
)
.await?;
let raw_result: ResponseListSuccessDto<Vec<PermissionsSchema>> =
query_list_with_meta(
&self.state.surrealdb_ws,
&ResourceEnum::Permissions.to_string(),
&meta,
conditions,
None,
"name",
None,
)
.await?;
let transformed_data = raw_result
.data
.into_iter()
@@ -69,9 +72,7 @@ impl<'a> PermissionsRepository<'a> {
&self,
id: String,
) -> Result<PermissionsItemDto> {
let raw_result = self
.query_permission_by_id(id.clone())
.await?;
let raw_result = self.query_permission_by_id(id.clone()).await?;
let transformed_data = PermissionsItemDto {
id: extract_id(&raw_result.id),
name: raw_result.name,
@@ -81,7 +82,6 @@ impl<'a> PermissionsRepository<'a> {
Ok(transformed_data)
}
pub async fn query_permission_by_name(
&self,
name: String,
@@ -1,6 +1,6 @@
use crate::{make_thing, ResourceEnum};
use crate::{ResourceEnum, make_thing};
use serde::{Deserialize, Serialize};
use surrealdb::{sql::Thing, Uuid};
use surrealdb::{Uuid, sql::Thing};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PermissionsSchema {
+11 -11
View File
@@ -95,19 +95,19 @@ pub struct UsersDetailItemDto {
}
impl UsersDetailItemDto {
pub fn from(dto: UsersDetailQueryDto) -> Self {
pub fn from(dto: &UsersDetailQueryDto) -> Self {
Self {
id: dto.id.id.to_raw(),
id: dto.id.id.to_raw().clone(),
role: RolesDetailItemDto::from(&dto.role),
fullname: dto.fullname,
email: dto.email,
avatar: dto.avatar,
phone_number: dto.phone_number,
is_active: dto.is_active,
gender: dto.gender,
birthdate: dto.birthdate,
created_at: dto.created_at,
updated_at: dto.updated_at,
fullname: dto.fullname.clone(),
email: dto.email.clone(),
avatar: dto.avatar.clone(),
phone_number: dto.phone_number.clone(),
is_active: dto.is_active.clone(),
gender: dto.gender.clone(),
birthdate: dto.birthdate.clone(),
created_at: dto.created_at.clone(),
updated_at: dto.updated_at.clone(),
}
}
}
+2 -2
View File
@@ -34,7 +34,7 @@ impl UsersService {
let repo = UsersRepository::new(state);
match repo.query_user_by_id(id).await {
Ok(user) if !user.is_deleted => success_response(ResponseSuccessDto {
data: UsersDetailItemDto::from(user),
data: UsersDetailItemDto::from(&user),
}),
Ok(_) => common_response(StatusCode::NOT_FOUND, "User not found"),
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
@@ -49,7 +49,7 @@ impl UsersService {
};
match repo.query_user_by_email(email).await {
Ok(user) if !user.is_deleted => success_response(ResponseSuccessDto {
data: UsersDetailItemDto::from(user),
data: UsersDetailItemDto::from(&user),
}),
Ok(_) => common_response(StatusCode::NOT_FOUND, "User not found"),
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),