feat(users): Add optional fields to user schemas and update user avatar functionality
This commit is contained in:
@@ -3,6 +3,46 @@ name = "imphnen-backend"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "api"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "clear_db_test"
|
||||||
|
path = "src/bin/clear_db_test.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seeder"
|
||||||
|
path = "src/bin/seeder.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seed_events"
|
||||||
|
path = "src/bin/seed_events.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seed_gacha_rolls"
|
||||||
|
path = "src/bin/seed_gacha_rolls.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seed_mentor_user"
|
||||||
|
path = "src/bin/seed_mentor_user.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seed_permissions"
|
||||||
|
path = "src/bin/seed_permissions.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seed_roles"
|
||||||
|
path = "src/bin/seed_roles.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seed_roles_permissions"
|
||||||
|
path = "src/bin/seed_roles_permissions.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "seed_users"
|
||||||
|
path = "src/bin/seed_users.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
imphnen-libs.workspace = true
|
imphnen-libs.workspace = true
|
||||||
imphnen-utils.workspace = true
|
imphnen-utils.workspace = true
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
PermissionsEnum::ReadOwnMentorProfile,
|
PermissionsEnum::ReadOwnMentorProfile,
|
||||||
PermissionsEnum::UpdateOwnMentorProfile,
|
PermissionsEnum::UpdateOwnMentorProfile,
|
||||||
PermissionsEnum::ReadOwnMentorStatus,
|
PermissionsEnum::ReadOwnMentorStatus,
|
||||||
|
PermissionsEnum::UpdateMentors,
|
||||||
PermissionsEnum::VerifyMentors,
|
PermissionsEnum::VerifyMentors,
|
||||||
PermissionsEnum::DeleteMentors,
|
PermissionsEnum::DeleteMentors,
|
||||||
] {
|
] {
|
||||||
|
|||||||
@@ -46,15 +46,25 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
let user = UsersSchema {
|
let user = UsersSchema {
|
||||||
id: Thing::from(("app_users", id)),
|
id: Thing::from(("app_users", id)),
|
||||||
fullname: fullname.into(),
|
fullname: fullname.into(),
|
||||||
|
legal_name: None,
|
||||||
email: email.into(),
|
email: email.into(),
|
||||||
password: hash_password("password").unwrap(),
|
password: hash_password("password").unwrap(),
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: "081234567890".into(),
|
phone_number: "081234567890".into(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active: true,
|
is_active: true,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
mentor_id: None,
|
mentor_id: None,
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
role: Thing::from(("app_roles", role_id)),
|
role: Thing::from(("app_roles", role_id)),
|
||||||
created_at: get_iso_date(),
|
created_at: get_iso_date(),
|
||||||
updated_at: get_iso_date(),
|
updated_at: get_iso_date(),
|
||||||
|
|||||||
@@ -94,13 +94,23 @@ impl<'a> AuthRepository<'a> {
|
|||||||
Ok(UsersDetailQueryDto {
|
Ok(UsersDetailQueryDto {
|
||||||
id: Thing::from(("app_users".to_string(), email.clone())),
|
id: Thing::from(("app_users".to_string(), email.clone())),
|
||||||
fullname: "Cached User".to_string(),
|
fullname: "Cached User".to_string(),
|
||||||
|
legal_name: None,
|
||||||
email: cache.email,
|
email: cache.email,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: String::new(),
|
phone_number: String::new(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active: true,
|
is_active: true,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
password: String::new(),
|
password: String::new(),
|
||||||
role: role_detail_query_dto,
|
role: role_detail_query_dto,
|
||||||
created_at: String::new(),
|
created_at: String::new(),
|
||||||
|
|||||||
@@ -215,9 +215,15 @@ where
|
|||||||
// Update avatar if user doesn't have one and Google provides one
|
// Update avatar if user doesn't have one and Google provides one
|
||||||
if user.avatar.is_none() && google_user.picture.is_some() {
|
if user.avatar.is_none() && google_user.picture.is_some() {
|
||||||
info!("Updating avatar for existing user: {}", google_user.email);
|
info!("Updating avatar for existing user: {}", google_user.email);
|
||||||
// Note: We would need to implement an update_user_avatar method in the user service
|
match self.users_service.update_user_avatar(&google_user.email, google_user.picture.clone()).await {
|
||||||
// For now, we'll just log this
|
Ok(_) => {
|
||||||
info!("Avatar would be updated to: {:?}", google_user.picture);
|
info!("Successfully updated avatar for user: {}", google_user.email);
|
||||||
|
user.avatar = google_user.picture.clone();
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to update avatar for user {}: {:?}", google_user.email, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user
|
user
|
||||||
|
|||||||
@@ -67,16 +67,33 @@ pub struct UsersUpdateRequestDto {
|
|||||||
pub password: String,
|
pub password: String,
|
||||||
#[validate(length(min = 2, message = "Fullname at least have 2 character"))]
|
#[validate(length(min = 2, message = "Fullname at least have 2 character"))]
|
||||||
pub fullname: String,
|
pub fullname: String,
|
||||||
|
#[validate(length(min = 2, message = "Legal name at least have 2 character"))]
|
||||||
|
pub legal_name: Option<String>,
|
||||||
#[validate(length(
|
#[validate(length(
|
||||||
min = 10,
|
min = 10,
|
||||||
message = "Phone number at least have 10 character"
|
message = "Phone number at least have 10 character"
|
||||||
))]
|
))]
|
||||||
pub phone_number: String,
|
pub phone_number: String,
|
||||||
|
pub phone_for_verification: Option<String>,
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
#[validate(length(min = 1, message = "Gender is required"))]
|
#[validate(length(min = 1, message = "Gender is required"))]
|
||||||
pub gender: Option<String>,
|
pub gender: Option<String>,
|
||||||
#[validate(length(min = 1, message = "Birthdate is required"))]
|
#[validate(length(min = 1, message = "Birthdate is required"))]
|
||||||
pub birthdate: Option<String>,
|
pub birthdate: Option<String>,
|
||||||
|
pub domicile: Option<String>,
|
||||||
|
#[validate(url(message = "Invalid identity document URL"))]
|
||||||
|
pub identity_document_url: Option<String>,
|
||||||
|
#[validate(length(min = 50, message = "Bio must be at least 50 characters"))]
|
||||||
|
pub bio: Option<String>,
|
||||||
|
pub last_education: Option<String>,
|
||||||
|
#[validate(url(message = "Invalid LinkedIn URL"))]
|
||||||
|
pub linkedin_url: Option<String>,
|
||||||
|
#[validate(url(message = "Invalid GitHub URL"))]
|
||||||
|
pub github_url: Option<String>,
|
||||||
|
#[validate(url(message = "Invalid CV URL"))]
|
||||||
|
pub cv_url: Option<String>,
|
||||||
|
#[validate(url(message = "Invalid portfolio URL"))]
|
||||||
|
pub portfolio_url: Option<String>,
|
||||||
#[validate(length(min = 1, message = "Avatar is required"))]
|
#[validate(length(min = 1, message = "Avatar is required"))]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub role_id: String,
|
pub role_id: String,
|
||||||
@@ -87,12 +104,22 @@ pub struct UsersDetailItemDto {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub role: RolesDetailItemDto,
|
pub role: RolesDetailItemDto,
|
||||||
pub fullname: String,
|
pub fullname: String,
|
||||||
|
pub legal_name: Option<String>,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub phone_number: String,
|
pub phone_number: String,
|
||||||
|
pub phone_for_verification: Option<String>,
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
pub gender: Option<String>,
|
pub gender: Option<String>,
|
||||||
pub birthdate: Option<String>,
|
pub birthdate: Option<String>,
|
||||||
|
pub domicile: Option<String>,
|
||||||
|
pub identity_document_url: Option<String>,
|
||||||
|
pub bio: Option<String>,
|
||||||
|
pub last_education: Option<String>,
|
||||||
|
pub linkedin_url: Option<String>,
|
||||||
|
pub github_url: Option<String>,
|
||||||
|
pub cv_url: Option<String>,
|
||||||
|
pub portfolio_url: Option<String>,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
@@ -103,12 +130,22 @@ impl UsersDetailItemDto {
|
|||||||
id: dto.id.id.to_raw().clone(),
|
id: dto.id.id.to_raw().clone(),
|
||||||
role: RolesDetailItemDto::from(&dto.role),
|
role: RolesDetailItemDto::from(&dto.role),
|
||||||
fullname: dto.fullname.clone(),
|
fullname: dto.fullname.clone(),
|
||||||
|
legal_name: dto.legal_name.clone(),
|
||||||
email: dto.email.clone(),
|
email: dto.email.clone(),
|
||||||
avatar: dto.avatar.clone(),
|
avatar: dto.avatar.clone(),
|
||||||
phone_number: dto.phone_number.clone(), // Corrected from dto.phone.clone()
|
phone_number: dto.phone_number.clone(), // Corrected from dto.phone.clone()
|
||||||
|
phone_for_verification: dto.phone_for_verification.clone(),
|
||||||
is_active: dto.is_active,
|
is_active: dto.is_active,
|
||||||
gender: dto.gender.clone(),
|
gender: dto.gender.clone(),
|
||||||
birthdate: dto.birthdate.clone(),
|
birthdate: dto.birthdate.clone(),
|
||||||
|
domicile: dto.domicile.clone(),
|
||||||
|
identity_document_url: dto.identity_document_url.clone(),
|
||||||
|
bio: dto.bio.clone(),
|
||||||
|
last_education: dto.last_education.clone(),
|
||||||
|
linkedin_url: dto.linkedin_url.clone(),
|
||||||
|
github_url: dto.github_url.clone(),
|
||||||
|
cv_url: dto.cv_url.clone(),
|
||||||
|
portfolio_url: dto.portfolio_url.clone(),
|
||||||
created_at: dto.created_at.clone(),
|
created_at: dto.created_at.clone(),
|
||||||
updated_at: dto.updated_at.clone(),
|
updated_at: dto.updated_at.clone(),
|
||||||
}
|
}
|
||||||
@@ -119,12 +156,22 @@ impl UsersDetailItemDto {
|
|||||||
id: schema.id.id.to_raw(),
|
id: schema.id.id.to_raw(),
|
||||||
role: RolesDetailItemDto::default(), // Placeholder, role needs to be fetched
|
role: RolesDetailItemDto::default(), // Placeholder, role needs to be fetched
|
||||||
fullname: schema.fullname.clone(),
|
fullname: schema.fullname.clone(),
|
||||||
|
legal_name: schema.legal_name.clone(),
|
||||||
email: schema.email.clone(),
|
email: schema.email.clone(),
|
||||||
avatar: schema.avatar.clone(),
|
avatar: schema.avatar.clone(),
|
||||||
phone_number: schema.phone_number.clone(),
|
phone_number: schema.phone_number.clone(),
|
||||||
|
phone_for_verification: schema.phone_for_verification.clone(),
|
||||||
is_active: schema.is_active,
|
is_active: schema.is_active,
|
||||||
gender: schema.gender.clone(),
|
gender: schema.gender.clone(),
|
||||||
birthdate: schema.birthdate.clone(),
|
birthdate: schema.birthdate.clone(),
|
||||||
|
domicile: schema.domicile.clone(),
|
||||||
|
identity_document_url: schema.identity_document_url.clone(),
|
||||||
|
bio: schema.bio.clone(),
|
||||||
|
last_education: schema.last_education.clone(),
|
||||||
|
linkedin_url: schema.linkedin_url.clone(),
|
||||||
|
github_url: schema.github_url.clone(),
|
||||||
|
cv_url: schema.cv_url.clone(),
|
||||||
|
portfolio_url: schema.portfolio_url.clone(),
|
||||||
created_at: schema.created_at.clone(),
|
created_at: schema.created_at.clone(),
|
||||||
updated_at: schema.updated_at.clone(),
|
updated_at: schema.updated_at.clone(),
|
||||||
}
|
}
|
||||||
@@ -177,13 +224,23 @@ impl UsersListQueryDto {
|
|||||||
pub struct UsersDetailQueryDto {
|
pub struct UsersDetailQueryDto {
|
||||||
pub id: Thing,
|
pub id: Thing,
|
||||||
pub fullname: String,
|
pub fullname: String,
|
||||||
|
pub legal_name: Option<String>,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub phone_number: String,
|
pub phone_number: String,
|
||||||
|
pub phone_for_verification: Option<String>,
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
pub is_deleted: bool,
|
pub is_deleted: bool,
|
||||||
pub gender: Option<String>,
|
pub gender: Option<String>,
|
||||||
pub birthdate: Option<String>,
|
pub birthdate: Option<String>,
|
||||||
|
pub domicile: Option<String>,
|
||||||
|
pub identity_document_url: Option<String>,
|
||||||
|
pub bio: Option<String>,
|
||||||
|
pub last_education: Option<String>,
|
||||||
|
pub linkedin_url: Option<String>,
|
||||||
|
pub github_url: Option<String>,
|
||||||
|
pub cv_url: Option<String>,
|
||||||
|
pub portfolio_url: Option<String>,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
pub role: RolesDetailQueryDto,
|
pub role: RolesDetailQueryDto,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
@@ -197,12 +254,22 @@ impl UsersDetailQueryDto {
|
|||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
role: self.role.clone(),
|
role: self.role.clone(),
|
||||||
fullname: self.fullname.clone(),
|
fullname: self.fullname.clone(),
|
||||||
|
legal_name: self.legal_name.clone(),
|
||||||
email: self.email.clone(),
|
email: self.email.clone(),
|
||||||
avatar: self.avatar.clone(),
|
avatar: self.avatar.clone(),
|
||||||
phone_number: self.phone_number.clone(),
|
phone_number: self.phone_number.clone(),
|
||||||
|
phone_for_verification: self.phone_for_verification.clone(),
|
||||||
is_active: self.is_active,
|
is_active: self.is_active,
|
||||||
mentor_id: self.mentor_id.clone(),
|
mentor_id: self.mentor_id.clone(),
|
||||||
gender: self.gender.clone(),
|
gender: self.gender.clone(),
|
||||||
|
domicile: self.domicile.clone(),
|
||||||
|
identity_document_url: self.identity_document_url.clone(),
|
||||||
|
bio: self.bio.clone(),
|
||||||
|
last_education: self.last_education.clone(),
|
||||||
|
linkedin_url: self.linkedin_url.clone(),
|
||||||
|
github_url: self.github_url.clone(),
|
||||||
|
cv_url: self.cv_url.clone(),
|
||||||
|
portfolio_url: self.portfolio_url.clone(),
|
||||||
is_deleted: self.is_deleted,
|
is_deleted: self.is_deleted,
|
||||||
password: self.password.clone(),
|
password: self.password.clone(),
|
||||||
birthdate: self.birthdate.clone(),
|
birthdate: self.birthdate.clone(),
|
||||||
@@ -217,13 +284,23 @@ impl From<&UsersDetailItemDto> for UsersDetailQueryDto {
|
|||||||
Self {
|
Self {
|
||||||
id: crate::make_thing(&imphnen_libs::ResourceEnum::Users.to_string(), &dto.id),
|
id: crate::make_thing(&imphnen_libs::ResourceEnum::Users.to_string(), &dto.id),
|
||||||
fullname: dto.fullname.clone(),
|
fullname: dto.fullname.clone(),
|
||||||
|
legal_name: dto.legal_name.clone(),
|
||||||
email: dto.email.clone(),
|
email: dto.email.clone(),
|
||||||
avatar: dto.avatar.clone(),
|
avatar: dto.avatar.clone(),
|
||||||
phone_number: dto.phone_number.clone(),
|
phone_number: dto.phone_number.clone(),
|
||||||
|
phone_for_verification: dto.phone_for_verification.clone(),
|
||||||
is_active: dto.is_active,
|
is_active: dto.is_active,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
gender: dto.gender.clone(),
|
gender: dto.gender.clone(),
|
||||||
birthdate: dto.birthdate.clone(),
|
birthdate: dto.birthdate.clone(),
|
||||||
|
domicile: dto.domicile.clone(),
|
||||||
|
identity_document_url: dto.identity_document_url.clone(),
|
||||||
|
bio: dto.bio.clone(),
|
||||||
|
last_education: dto.last_education.clone(),
|
||||||
|
linkedin_url: dto.linkedin_url.clone(),
|
||||||
|
github_url: dto.github_url.clone(),
|
||||||
|
cv_url: dto.cv_url.clone(),
|
||||||
|
portfolio_url: dto.portfolio_url.clone(),
|
||||||
password: String::new(),
|
password: String::new(),
|
||||||
role: RolesDetailQueryDto::default(),
|
role: RolesDetailQueryDto::default(),
|
||||||
created_at: dto.created_at.clone(),
|
created_at: dto.created_at.clone(),
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ use surrealdb::{Uuid, sql::Thing};
|
|||||||
pub struct UsersSchema {
|
pub struct UsersSchema {
|
||||||
pub id: Thing,
|
pub id: Thing,
|
||||||
pub fullname: String,
|
pub fullname: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub legal_name: Option<String>,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub phone_number: String,
|
pub phone_number: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub phone_for_verification: Option<String>,
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
pub is_deleted: bool,
|
pub is_deleted: bool,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -22,6 +26,22 @@ pub struct UsersSchema {
|
|||||||
pub gender: Option<String>,
|
pub gender: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub birthdate: Option<String>,
|
pub birthdate: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub domicile: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub identity_document_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub bio: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub last_education: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub linkedin_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub github_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub cv_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub portfolio_url: Option<String>,
|
||||||
pub role: Thing,
|
pub role: Thing,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
@@ -35,10 +55,12 @@ impl Default for UsersSchema {
|
|||||||
&Uuid::new_v4().to_string(),
|
&Uuid::new_v4().to_string(),
|
||||||
),
|
),
|
||||||
fullname: String::new(),
|
fullname: String::new(),
|
||||||
|
legal_name: None,
|
||||||
email: String::new(),
|
email: String::new(),
|
||||||
password: hash_password("").unwrap(),
|
password: hash_password("").unwrap(),
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: String::new(),
|
phone_number: String::new(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active: false,
|
is_active: false,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
mentor_id: Some(make_thing(
|
mentor_id: Some(make_thing(
|
||||||
@@ -47,6 +69,14 @@ impl Default for UsersSchema {
|
|||||||
)),
|
)),
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
role: make_thing(
|
role: make_thing(
|
||||||
&ResourceEnum::Roles.to_string(),
|
&ResourceEnum::Roles.to_string(),
|
||||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||||
@@ -62,9 +92,11 @@ impl UsersSchema {
|
|||||||
Self {
|
Self {
|
||||||
id: dto.id,
|
id: dto.id,
|
||||||
fullname: dto.fullname,
|
fullname: dto.fullname,
|
||||||
|
legal_name: dto.legal_name,
|
||||||
email: dto.email,
|
email: dto.email,
|
||||||
avatar: dto.avatar,
|
avatar: dto.avatar,
|
||||||
phone_number: dto.phone_number,
|
phone_number: dto.phone_number,
|
||||||
|
phone_for_verification: dto.phone_for_verification,
|
||||||
is_active: dto.is_active,
|
is_active: dto.is_active,
|
||||||
is_deleted: dto.is_deleted,
|
is_deleted: dto.is_deleted,
|
||||||
mentor_id: Some(dto.mentor_id.unwrap_or_else(|| {
|
mentor_id: Some(dto.mentor_id.unwrap_or_else(|| {
|
||||||
@@ -75,6 +107,14 @@ impl UsersSchema {
|
|||||||
})),
|
})),
|
||||||
gender: dto.gender,
|
gender: dto.gender,
|
||||||
birthdate: dto.birthdate,
|
birthdate: dto.birthdate,
|
||||||
|
domicile: dto.domicile,
|
||||||
|
identity_document_url: dto.identity_document_url,
|
||||||
|
bio: dto.bio,
|
||||||
|
last_education: dto.last_education,
|
||||||
|
linkedin_url: dto.linkedin_url,
|
||||||
|
github_url: dto.github_url,
|
||||||
|
cv_url: dto.cv_url,
|
||||||
|
portfolio_url: dto.portfolio_url,
|
||||||
password: dto.password,
|
password: dto.password,
|
||||||
created_at: dto.created_at,
|
created_at: dto.created_at,
|
||||||
updated_at: dto.updated_at,
|
updated_at: dto.updated_at,
|
||||||
@@ -86,11 +126,21 @@ impl UsersSchema {
|
|||||||
Self {
|
Self {
|
||||||
id: make_thing(&ResourceEnum::Users.to_string(), &id),
|
id: make_thing(&ResourceEnum::Users.to_string(), &id),
|
||||||
fullname: user.fullname,
|
fullname: user.fullname,
|
||||||
|
legal_name: user.legal_name,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
phone_number: user.phone_number,
|
phone_number: user.phone_number,
|
||||||
|
phone_for_verification: user.phone_for_verification,
|
||||||
is_active: user.is_active,
|
is_active: user.is_active,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
birthdate: user.birthdate,
|
birthdate: user.birthdate,
|
||||||
|
domicile: user.domicile,
|
||||||
|
identity_document_url: user.identity_document_url,
|
||||||
|
bio: user.bio,
|
||||||
|
last_education: user.last_education,
|
||||||
|
linkedin_url: user.linkedin_url,
|
||||||
|
github_url: user.github_url,
|
||||||
|
cv_url: user.cv_url,
|
||||||
|
portfolio_url: user.portfolio_url,
|
||||||
avatar: user.avatar,
|
avatar: user.avatar,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
role: make_thing(&ResourceEnum::Roles.to_string(), &user.role_id),
|
role: make_thing(&ResourceEnum::Roles.to_string(), &user.role_id),
|
||||||
@@ -107,9 +157,11 @@ impl UsersSchema {
|
|||||||
&Uuid::new_v4().to_string(),
|
&Uuid::new_v4().to_string(),
|
||||||
),
|
),
|
||||||
fullname: user.fullname,
|
fullname: user.fullname,
|
||||||
|
legal_name: None,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
password,
|
password,
|
||||||
phone_number: user.phone_number,
|
phone_number: user.phone_number,
|
||||||
|
phone_for_verification: None,
|
||||||
is_active: false,
|
is_active: false,
|
||||||
mentor_id: Some(make_thing(
|
mentor_id: Some(make_thing(
|
||||||
&ResourceEnum::Users.to_string(),
|
&ResourceEnum::Users.to_string(),
|
||||||
@@ -117,6 +169,14 @@ impl UsersSchema {
|
|||||||
)),
|
)),
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
avatar: user.avatar,
|
avatar: user.avatar,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
role: make_thing(&ResourceEnum::Roles.to_string(), &user.role_id),
|
role: make_thing(&ResourceEnum::Roles.to_string(), &user.role_id),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use imphnen_utils::make_thing;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use tracing::info;
|
||||||
use crate::v1::users::users_dto::{UsersDetailItemDto as UserDto, UsersCreateRequestDto as CreateUserDto};
|
use crate::v1::users::users_dto::{UsersDetailItemDto as UserDto, UsersCreateRequestDto as CreateUserDto};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -33,6 +34,7 @@ pub trait UsersServiceTrait: Send + Sync + 'static {
|
|||||||
|
|
||||||
async fn get_user_by_email(&self, email: &str) -> Result<Option<UserDto>>;
|
async fn get_user_by_email(&self, email: &str) -> Result<Option<UserDto>>;
|
||||||
async fn create_user_by_dto(&self, new_user: CreateUserDto) -> Result<UserDto>;
|
async fn create_user_by_dto(&self, new_user: CreateUserDto) -> Result<UserDto>;
|
||||||
|
async fn update_user_avatar(&self, email: &str, avatar_url: Option<String>) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -301,4 +303,34 @@ impl UsersServiceTrait for UsersService {
|
|||||||
Err(e) => Err(anyhow::anyhow!(e.to_string())),
|
Err(e) => Err(anyhow::anyhow!(e.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_user_avatar(&self, email: &str, avatar_url: Option<String>) -> Result<()> {
|
||||||
|
let surrealdb_ws = surrealdb_init_ws().await
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to initialize websocket database: {}", e))?;
|
||||||
|
let surrealdb_mem = surrealdb_init_mem().await
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to initialize memory database: {}", e))?;
|
||||||
|
|
||||||
|
let state = AppState {
|
||||||
|
surrealdb_ws,
|
||||||
|
surrealdb_mem,
|
||||||
|
};
|
||||||
|
let repo = UsersRepository::new(&state);
|
||||||
|
|
||||||
|
// Get the existing user
|
||||||
|
let mut user = repo.query_user_by_email(email.to_string()).await
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to get user: {}", e))?;
|
||||||
|
|
||||||
|
// Update the avatar
|
||||||
|
user.avatar = avatar_url;
|
||||||
|
|
||||||
|
// Convert to schema and update
|
||||||
|
let user_schema = UsersSchema::from(user);
|
||||||
|
match repo.query_update_user(user_schema).await {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Successfully updated avatar for user: {}", email);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => Err(anyhow::anyhow!("Failed to update user avatar: {}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,8 +191,14 @@ mod tests {
|
|||||||
let secret = "test_secret";
|
let secret = "test_secret";
|
||||||
let token = generate_csrf_token(secret).unwrap();
|
let token = generate_csrf_token(secret).unwrap();
|
||||||
|
|
||||||
// Should fail with 0 max age
|
// Add a 2 second delay to ensure the token expires when max_age is 1 second
|
||||||
assert!(validate_csrf_token(&token, secret, 0).is_err());
|
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||||
|
|
||||||
|
// Should fail with 1 second max age (token is now 2 seconds old)
|
||||||
|
assert!(validate_csrf_token(&token, secret, 1).is_err());
|
||||||
|
|
||||||
|
// Should still work with a large max age
|
||||||
|
assert!(validate_csrf_token(&token, secret, 300).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -47,13 +47,23 @@ mod auth_login_tests {
|
|||||||
id: crate::make_thing("app_users", &uuid::Uuid::new_v4().to_string()),
|
id: crate::make_thing("app_users", &uuid::Uuid::new_v4().to_string()),
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
fullname: "Test User".to_string(),
|
fullname: "Test User".to_string(),
|
||||||
|
legal_name: None,
|
||||||
password: hash_password(password).unwrap(),
|
password: hash_password(password).unwrap(),
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: "081234567890".to_string(),
|
phone_number: "081234567890".to_string(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active,
|
is_active,
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
role: crate::make_thing("app_roles", &role.id),
|
role: crate::make_thing("app_roles", &role.id),
|
||||||
mentor_id: None,
|
mentor_id: None,
|
||||||
created_at: imphnen_utils::get_iso_date(),
|
created_at: imphnen_utils::get_iso_date(),
|
||||||
|
|||||||
@@ -21,13 +21,23 @@ mod auth_repository_test {
|
|||||||
id: make_thing("app_users", &Uuid::new_v4().to_string()),
|
id: make_thing("app_users", &Uuid::new_v4().to_string()),
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
fullname: "Test User".to_string(),
|
fullname: "Test User".to_string(),
|
||||||
|
legal_name: None,
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: "081234567890".to_string(),
|
phone_number: "081234567890".to_string(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active: true,
|
is_active: true,
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
role: make_thing("app_roles", &get_role_id(state).await),
|
role: make_thing("app_roles", &get_role_id(state).await),
|
||||||
mentor_id: None,
|
mentor_id: None,
|
||||||
created_at: get_iso_date(),
|
created_at: get_iso_date(),
|
||||||
@@ -71,12 +81,22 @@ mod auth_repository_test {
|
|||||||
let mock_user = UsersDetailQueryDto {
|
let mock_user = UsersDetailQueryDto {
|
||||||
id: make_thing(&ResourceEnum::UsersCache.to_string(), &email),
|
id: make_thing(&ResourceEnum::UsersCache.to_string(), &email),
|
||||||
fullname: "Test User".into(),
|
fullname: "Test User".into(),
|
||||||
|
legal_name: None,
|
||||||
email: email.clone(),
|
email: email.clone(),
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: "08123456789".into(),
|
phone_number: "08123456789".into(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active: true,
|
is_active: true,
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
role: RolesDetailQueryDto {
|
role: RolesDetailQueryDto {
|
||||||
id: make_thing("app_roles", &Uuid::new_v4().to_string()),
|
id: make_thing("app_roles", &Uuid::new_v4().to_string()),
|
||||||
name: "Dummy Role".into(),
|
name: "Dummy Role".into(),
|
||||||
|
|||||||
@@ -21,13 +21,23 @@ pub fn create_test_user(
|
|||||||
id: make_thing("app_users", &Uuid::new_v4().to_string()),
|
id: make_thing("app_users", &Uuid::new_v4().to_string()),
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
fullname: format!("Randomize {} {}", fullname, rand::random::<u32>()),
|
fullname: format!("Randomize {} {}", fullname, rand::random::<u32>()),
|
||||||
|
legal_name: None,
|
||||||
password: hash_password("secret").unwrap(),
|
password: hash_password("secret").unwrap(),
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: "081234567890".to_string(),
|
phone_number: "081234567890".to_string(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active,
|
is_active,
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
role: make_thing("app_roles", role_id),
|
role: make_thing("app_roles", role_id),
|
||||||
created_at: get_iso_date(),
|
created_at: get_iso_date(),
|
||||||
updated_at: get_iso_date(),
|
updated_at: get_iso_date(),
|
||||||
|
|||||||
@@ -149,15 +149,25 @@ pub async fn seed_users_for_test(
|
|||||||
let user = UsersSchema {
|
let user = UsersSchema {
|
||||||
id: Thing::from(("app_users", id)),
|
id: Thing::from(("app_users", id)),
|
||||||
fullname: fullname.into(),
|
fullname: fullname.into(),
|
||||||
|
legal_name: None,
|
||||||
email: email.into(),
|
email: email.into(),
|
||||||
password: hash_password("password").unwrap(),
|
password: hash_password("password").unwrap(),
|
||||||
avatar: None,
|
avatar: None,
|
||||||
phone_number: "081234567890".into(),
|
phone_number: "081234567890".into(),
|
||||||
|
phone_for_verification: None,
|
||||||
is_active: true,
|
is_active: true,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
mentor_id: None,
|
mentor_id: None,
|
||||||
gender: None,
|
gender: None,
|
||||||
birthdate: None,
|
birthdate: None,
|
||||||
|
domicile: None,
|
||||||
|
identity_document_url: None,
|
||||||
|
bio: None,
|
||||||
|
last_education: None,
|
||||||
|
linkedin_url: None,
|
||||||
|
github_url: None,
|
||||||
|
cv_url: None,
|
||||||
|
portfolio_url: None,
|
||||||
role: Thing::from(("app_roles", role_id)),
|
role: Thing::from(("app_roles", role_id)),
|
||||||
created_at: get_iso_date(),
|
created_at: get_iso_date(),
|
||||||
updated_at: get_iso_date(),
|
updated_at: get_iso_date(),
|
||||||
|
|||||||
Reference in New Issue
Block a user