feat: complete backend service

This commit is contained in:
Maulana Sodiqin
2025-03-30 16:08:58 +07:00
parent 3ae9dbd717
commit 6460979bab
22 changed files with 64 additions and 257 deletions
-1
View File
@@ -4,6 +4,5 @@ SURREALDB_USERNAME=
SURREALDB_PASSWORD= SURREALDB_PASSWORD=
SURREALDB_NAMESPACE= SURREALDB_NAMESPACE=
SURREALDB_DBNAME= SURREALDB_DBNAME=
REDISDB_URL=
ACCESS_TOKEN_SECRET= ACCESS_TOKEN_SECRET=
REFRESH_TOKEN_SECRET= REFRESH_TOKEN_SECRET=
+2 -2
View File
@@ -41,8 +41,8 @@ jobs:
username: ${{ secrets.VPS_USER }} username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }} key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }} port: ${{ secrets.VPS_PORT }}
source: ./target/release/najm-course-api source: ./target/release/imphnen-backend-service
target: /opt/najm-course-api/najm-course-api target: /opt/imphnen-backend-service/imphnen-backend-service
rm: true rm: true
overwrite: true overwrite: true
Generated
+27 -27
View File
@@ -1870,6 +1870,33 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]]
name = "imphnen-backend-service"
version = "0.1.0"
dependencies = [
"anyhow",
"argon2",
"axum",
"axum-test",
"chrono",
"fancy-regex",
"jsonwebtoken",
"lazy_static",
"lettre",
"log",
"rand 0.9.0",
"regex",
"serde",
"serde_json",
"surrealdb",
"thiserror 2.0.12",
"tokio",
"tower-http",
"utoipa",
"utoipa-swagger-ui",
"validator",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.3" version = "1.9.3"
@@ -2256,33 +2283,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "najm-course-api"
version = "0.1.0"
dependencies = [
"anyhow",
"argon2",
"axum",
"axum-test",
"chrono",
"fancy-regex",
"jsonwebtoken",
"lazy_static",
"lettre",
"log",
"rand 0.9.0",
"regex",
"serde",
"serde_json",
"surrealdb",
"thiserror 2.0.12",
"tokio",
"tower-http",
"utoipa",
"utoipa-swagger-ui",
"validator",
]
[[package]] [[package]]
name = "nanoid" name = "nanoid"
version = "0.4.0" version = "0.4.0"
+1 -1
View File
@@ -1,5 +1,5 @@
[package] [package]
name = "najm-course-api" name = "imphnen-backend-service"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
+3 -3
View File
@@ -11,12 +11,12 @@ WORKDIR /app
COPY Cargo.toml Cargo.lock ./ COPY Cargo.toml Cargo.lock ./
COPY ./src ./src COPY ./src ./src
RUN cargo build --release && strip /app/target/release/axum-surrealdb-boilerplate RUN cargo build --release && strip /app/target/release/imphnen-backend-service
FROM gcr.io/distroless/cc AS runner FROM gcr.io/distroless/cc AS runner
WORKDIR /app WORKDIR /app
COPY --from=builder /app/target/release/axum-surrealdb-boilerplate . COPY --from=builder /app/target/release/imphnen-backend-service .
CMD ["/app/axum-boilerplate-surrealdb"] CMD ["/app/imphnen-backend-service"]
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
description = "IMPHNEN CMS API Nix Flake"; description = "IMPHNEN Backend Service Nix Flake";
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
-6
View File
@@ -28,13 +28,7 @@ pub struct AuthUserItemDto {
pub email: String, pub email: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub phone_number: String, pub phone_number: String,
pub referred_by: Option<String>,
pub referral_code: Option<String>,
pub student_type: String,
pub is_active: bool, pub is_active: bool,
pub is_profile_completed: bool,
pub identity_number: Option<String>,
pub religion: Option<String>,
pub gender: Option<String>, pub gender: Option<String>,
pub birthdate: Option<String>, pub birthdate: Option<String>,
} }
-12
View File
@@ -16,15 +16,9 @@ mod auth_repository_test {
is_deleted: false, is_deleted: false,
avatar: None, avatar: None,
phone_number: "081234567890".to_string(), phone_number: "081234567890".to_string(),
referral_code: None,
referred_by: None,
identity_number: None,
is_active: true, is_active: true,
student_type: "general".to_string(),
religion: None,
gender: None, gender: None,
birthdate: None, birthdate: None,
is_profile_completed: false,
role: make_thing("roles", "user"), role: make_thing("roles", "user"),
created_at: get_iso_date(), created_at: get_iso_date(),
updated_at: get_iso_date(), updated_at: get_iso_date(),
@@ -59,13 +53,7 @@ mod auth_repository_test {
email: email.clone(), email: email.clone(),
avatar: None, avatar: None,
phone_number: "08123456789".into(), phone_number: "08123456789".into(),
referred_by: None,
referral_code: None,
student_type: "TNI".into(),
is_active: true, is_active: true,
is_profile_completed: false,
identity_number: None,
religion: None,
gender: None, gender: None,
birthdate: None, birthdate: None,
role: RolesItemDtoRaw { role: RolesItemDtoRaw {
-9
View File
@@ -82,14 +82,8 @@ impl AuthService {
is_active: user.is_active.clone(), is_active: user.is_active.clone(),
avatar: user.avatar.clone(), avatar: user.avatar.clone(),
phone_number: user.phone_number.clone(), phone_number: user.phone_number.clone(),
referral_code: user.referral_code.clone(),
referred_by: user.referred_by.clone(),
identity_number: user.identity_number.clone(),
student_type: user.student_type.clone(),
religion: user.religion.clone(),
gender: user.gender.clone(), gender: user.gender.clone(),
birthdate: user.birthdate.clone(), birthdate: user.birthdate.clone(),
is_profile_completed: user.is_profile_completed.clone(),
role: RolesItemDto { role: RolesItemDto {
id: role_response.id, id: role_response.id,
name: role_response.name, name: role_response.name,
@@ -187,9 +181,6 @@ impl AuthService {
fullname: new_user.fullname.clone(), fullname: new_user.fullname.clone(),
password: new_user.password.clone(), password: new_user.password.clone(),
phone_number: new_user.phone_number.clone(), phone_number: new_user.phone_number.clone(),
referral_code: new_user.referral_code.clone(),
referred_by: new_user.referred_by.clone(),
student_type: new_user.student_type.clone(),
created_at: get_iso_date(), created_at: get_iso_date(),
updated_at: get_iso_date(), updated_at: get_iso_date(),
role: role_thing, role: role_thing,
+2 -2
View File
@@ -69,8 +69,8 @@ use utoipa::{
) )
), ),
info( info(
title = "NAJM Course API", title = "IMPHNEN Backend Service",
description = "NAJM Course API", description = "IMPHNEN Backend Service for Provide Gacha, Dimentorin and Backoffice Web App",
version = "0.1.0", version = "0.1.0",
contact( contact(
name = "Maulana Sodiqin", name = "Maulana Sodiqin",
@@ -119,11 +119,8 @@ async fn test_create_user_should_return_201() {
email: format!("test-{}@create.com", Uuid::new_v4()).into(), email: format!("test-{}@create.com", Uuid::new_v4()).into(),
role_id, role_id,
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
let res = server.post("/v1/users/create").json(&payload).await; let res = server.post("/v1/users/create").json(&payload).await;
assert_eq!(res.status_code(), StatusCode::CREATED); assert_eq!(res.status_code(), StatusCode::CREATED);
@@ -148,11 +145,8 @@ async fn test_get_user_detail_should_return_200() {
email: "detail@test.com".into(), email: "detail@test.com".into(),
role_id, role_id,
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
server.post("/v1/users/create").json(&payload).await; server.post("/v1/users/create").json(&payload).await;
let user = repo.query_user_by_email(payload.email).await.unwrap(); let user = repo.query_user_by_email(payload.email).await.unwrap();
@@ -178,12 +172,9 @@ async fn test_delete_user_should_return_200() {
fullname: "Test Delete Me".into(), fullname: "Test Delete Me".into(),
email: "test@delete-me.com".into(), email: "test@delete-me.com".into(),
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
role_id: role.id.clone(), role_id: role.id.clone(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
let create_res = server.post("/v1/users/create").json(&payload).await; let create_res = server.post("/v1/users/create").json(&payload).await;
assert_eq!(create_res.status_code(), StatusCode::CREATED); assert_eq!(create_res.status_code(), StatusCode::CREATED);
@@ -216,11 +207,8 @@ async fn test_activate_user_should_return_200() {
email: format!("inactive-{}@test.com", Uuid::new_v4()).into(), email: format!("inactive-{}@test.com", Uuid::new_v4()).into(),
password: "Password1!".into(), password: "Password1!".into(),
role_id, role_id,
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: false, is_active: false,
referral_code: None,
referred_by: None,
}; };
let res_create = server.post("/v1/users/create").json(&payload).await; let res_create = server.post("/v1/users/create").json(&payload).await;
dbg!(res_create.text()); dbg!(res_create.text());
@@ -255,11 +243,8 @@ async fn test_update_user_should_return_200() {
email: unique_email.clone(), email: unique_email.clone(),
password: "Password1!".into(), password: "Password1!".into(),
role_id: role_id.clone(), role_id: role_id.clone(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
let create_res = server.post("/v1/users/create").json(&payload).await; let create_res = server.post("/v1/users/create").json(&payload).await;
assert_eq!(create_res.status_code(), StatusCode::CREATED); assert_eq!(create_res.status_code(), StatusCode::CREATED);
@@ -268,14 +253,9 @@ async fn test_update_user_should_return_200() {
let update_payload = UsersUpdateRequestDto { let update_payload = UsersUpdateRequestDto {
fullname: "Updated Name".into(), fullname: "Updated Name".into(),
email: payload.email.clone(), email: payload.email.clone(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
role_id: user.role.id.id.to_raw(), role_id: user.role.id.id.to_raw(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
identity_number: Some("1234567890123456".into()),
religion: Some("Islam".into()),
gender: Some("Laki-laki".into()), gender: Some("Laki-laki".into()),
birthdate: Some("2000-01-01".into()), birthdate: Some("2000-01-01".into()),
avatar: None, avatar: None,
@@ -307,12 +287,9 @@ async fn test_create_user_should_fail_if_email_taken() {
fullname: "User Satu".into(), fullname: "User Satu".into(),
email: email.clone(), email: email.clone(),
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
role_id, role_id,
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
let res1 = server.post("/v1/users/create").json(&payload).await; let res1 = server.post("/v1/users/create").json(&payload).await;
assert_eq!(res1.status_code(), StatusCode::CREATED); assert_eq!(res1.status_code(), StatusCode::CREATED);
@@ -354,11 +331,8 @@ async fn test_delete_user_should_fail_if_already_deleted() {
email: "delete-me@example.com".into(), email: "delete-me@example.com".into(),
role_id, role_id,
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
server.post("/v1/users/create").json(&payload).await; server.post("/v1/users/create").json(&payload).await;
let user = repo.query_user_by_email(payload.email).await.unwrap(); let user = repo.query_user_by_email(payload.email).await.unwrap();
@@ -387,14 +361,9 @@ async fn test_update_user_should_fail_if_user_not_found() {
let update_payload = UsersUpdateRequestDto { let update_payload = UsersUpdateRequestDto {
fullname: "Does Not Exist".into(), fullname: "Does Not Exist".into(),
email: "nonexistent@test.com".into(), email: "nonexistent@test.com".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
role_id, role_id,
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
identity_number: Some("1234567890123456".into()),
religion: Some("Islam".into()),
gender: Some("Laki-laki".into()), gender: Some("Laki-laki".into()),
birthdate: Some("2000-01-01".into()), birthdate: Some("2000-01-01".into()),
avatar: None, avatar: None,
@@ -526,11 +495,8 @@ async fn test_user_detail_should_fail_if_user_is_soft_deleted() {
fullname: "Soft Deleted".into(), fullname: "Soft Deleted".into(),
email: "softdeleted@test.com".into(), email: "softdeleted@test.com".into(),
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
server.post("/v1/users/create").json(&payload).await; server.post("/v1/users/create").json(&payload).await;
let user = repo.query_user_by_email(payload.email).await.unwrap(); let user = repo.query_user_by_email(payload.email).await.unwrap();
@@ -562,11 +528,8 @@ async fn test_update_user_should_fail_if_user_is_deleted() {
fullname: "To Be Deleted".into(), fullname: "To Be Deleted".into(),
email: "deleteduser@test.com".into(), email: "deleteduser@test.com".into(),
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
server.post("/v1/users/create").json(&payload).await; server.post("/v1/users/create").json(&payload).await;
let user = repo.query_user_by_email(payload.email).await.unwrap(); let user = repo.query_user_by_email(payload.email).await.unwrap();
@@ -576,13 +539,8 @@ async fn test_update_user_should_fail_if_user_is_deleted() {
role_id: role_id.clone(), role_id: role_id.clone(),
fullname: "Should Fail".into(), fullname: "Should Fail".into(),
email: "deleteduser@test.com".into(), email: "deleteduser@test.com".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
identity_number: Some("1234567890123456".into()),
religion: Some("Islam".into()),
gender: Some("Laki-laki".into()), gender: Some("Laki-laki".into()),
birthdate: Some("2000-01-01".into()), birthdate: Some("2000-01-01".into()),
avatar: None, avatar: None,
@@ -615,11 +573,8 @@ async fn test_update_user_should_fail_if_payload_invalid() {
email: "invalid@test.com".into(), email: "invalid@test.com".into(),
role_id: role_id.clone(), role_id: role_id.clone(),
password: "Password1!".into(), password: "Password1!".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
referred_by: None,
}; };
server.post("/v1/users/create").json(&payload).await; server.post("/v1/users/create").json(&payload).await;
let user = repo.query_user_by_email(payload.email).await.unwrap(); let user = repo.query_user_by_email(payload.email).await.unwrap();
@@ -627,14 +582,9 @@ async fn test_update_user_should_fail_if_payload_invalid() {
let update_payload = UsersUpdateRequestDto { let update_payload = UsersUpdateRequestDto {
fullname: "".into(), fullname: "".into(),
email: "invalid@test.com".into(), email: "invalid@test.com".into(),
student_type: "general".into(),
phone_number: "081234567890".into(), phone_number: "081234567890".into(),
is_active: true, is_active: true,
referral_code: None,
role_id: role_id.clone(), role_id: role_id.clone(),
referred_by: None,
identity_number: Some("1234567890123456".into()),
religion: Some("Islam".into()),
gender: Some("Laki-laki".into()), gender: Some("Laki-laki".into()),
birthdate: Some("2000-01-01".into()), birthdate: Some("2000-01-01".into()),
avatar: None, avatar: None,
+4 -44
View File
@@ -34,19 +34,11 @@ pub struct UsersCreateRequestDto {
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 = 1, message = "Student type is required"))]
pub student_type: 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,
#[validate(length(
max = 4,
message = "Referal code cannot be more than 4 character"
))]
pub referral_code: Option<String>,
pub referred_by: Option<String>,
pub is_active: bool, pub is_active: bool,
pub role_id: String, pub role_id: String,
} }
@@ -64,24 +56,12 @@ pub struct UsersUpdateRequestDto {
))] ))]
#[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 = 1, message = "Student type is required"))]
pub student_type: 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,
#[validate(length(
max = 4,
message = "Referal code cannot be more than 4 character"
))]
pub referral_code: Option<String>,
pub referred_by: Option<String>,
pub is_active: bool, pub is_active: bool,
#[validate(length(min = 16, message = "NIK at least have 16 character"))]
pub identity_number: Option<String>,
#[validate(length(min = 1, message = "Religion is required"))]
pub religion: Option<String>,
#[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"))]
@@ -99,14 +79,8 @@ pub struct UsersItemDto {
pub email: String, pub email: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub phone_number: String, pub phone_number: String,
pub referred_by: Option<String>,
pub referral_code: Option<String>,
pub student_type: String,
pub is_active: bool, pub is_active: bool,
pub is_profile_completed: bool,
pub is_deleted: bool, pub is_deleted: bool,
pub identity_number: Option<String>,
pub religion: Option<String>,
pub gender: Option<String>, pub gender: Option<String>,
pub birthdate: Option<String>, pub birthdate: Option<String>,
pub password: String, pub password: String,
@@ -122,13 +96,7 @@ pub struct UsersDetailItemDto {
pub email: String, pub email: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub phone_number: String, pub phone_number: String,
pub referred_by: Option<String>,
pub referral_code: Option<String>,
pub student_type: String,
pub is_active: bool, pub is_active: bool,
pub is_profile_completed: bool,
pub identity_number: Option<String>,
pub religion: Option<String>,
pub gender: Option<String>, pub gender: Option<String>,
pub birthdate: Option<String>, pub birthdate: Option<String>,
} }
@@ -141,10 +109,9 @@ pub struct UsersListItemDto {
pub email: String, pub email: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub phone_number: String, pub phone_number: String,
pub referred_by: Option<String>,
pub referral_code: Option<String>,
pub student_type: String,
pub is_active: bool, pub is_active: bool,
pub created_at: String,
pub updated_at: String,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -155,10 +122,9 @@ pub struct UsersListItemDtoRaw {
pub email: String, pub email: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub phone_number: String, pub phone_number: String,
pub referred_by: Option<String>,
pub referral_code: Option<String>,
pub student_type: String,
pub is_active: bool, pub is_active: bool,
pub created_at: String,
pub updated_at: String,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -168,14 +134,8 @@ pub struct UsersItemDtoRaw {
pub email: String, pub email: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub phone_number: String, pub phone_number: String,
pub referred_by: Option<String>,
pub referral_code: Option<String>,
pub student_type: String,
pub is_active: bool, pub is_active: bool,
pub is_profile_completed: bool,
pub is_deleted: bool, pub is_deleted: bool,
pub identity_number: Option<String>,
pub religion: Option<String>,
pub gender: Option<String>, pub gender: Option<String>,
pub birthdate: Option<String>, pub birthdate: Option<String>,
pub role: RolesItemDtoRaw, pub role: RolesItemDtoRaw,
+5 -34
View File
@@ -23,37 +23,28 @@ impl<'a> UsersRepository<'a> {
meta: MetaRequestDto, meta: MetaRequestDto,
) -> Result<ResponseListSuccessDto<Vec<UsersListItemDto>>> { ) -> Result<ResponseListSuccessDto<Vec<UsersListItemDto>>> {
let db = &self.state.surrealdb_ws; let db = &self.state.surrealdb_ws;
let mut conditions = vec!["is_deleted = false".to_string()]; let mut conditions = vec!["is_deleted = false".to_string()];
if let Some(search) = meta.search.as_deref() { if let Some(search) = meta.search.as_deref() {
if !search.is_empty() { if !search.is_empty() {
conditions.push("string::contains(fullname ?? '', $search)".to_string()); conditions.push("string::contains(fullname ?? '', $search)".to_string());
} }
} }
if let (Some(filter_by), Some(_filter)) = if let (Some(filter_by), Some(_filter)) =
(meta.filter_by.as_ref(), meta.filter.as_ref()) (meta.filter_by.as_ref(), meta.filter.as_ref())
{ {
conditions.push(format!("{} = $filter", filter_by)); conditions.push(format!("{} = $filter", filter_by));
} }
let where_clause = if !conditions.is_empty() { let where_clause = if !conditions.is_empty() {
format!("WHERE {}", conditions.join(" AND ")) format!("WHERE {}", conditions.join(" AND "))
} else { } else {
String::new() String::new()
}; };
let limit = meta.per_page.unwrap_or(10); let limit = meta.per_page.unwrap_or(10);
let page = meta.page.unwrap_or(1); let page = meta.page.unwrap_or(1);
// Validate pagination parameters explicitly
if page < 1 || limit < 1 { if page < 1 || limit < 1 {
return Err(anyhow!("Invalid pagination parameters")); return Err(anyhow!("Invalid pagination parameters"));
} }
let start = (page - 1) * limit; let start = (page - 1) * limit;
let select_query = format!( let select_query = format!(
" "
SELECT SELECT
@@ -63,15 +54,11 @@ impl<'a> UsersRepository<'a> {
email, email,
avatar, avatar,
phone_number, phone_number,
referred_by,
referral_code,
student_type,
is_active, is_active,
is_profile_completed,
identity_number,
religion,
gender, gender,
birthdate birthdate,
created_at,
updated_at
FROM {} FROM {}
{} {}
LIMIT {} START {} LIMIT {} START {}
@@ -82,7 +69,6 @@ impl<'a> UsersRepository<'a> {
limit, limit,
start start
); );
let raw_result = query_list_with_meta::<UsersListItemDtoRaw>( let raw_result = query_list_with_meta::<UsersListItemDtoRaw>(
db, db,
&ResourceEnum::Users.to_string(), &ResourceEnum::Users.to_string(),
@@ -91,7 +77,6 @@ impl<'a> UsersRepository<'a> {
Some(select_query), Some(select_query),
) )
.await?; .await?;
let transformed_data = raw_result let transformed_data = raw_result
.data .data
.into_iter() .into_iter()
@@ -101,14 +86,12 @@ impl<'a> UsersRepository<'a> {
email: user.email, email: user.email,
avatar: user.avatar, avatar: user.avatar,
phone_number: user.phone_number, phone_number: user.phone_number,
referred_by: user.referred_by,
referral_code: user.referral_code,
student_type: user.student_type,
is_active: user.is_active, is_active: user.is_active,
role: user.role.unwrap_or_else(|| "-".into()), role: user.role.unwrap_or_else(|| "-".into()),
created_at: user.created_at,
updated_at: user.updated_at,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Ok(ResponseListSuccessDto { Ok(ResponseListSuccessDto {
data: transformed_data, data: transformed_data,
meta: raw_result.meta, meta: raw_result.meta,
@@ -147,14 +130,8 @@ impl<'a> UsersRepository<'a> {
email: user.email, email: user.email,
avatar: user.avatar, avatar: user.avatar,
phone_number: user.phone_number, phone_number: user.phone_number,
referred_by: user.referred_by,
referral_code: user.referral_code,
student_type: user.student_type,
is_active: user.is_active, is_active: user.is_active,
is_deleted: user.is_deleted, is_deleted: user.is_deleted,
is_profile_completed: user.is_profile_completed,
identity_number: user.identity_number,
religion: user.religion,
gender: user.gender, gender: user.gender,
birthdate: user.birthdate, birthdate: user.birthdate,
password: user.password, password: user.password,
@@ -206,14 +183,8 @@ impl<'a> UsersRepository<'a> {
email: user.email, email: user.email,
avatar: user.avatar, avatar: user.avatar,
phone_number: user.phone_number, phone_number: user.phone_number,
referred_by: user.referred_by,
referral_code: user.referral_code,
student_type: user.student_type,
is_active: user.is_active, is_active: user.is_active,
is_deleted: user.is_deleted, is_deleted: user.is_deleted,
is_profile_completed: user.is_profile_completed,
identity_number: user.identity_number,
religion: user.religion,
gender: user.gender, gender: user.gender,
birthdate: user.birthdate, birthdate: user.birthdate,
password: user.password, password: user.password,
+10 -25
View File
@@ -1,9 +1,6 @@
use crate::{get_iso_date, ResourceEnum}; use crate::{get_iso_date, make_thing, ResourceEnum};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use surrealdb::{ use surrealdb::{sql::Thing, Uuid};
sql::{Id, Thing},
Uuid,
};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UsersSchema { pub struct UsersSchema {
@@ -13,16 +10,10 @@ pub struct UsersSchema {
pub password: String, pub password: String,
pub avatar: Option<String>, pub avatar: Option<String>,
pub phone_number: 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 is_deleted: bool, pub is_deleted: bool,
pub student_type: String,
pub religion: Option<String>,
pub gender: Option<String>, pub gender: Option<String>,
pub birthdate: Option<String>, pub birthdate: Option<String>,
pub is_profile_completed: bool,
pub role: Thing, pub role: Thing,
pub created_at: String, pub created_at: String,
pub updated_at: String, pub updated_at: String,
@@ -31,29 +22,23 @@ pub struct UsersSchema {
impl Default for UsersSchema { impl Default for UsersSchema {
fn default() -> Self { fn default() -> Self {
UsersSchema { UsersSchema {
id: Thing::from(( id: make_thing(
ResourceEnum::Users.to_string(), &ResourceEnum::Users.to_string(),
Id::String(Uuid::new_v4().to_string()), &Uuid::new_v4().to_string(),
)), ),
fullname: String::new(), fullname: String::new(),
email: String::new(), email: String::new(),
password: String::new(), password: String::new(),
avatar: None, avatar: None,
phone_number: String::new(), phone_number: String::new(),
referral_code: None,
referred_by: None,
identity_number: None,
is_active: false, is_active: false,
is_deleted: false, is_deleted: false,
student_type: String::new(),
religion: None,
gender: None, gender: None,
birthdate: None, birthdate: None,
is_profile_completed: false, role: make_thing(
role: Thing::from(( &ResourceEnum::Roles.to_string(),
ResourceEnum::Roles.to_string(), &Uuid::new_v4().to_string(),
Id::String(Uuid::new_v4().to_string()), ),
)),
created_at: get_iso_date(), created_at: get_iso_date(),
updated_at: get_iso_date(), updated_at: get_iso_date(),
} }
+3 -28
View File
@@ -43,13 +43,7 @@ impl UsersService {
email: user.email, email: user.email,
avatar: user.avatar, avatar: user.avatar,
phone_number: user.phone_number, phone_number: user.phone_number,
referred_by: user.referred_by,
referral_code: user.referral_code,
student_type: user.student_type,
is_active: user.is_active, is_active: user.is_active,
is_profile_completed: user.is_profile_completed,
identity_number: user.identity_number,
religion: user.religion,
gender: user.gender, gender: user.gender,
birthdate: user.birthdate, birthdate: user.birthdate,
}, },
@@ -72,13 +66,7 @@ impl UsersService {
email: user.email, email: user.email,
avatar: user.avatar, avatar: user.avatar,
phone_number: user.phone_number, phone_number: user.phone_number,
referred_by: user.referred_by,
referral_code: user.referral_code,
student_type: user.student_type,
is_active: user.is_active, is_active: user.is_active,
is_profile_completed: user.is_profile_completed,
identity_number: user.identity_number,
religion: user.religion,
gender: user.gender, gender: user.gender,
birthdate: user.birthdate, birthdate: user.birthdate,
}, },
@@ -109,12 +97,8 @@ impl UsersService {
fullname: new_user.fullname.clone(), fullname: new_user.fullname.clone(),
password: hash_password(&new_user.password).unwrap(), password: hash_password(&new_user.password).unwrap(),
phone_number: new_user.phone_number.clone(), phone_number: new_user.phone_number.clone(),
referral_code: new_user.referral_code.clone(),
referred_by: new_user.referred_by.clone(),
student_type: new_user.student_type.clone(),
is_active: new_user.is_active.clone(), is_active: new_user.is_active.clone(),
role: role_thing, role: role_thing,
is_profile_completed: false,
..Default::default() ..Default::default()
}) })
.await .await
@@ -145,16 +129,10 @@ impl UsersService {
fullname: user.fullname, fullname: user.fullname,
email: user.email, email: user.email,
phone_number: user.phone_number, phone_number: user.phone_number,
referral_code: user.referral_code,
referred_by: user.referred_by,
identity_number: user.identity_number,
is_active: user.is_active, is_active: user.is_active,
student_type: user.student_type,
religion: user.religion,
gender: user.gender, gender: user.gender,
birthdate: user.birthdate, birthdate: user.birthdate,
avatar: user.avatar, avatar: user.avatar,
is_profile_completed: false,
role: role_id, role: role_id,
updated_at: get_iso_date(), updated_at: get_iso_date(),
..Default::default() ..Default::default()
@@ -185,16 +163,13 @@ impl UsersService {
fullname: user.fullname, fullname: user.fullname,
email: user.email, email: user.email,
phone_number: user.phone_number, phone_number: user.phone_number,
referral_code: user.referral_code,
referred_by: user.referred_by,
identity_number: user.identity_number,
is_active: user.is_active, is_active: user.is_active,
student_type: user.student_type,
religion: user.religion,
gender: user.gender, gender: user.gender,
birthdate: user.birthdate, birthdate: user.birthdate,
avatar: user.avatar, avatar: user.avatar,
is_profile_completed: true,
role: role_id, role: role_id,
updated_at: get_iso_date(), updated_at: get_iso_date(),
..Default::default() ..Default::default()
+1 -1
View File
@@ -1,4 +1,4 @@
use najm_course_api::{apps, axum_init}; use imphnen_backend_service::{apps, axum_init};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
+1 -1
View File
@@ -1,4 +1,4 @@
use najm_course_api::{get_iso_date, Env}; use imphnen_backend_service::{get_iso_date, Env};
use serde_json::json; use serde_json::json;
use std::error::Error; use std::error::Error;
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal}; use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};
+1 -1
View File
@@ -1,4 +1,4 @@
use najm_course_api::{get_iso_date, Env}; use imphnen_backend_service::{get_iso_date, Env};
use serde_json::json; use serde_json::json;
use std::error::Error; use std::error::Error;
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal}; use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};
+1 -1
View File
@@ -1,4 +1,4 @@
use najm_course_api::{get_iso_date, make_thing, Env}; use imphnen_backend_service::{get_iso_date, make_thing, Env};
use std::error::Error; use std::error::Error;
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal}; use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};
+1 -1
View File
@@ -1,4 +1,4 @@
use najm_course_api::{get_iso_date, hash_password, Env}; use imphnen_backend_service::{get_iso_date, hash_password, Env};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::error::Error; use std::error::Error;
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, sql::Thing, Surreal}; use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, sql::Thing, Surreal};
+1 -1
View File
@@ -1,4 +1,4 @@
use najm_course_api::{apps, axum_init}; use imphnen_backend_service::{apps, axum_init};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
-6
View File
@@ -66,15 +66,9 @@ pub fn create_test_user(
is_deleted: false, is_deleted: false,
avatar: None, avatar: None,
phone_number: "081234567890".to_string(), phone_number: "081234567890".to_string(),
referral_code: None,
referred_by: None,
identity_number: None,
is_active, is_active,
student_type: "TNI".to_string(),
religion: None,
gender: None, gender: None,
birthdate: None, birthdate: None,
is_profile_completed: false,
role: make_thing("app_roles", role_id), role: make_thing("app_roles", role_id),
..Default::default() ..Default::default()
} }