feat: setup data
This commit is contained in:
@@ -3,7 +3,7 @@ use super::{
|
||||
AuthVerifyEmailRequestDto,
|
||||
};
|
||||
use crate::{v1::AuthLoginResponsetDto, AppState};
|
||||
use crate::{MessageResponseDto, ResponseSuccessDto};
|
||||
use crate::{AuthNewPasswordRequestDto, MessageResponseDto, ResponseSuccessDto};
|
||||
use axum::{response::IntoResponse, Extension, Json};
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -90,3 +90,20 @@ pub async fn post_forgot_password(
|
||||
) -> impl IntoResponse {
|
||||
AuthService::mutation_forgot_password(payload, &state).await
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/v1/auth/new-password",
|
||||
request_body = AuthNewPasswordRequestDto,
|
||||
responses(
|
||||
(status = 200, description = "New password request successful", body = MessageResponseDto),
|
||||
(status = 401, description = "New password request failed", body = MessageResponseDto)
|
||||
),
|
||||
tag = "Authentication"
|
||||
)]
|
||||
pub async fn post_new_password(
|
||||
Extension(state): Extension<AppState>,
|
||||
Json(payload): Json<AuthNewPasswordRequestDto>,
|
||||
) -> impl IntoResponse {
|
||||
AuthService::mutation_new_password(payload, &state).await
|
||||
}
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
use crate::UsersItemDto;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
use validator::Validate;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
lazy_static! {
|
||||
static ref PASSWORD_REGEX: Regex = Regex::new(
|
||||
r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct AuthLoginRequestDto {
|
||||
#[validate(
|
||||
length(min = 1, message = "Email cannot be empty"),
|
||||
email(message = "Email not valid")
|
||||
)]
|
||||
pub email: String,
|
||||
#[validate(length(min = 1, message = "Password cannot be empty"))]
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
@@ -20,46 +35,96 @@ pub struct TokenDto {
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct AuthRegisterRequestDto {
|
||||
#[validate(
|
||||
length(min = 1, message = "Email cannot be empty"),
|
||||
email(message = "Email not valid")
|
||||
)]
|
||||
pub email: String,
|
||||
#[validate(length(
|
||||
min = 8,
|
||||
message = "Password must have at least 8 characters"
|
||||
))]
|
||||
#[validate(regex(
|
||||
path = "PASSWORD_REGEX",
|
||||
message = "Password must include uppercase, lowercase, number, and special character"
|
||||
))]
|
||||
pub password: String,
|
||||
#[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)]
|
||||
#[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)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct AuthVerifyEmailRequestDto {
|
||||
#[validate(
|
||||
length(min = 1, message = "Email cannot be empty"),
|
||||
email(message = "Email not valid")
|
||||
)]
|
||||
pub email: String,
|
||||
pub otp: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct AuthResendOtpRequestDto {
|
||||
#[validate(
|
||||
length(min = 1, message = "Email cannot be empty"),
|
||||
email(message = "Email not valid")
|
||||
)]
|
||||
pub email: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct AuthNewPasswordRequestDto {
|
||||
pub token: String,
|
||||
#[validate(length(
|
||||
min = 8,
|
||||
message = "Password must have at least 8 characters"
|
||||
))]
|
||||
#[validate(regex(
|
||||
path = "PASSWORD_REGEX",
|
||||
message = "Password must include uppercase, lowercase, number, and special character"
|
||||
))]
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct AuthSetNewPasswordRequestDto {
|
||||
#[validate(
|
||||
length(min = 1, message = "Email cannot be empty"),
|
||||
email(message = "Email not valid")
|
||||
)]
|
||||
pub email: String,
|
||||
#[validate(length(
|
||||
min = 8,
|
||||
message = "Password must have at least 8 characters"
|
||||
))]
|
||||
#[validate(regex(
|
||||
path = "PASSWORD_REGEX",
|
||||
message = "Password must include uppercase, lowercase, number, and special character"
|
||||
))]
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct AuthQueryByEmailResponseDto {
|
||||
pub email: String,
|
||||
pub fullname: String,
|
||||
pub password: String,
|
||||
pub is_active: bool,
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
use super::{
|
||||
AuthLoginRequestDto, AuthLoginResponsetDto, AuthRegisterRequestDto,
|
||||
AuthRepository, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto, TokenDto,
|
||||
AuthLoginRequestDto, AuthLoginResponsetDto, AuthNewPasswordRequestDto,
|
||||
AuthRegisterRequestDto, AuthRepository, AuthResendOtpRequestDto,
|
||||
AuthVerifyEmailRequestDto, TokenDto,
|
||||
};
|
||||
use crate::{
|
||||
common_response, encode_access_token, encode_refresh_token, generate_otp,
|
||||
hash_password, send_email, success_response, verify_password, AppState, Env,
|
||||
common_response, encode_access_token, encode_refresh_token, extract_email_token,
|
||||
generate_otp, get_iso_date, hash_password, send_email, success_response,
|
||||
validate_request, verify_password, AppState, Env, ResourceEnum,
|
||||
ResponseSuccessDto, UsersActiveInactiveSchema, UsersItemDto, UsersRepository,
|
||||
UsersSchema,
|
||||
};
|
||||
use axum::{http::StatusCode, response::Response};
|
||||
use surrealdb::sql::{Id, Thing};
|
||||
|
||||
pub struct AuthService;
|
||||
|
||||
@@ -17,6 +20,10 @@ impl AuthService {
|
||||
payload: AuthLoginRequestDto,
|
||||
state: &AppState,
|
||||
) -> Response {
|
||||
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);
|
||||
|
||||
@@ -112,6 +119,10 @@ impl AuthService {
|
||||
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();
|
||||
@@ -124,12 +135,30 @@ impl AuthService {
|
||||
|
||||
send_email(&new_user.email.clone(), "OTP Verification", &message).unwrap();
|
||||
|
||||
let role_thing =
|
||||
Thing::from((ResourceEnum::Roles.to_string(), Id::String("".to_string())));
|
||||
|
||||
match user_repo
|
||||
.query_create_user(UsersSchema {
|
||||
id: Some("".to_string()),
|
||||
email: new_user.email.clone(),
|
||||
fullname: new_user.fullname.clone(),
|
||||
password: new_user.password.clone(),
|
||||
is_active: false,
|
||||
role_id: "".to_string(),
|
||||
avatar: Some("".to_string()),
|
||||
phone_number: new_user.phone_number.clone(),
|
||||
referral_code: new_user.referral_code.clone(),
|
||||
referred_by: new_user.referred_by.clone(),
|
||||
identity_number: Some("".to_string()),
|
||||
student_type: new_user.student_type.clone(),
|
||||
religion: Some("".to_string()),
|
||||
gender: Some("".to_string()),
|
||||
birthdate: Some("".to_string()),
|
||||
is_profile_completed: Some(false),
|
||||
created_at: Some(get_iso_date()),
|
||||
updated_at: Some(get_iso_date()),
|
||||
role: role_thing,
|
||||
})
|
||||
.await
|
||||
{
|
||||
@@ -235,4 +264,23 @@ impl AuthService {
|
||||
Err(err) => common_response(StatusCode::BAD_REQUEST, &err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mutation_new_password(
|
||||
payload: AuthNewPasswordRequestDto,
|
||||
state: &AppState,
|
||||
) -> Response {
|
||||
let user_repo = UsersRepository::new(state);
|
||||
let email = extract_email_token(payload.token).unwrap();
|
||||
|
||||
match user_repo
|
||||
.query_update_password_user(crate::UsersSetNewPasswordSchema {
|
||||
email: email.clone(),
|
||||
password: payload.password.clone(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(msg) => common_response(StatusCode::OK, &msg),
|
||||
Err(err) => common_response(StatusCode::BAD_REQUEST, &err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@ pub fn auth_router() -> Router {
|
||||
.route("/verify", post(auth_controller::post_verify_email))
|
||||
.route("/resend", post(auth_controller::post_resend_otp))
|
||||
.route("/forgot", post(auth_controller::post_forgot_password))
|
||||
.route("/new-password", post(auth_controller::post_new_password))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ use crate::{
|
||||
auth, AuthLoginRequestDto, AuthLoginResponsetDto, AuthResendOtpRequestDto,
|
||||
AuthVerifyEmailRequestDto,
|
||||
},
|
||||
MessageResponseDto, MetaRequestDto, MetaResponseDto, ResponseSuccessDto,
|
||||
AuthNewPasswordRequestDto, MessageResponseDto, MetaRequestDto, MetaResponseDto,
|
||||
ResponseSuccessDto,
|
||||
};
|
||||
|
||||
use utoipa::{
|
||||
@@ -17,7 +18,8 @@ use utoipa::{
|
||||
auth::auth_controller::post_login,
|
||||
auth::auth_controller::post_register,
|
||||
auth::auth_controller::post_verify_email,
|
||||
auth::auth_controller::post_resend_otp
|
||||
auth::auth_controller::post_resend_otp,
|
||||
auth::auth_controller::post_new_password
|
||||
),
|
||||
components(
|
||||
schemas(
|
||||
@@ -28,12 +30,13 @@ use utoipa::{
|
||||
AuthLoginResponsetDto,
|
||||
AuthVerifyEmailRequestDto,
|
||||
AuthResendOtpRequestDto,
|
||||
AuthNewPasswordRequestDto,
|
||||
ResponseSuccessDto<AuthLoginResponsetDto>,
|
||||
)
|
||||
),
|
||||
info(
|
||||
title = "Axum SurrealDB Boilerplate",
|
||||
description = "Axum SurrealDB Documentation",
|
||||
title = "NAJM Course API",
|
||||
description = "NAJM Course API",
|
||||
version = "0.1.0",
|
||||
contact(
|
||||
name = "Maulana Sodiqin",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::{UsersActiveInactiveSchema, UsersSetNewPasswordSchema};
|
||||
use crate::{v1::users_schema::UsersSchema, AppState, ResourceEnum};
|
||||
use super::{UsersActiveInactiveSchema, UsersSchema, UsersSetNewPasswordSchema};
|
||||
use crate::{get_iso_date, AppState, ResourceEnum};
|
||||
use anyhow::{bail, Result};
|
||||
use surrealdb::Uuid;
|
||||
|
||||
pub struct UsersRepository<'a> {
|
||||
state: &'a AppState,
|
||||
@@ -25,14 +26,31 @@ impl<'a> UsersRepository<'a> {
|
||||
}
|
||||
|
||||
pub async fn query_create_user(&self, data: UsersSchema) -> Result<String> {
|
||||
let id = Uuid::new_v4().to_string();
|
||||
let db = &self.state.surrealdb;
|
||||
|
||||
let record: Option<UsersSchema> = db
|
||||
.create((ResourceEnum::Users.to_string(), &data.email))
|
||||
.create((ResourceEnum::Users.to_string(), &id))
|
||||
.content(UsersSchema {
|
||||
id: Some(id.clone()),
|
||||
role_id: data.role_id.clone(),
|
||||
fullname: data.fullname.clone(),
|
||||
email: data.email.clone(),
|
||||
password: data.password.clone(),
|
||||
is_active: false,
|
||||
avatar: data.avatar.clone(),
|
||||
phone_number: data.phone_number.clone(),
|
||||
referral_code: data.referral_code.clone(),
|
||||
referred_by: data.referred_by.clone(),
|
||||
identity_number: data.identity_number.clone(),
|
||||
student_type: data.student_type.clone(),
|
||||
religion: data.religion.clone(),
|
||||
gender: data.gender.clone(),
|
||||
birthdate: data.birthdate.clone(),
|
||||
is_active: data.is_active.clone(),
|
||||
is_profile_completed: data.is_profile_completed.clone(),
|
||||
role: data.role.clone(),
|
||||
created_at: Some(get_iso_date()),
|
||||
updated_at: Some(get_iso_date()),
|
||||
})
|
||||
.await?;
|
||||
match record {
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use surrealdb::sql::Thing;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct UsersSchema {
|
||||
pub email: String,
|
||||
pub id: Option<String>,
|
||||
pub role_id: String,
|
||||
pub fullname: String,
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
pub avatar: Option<String>,
|
||||
pub phone_number: String,
|
||||
pub referral_code: Option<String>,
|
||||
pub referred_by: Option<String>,
|
||||
pub identity_number: Option<String>,
|
||||
pub is_active: bool,
|
||||
pub student_type: String,
|
||||
pub religion: Option<String>,
|
||||
pub gender: Option<String>,
|
||||
pub birthdate: Option<String>,
|
||||
pub is_profile_completed: Option<bool>,
|
||||
pub role: Thing,
|
||||
pub created_at: Option<String>,
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct UsersSetNewPasswordSchema {
|
||||
pub email: String,
|
||||
pub password: bool,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user