feat: setup data
This commit is contained in:
Generated
+97
-2
@@ -1733,6 +1733,17 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||||
|
dependencies = [
|
||||||
|
"matches",
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@@ -1754,6 +1765,12 @@ dependencies = [
|
|||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "if_chain"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
@@ -1902,7 +1919,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"hostname",
|
"hostname",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"idna",
|
"idna 1.0.3",
|
||||||
"mime",
|
"mime",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"nom",
|
"nom",
|
||||||
@@ -2017,6 +2034,12 @@ dependencies = [
|
|||||||
"tendril",
|
"tendril",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matches"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@@ -2134,10 +2157,12 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"chrono",
|
"chrono",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
|
"lazy_static",
|
||||||
"lettre",
|
"lettre",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
"redis",
|
"redis",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"surrealdb",
|
"surrealdb",
|
||||||
@@ -2146,6 +2171,7 @@ dependencies = [
|
|||||||
"tower-http",
|
"tower-http",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"utoipa-swagger-ui",
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2598,6 +2624,30 @@ dependencies = [
|
|||||||
"toml_edit",
|
"toml_edit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.94"
|
version = "1.0.94"
|
||||||
@@ -4259,6 +4309,12 @@ version = "2.8.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@@ -4315,7 +4371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna 1.0.3",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4397,6 +4453,45 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "validator"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d6937c33ec6039d8071bcf72933146b5bbe378d645d8fa59bdadabfc2a249"
|
||||||
|
dependencies = [
|
||||||
|
"idna 0.2.3",
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"url",
|
||||||
|
"validator_derive",
|
||||||
|
"validator_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "validator_derive"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4286b4497f270f59276a89ae0ad109d5f8f18c69b613e3fb22b61201aadb0c4d"
|
||||||
|
dependencies = [
|
||||||
|
"if_chain",
|
||||||
|
"lazy_static",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"validator_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "validator_types"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vart"
|
name = "vart"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ thiserror = "2.0.11"
|
|||||||
anyhow = "1.0.97"
|
anyhow = "1.0.97"
|
||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
tower-http = { version = "0.6.2", features = ["cors"] }
|
tower-http = { version = "0.6.2", features = ["cors"] }
|
||||||
|
validator = { version = "0.12", features = ["derive"] }
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
regex = "1.11.1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use super::{
|
|||||||
AuthVerifyEmailRequestDto,
|
AuthVerifyEmailRequestDto,
|
||||||
};
|
};
|
||||||
use crate::{v1::AuthLoginResponsetDto, AppState};
|
use crate::{v1::AuthLoginResponsetDto, AppState};
|
||||||
use crate::{MessageResponseDto, ResponseSuccessDto};
|
use crate::{AuthNewPasswordRequestDto, MessageResponseDto, ResponseSuccessDto};
|
||||||
use axum::{response::IntoResponse, Extension, Json};
|
use axum::{response::IntoResponse, Extension, Json};
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -90,3 +90,20 @@ pub async fn post_forgot_password(
|
|||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
AuthService::mutation_forgot_password(payload, &state).await
|
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 crate::UsersItemDto;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use utoipa::ToSchema;
|
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 {
|
pub struct AuthLoginRequestDto {
|
||||||
|
#[validate(
|
||||||
|
length(min = 1, message = "Email cannot be empty"),
|
||||||
|
email(message = "Email not valid")
|
||||||
|
)]
|
||||||
pub email: String,
|
pub email: String,
|
||||||
|
#[validate(length(min = 1, message = "Password cannot be empty"))]
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,46 +35,96 @@ pub struct TokenDto {
|
|||||||
pub refresh_token: String,
|
pub refresh_token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct AuthRegisterRequestDto {
|
pub struct AuthRegisterRequestDto {
|
||||||
|
#[validate(
|
||||||
|
length(min = 1, message = "Email cannot be empty"),
|
||||||
|
email(message = "Email not valid")
|
||||||
|
)]
|
||||||
pub email: String,
|
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,
|
pub password: String,
|
||||||
|
#[validate(length(min = 2, message = "Fullname at least have 2 character"))]
|
||||||
pub fullname: String,
|
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 struct AuthActiveInactiveRequestDto {
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
|
#[validate(
|
||||||
|
length(min = 1, message = "Email cannot be empty"),
|
||||||
|
email(message = "Email not valid")
|
||||||
|
)]
|
||||||
pub email: String,
|
pub email: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct AuthVerifyEmailRequestDto {
|
pub struct AuthVerifyEmailRequestDto {
|
||||||
|
#[validate(
|
||||||
|
length(min = 1, message = "Email cannot be empty"),
|
||||||
|
email(message = "Email not valid")
|
||||||
|
)]
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub otp: u32,
|
pub otp: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct AuthResendOtpRequestDto {
|
pub struct AuthResendOtpRequestDto {
|
||||||
|
#[validate(
|
||||||
|
length(min = 1, message = "Email cannot be empty"),
|
||||||
|
email(message = "Email not valid")
|
||||||
|
)]
|
||||||
pub email: String,
|
pub email: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct AuthNewPasswordRequestDto {
|
pub struct AuthNewPasswordRequestDto {
|
||||||
pub token: String,
|
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,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
pub struct AuthSetNewPasswordRequestDto {
|
pub struct AuthSetNewPasswordRequestDto {
|
||||||
|
#[validate(
|
||||||
|
length(min = 1, message = "Email cannot be empty"),
|
||||||
|
email(message = "Email not valid")
|
||||||
|
)]
|
||||||
pub email: String,
|
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,
|
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::{
|
use super::{
|
||||||
AuthLoginRequestDto, AuthLoginResponsetDto, AuthRegisterRequestDto,
|
AuthLoginRequestDto, AuthLoginResponsetDto, AuthNewPasswordRequestDto,
|
||||||
AuthRepository, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto, TokenDto,
|
AuthRegisterRequestDto, AuthRepository, AuthResendOtpRequestDto,
|
||||||
|
AuthVerifyEmailRequestDto, TokenDto,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
common_response, encode_access_token, encode_refresh_token, generate_otp,
|
common_response, encode_access_token, encode_refresh_token, extract_email_token,
|
||||||
hash_password, send_email, success_response, verify_password, AppState, Env,
|
generate_otp, get_iso_date, hash_password, send_email, success_response,
|
||||||
|
validate_request, verify_password, AppState, Env, ResourceEnum,
|
||||||
ResponseSuccessDto, UsersActiveInactiveSchema, UsersItemDto, UsersRepository,
|
ResponseSuccessDto, UsersActiveInactiveSchema, UsersItemDto, UsersRepository,
|
||||||
UsersSchema,
|
UsersSchema,
|
||||||
};
|
};
|
||||||
use axum::{http::StatusCode, response::Response};
|
use axum::{http::StatusCode, response::Response};
|
||||||
|
use surrealdb::sql::{Id, Thing};
|
||||||
|
|
||||||
pub struct AuthService;
|
pub struct AuthService;
|
||||||
|
|
||||||
@@ -17,6 +20,10 @@ impl AuthService {
|
|||||||
payload: AuthLoginRequestDto,
|
payload: AuthLoginRequestDto,
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
if let Err((status, message)) = validate_request(&payload) {
|
||||||
|
return common_response(status, &message);
|
||||||
|
}
|
||||||
|
|
||||||
let user_repo = UsersRepository::new(state);
|
let user_repo = UsersRepository::new(state);
|
||||||
let auth_repo = AuthRepository::new(state);
|
let auth_repo = AuthRepository::new(state);
|
||||||
|
|
||||||
@@ -112,6 +119,10 @@ impl AuthService {
|
|||||||
email: payload.email,
|
email: payload.email,
|
||||||
password: hashed_password,
|
password: hashed_password,
|
||||||
fullname: payload.fullname,
|
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();
|
let otp = generate_otp::OtpManager::generate_otp();
|
||||||
@@ -124,12 +135,30 @@ impl AuthService {
|
|||||||
|
|
||||||
send_email(&new_user.email.clone(), "OTP Verification", &message).unwrap();
|
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
|
match user_repo
|
||||||
.query_create_user(UsersSchema {
|
.query_create_user(UsersSchema {
|
||||||
|
id: Some("".to_string()),
|
||||||
email: new_user.email.clone(),
|
email: new_user.email.clone(),
|
||||||
fullname: new_user.fullname.clone(),
|
fullname: new_user.fullname.clone(),
|
||||||
password: new_user.password.clone(),
|
password: new_user.password.clone(),
|
||||||
is_active: false,
|
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
|
.await
|
||||||
{
|
{
|
||||||
@@ -235,4 +264,23 @@ impl AuthService {
|
|||||||
Err(err) => common_response(StatusCode::BAD_REQUEST, &err.to_string()),
|
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("/verify", post(auth_controller::post_verify_email))
|
||||||
.route("/resend", post(auth_controller::post_resend_otp))
|
.route("/resend", post(auth_controller::post_resend_otp))
|
||||||
.route("/forgot", post(auth_controller::post_forgot_password))
|
.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,
|
auth, AuthLoginRequestDto, AuthLoginResponsetDto, AuthResendOtpRequestDto,
|
||||||
AuthVerifyEmailRequestDto,
|
AuthVerifyEmailRequestDto,
|
||||||
},
|
},
|
||||||
MessageResponseDto, MetaRequestDto, MetaResponseDto, ResponseSuccessDto,
|
AuthNewPasswordRequestDto, MessageResponseDto, MetaRequestDto, MetaResponseDto,
|
||||||
|
ResponseSuccessDto,
|
||||||
};
|
};
|
||||||
|
|
||||||
use utoipa::{
|
use utoipa::{
|
||||||
@@ -17,7 +18,8 @@ use utoipa::{
|
|||||||
auth::auth_controller::post_login,
|
auth::auth_controller::post_login,
|
||||||
auth::auth_controller::post_register,
|
auth::auth_controller::post_register,
|
||||||
auth::auth_controller::post_verify_email,
|
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(
|
components(
|
||||||
schemas(
|
schemas(
|
||||||
@@ -28,12 +30,13 @@ use utoipa::{
|
|||||||
AuthLoginResponsetDto,
|
AuthLoginResponsetDto,
|
||||||
AuthVerifyEmailRequestDto,
|
AuthVerifyEmailRequestDto,
|
||||||
AuthResendOtpRequestDto,
|
AuthResendOtpRequestDto,
|
||||||
|
AuthNewPasswordRequestDto,
|
||||||
ResponseSuccessDto<AuthLoginResponsetDto>,
|
ResponseSuccessDto<AuthLoginResponsetDto>,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
info(
|
info(
|
||||||
title = "Axum SurrealDB Boilerplate",
|
title = "NAJM Course API",
|
||||||
description = "Axum SurrealDB Documentation",
|
description = "NAJM Course API",
|
||||||
version = "0.1.0",
|
version = "0.1.0",
|
||||||
contact(
|
contact(
|
||||||
name = "Maulana Sodiqin",
|
name = "Maulana Sodiqin",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use super::{UsersActiveInactiveSchema, UsersSetNewPasswordSchema};
|
use super::{UsersActiveInactiveSchema, UsersSchema, UsersSetNewPasswordSchema};
|
||||||
use crate::{v1::users_schema::UsersSchema, AppState, ResourceEnum};
|
use crate::{get_iso_date, AppState, ResourceEnum};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use surrealdb::Uuid;
|
||||||
|
|
||||||
pub struct UsersRepository<'a> {
|
pub struct UsersRepository<'a> {
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
@@ -25,14 +26,31 @@ impl<'a> UsersRepository<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn query_create_user(&self, data: UsersSchema) -> Result<String> {
|
pub async fn query_create_user(&self, data: UsersSchema) -> Result<String> {
|
||||||
|
let id = Uuid::new_v4().to_string();
|
||||||
let db = &self.state.surrealdb;
|
let db = &self.state.surrealdb;
|
||||||
|
|
||||||
let record: Option<UsersSchema> = db
|
let record: Option<UsersSchema> = db
|
||||||
.create((ResourceEnum::Users.to_string(), &data.email))
|
.create((ResourceEnum::Users.to_string(), &id))
|
||||||
.content(UsersSchema {
|
.content(UsersSchema {
|
||||||
|
id: Some(id.clone()),
|
||||||
|
role_id: data.role_id.clone(),
|
||||||
fullname: data.fullname.clone(),
|
fullname: data.fullname.clone(),
|
||||||
email: data.email.clone(),
|
email: data.email.clone(),
|
||||||
password: data.password.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?;
|
.await?;
|
||||||
match record {
|
match record {
|
||||||
|
|||||||
@@ -1,17 +1,33 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::sql::Thing;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct UsersSchema {
|
pub struct UsersSchema {
|
||||||
pub email: String,
|
pub id: Option<String>,
|
||||||
|
pub role_id: String,
|
||||||
pub fullname: String,
|
pub fullname: String,
|
||||||
|
pub email: String,
|
||||||
pub password: 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 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)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct UsersSetNewPasswordSchema {
|
pub struct UsersSetNewPasswordSchema {
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub password: bool,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -7,3 +7,8 @@ pub fn extract_email(headers: &HeaderMap) -> Option<String> {
|
|||||||
let token_data = decode_access_token(token).ok()?;
|
let token_data = decode_access_token(token).ok()?;
|
||||||
Some(token_data.claims.sub)
|
Some(token_data.claims.sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract_email_token(token: String) -> Option<String> {
|
||||||
|
let token_data = decode_access_token(&token).ok()?;
|
||||||
|
Some(token_data.claims.sub)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
pub fn get_iso_date() -> String {
|
||||||
|
let now: DateTime<Utc> = Utc::now();
|
||||||
|
now.to_rfc3339()
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
pub mod extract_email;
|
pub mod extract_email;
|
||||||
|
pub mod generate_date;
|
||||||
pub mod generate_otp;
|
pub mod generate_otp;
|
||||||
pub mod response_format;
|
pub mod response_format;
|
||||||
|
pub mod validator;
|
||||||
|
|
||||||
pub use extract_email::*;
|
pub use extract_email::*;
|
||||||
|
pub use generate_date::*;
|
||||||
pub use generate_otp::*;
|
pub use generate_otp::*;
|
||||||
pub use response_format::*;
|
pub use response_format::*;
|
||||||
|
pub use validator::*;
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use axum::http::StatusCode;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
pub fn validate_request<T: Validate>(
|
||||||
|
payload: &T,
|
||||||
|
) -> Result<(), (StatusCode, String)> {
|
||||||
|
if let Err(validation_errors) = payload.validate() {
|
||||||
|
let error_messages: Vec<String> = validation_errors
|
||||||
|
.field_errors()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, errors)| {
|
||||||
|
errors.iter().map(move |error| {
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
error
|
||||||
|
.message
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| "Invalid value".into())
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
return Err((StatusCode::BAD_REQUEST, error_messages.join(", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user