diff --git a/Cargo.lock b/Cargo.lock index 910ddca..71e6017 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,18 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "affinitypool" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dde2a385b82232b559baeec740c37809051c596f9b56e7da0d0da2c8e8f54f6" +dependencies = [ + "async-channel", + "num_cpus", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "ahash" version = "0.7.8" @@ -142,12 +154,6 @@ dependencies = [ "derive_arbitrary", ] -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - [[package]] name = "argon2" version = "0.5.3" @@ -738,20 +744,6 @@ dependencies = [ "inout", ] -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1074,6 +1066,19 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "ext-sort" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf73e44617eab501beba39234441a194cf138629d3b6447f81f573e1c3d0a13" +dependencies = [ + "log", + "rayon", + "rmp-serde", + "serde", + "tempfile", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -2008,6 +2013,15 @@ version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.2", +] + [[package]] name = "mac" version = "0.1.1" @@ -2161,7 +2175,6 @@ dependencies = [ "lettre", "log", "rand 0.9.0", - "redis", "regex", "serde", "serde_json", @@ -2704,6 +2717,18 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "quick_cache" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f8ed0655cbaf18a26966142ad23b95d8ab47221c50c4f73a1db7d0d2d6e3da8" +dependencies = [ + "ahash 0.8.11", + "equivalent", + "hashbrown 0.15.2", + "parking_lot", +] + [[package]] name = "quinn" version = "0.11.7" @@ -2888,28 +2913,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffead9d0a0b45f3e0bc063a244b1779fd53a09d2c2f7282c186a016b1f10a778" -[[package]] -name = "redis" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37ec3fd44bea2ec947ba6cc7634d7999a6590aca7c35827c250bc0de502bda6" -dependencies = [ - "arc-swap", - "bytes", - "combine", - "futures-util", - "itoa", - "num-bigint", - "percent-encoding", - "pin-project-lite", - "ryu", - "sha1_smol", - "socket2", - "tokio", - "tokio-util", - "url", -] - [[package]] name = "redox_syscall" version = "0.5.10" @@ -3034,6 +3037,15 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "revision" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f53179a035f881adad8c4d58a2c599c6b4a8325b989c68d178d7a34d1b1e4c" +dependencies = [ + "revision-derive 0.10.0", +] + [[package]] name = "revision" version = "0.11.0" @@ -3043,12 +3055,23 @@ dependencies = [ "chrono", "geo", "regex", - "revision-derive", + "revision-derive 0.11.0", "roaring", "rust_decimal", "uuid", ] +[[package]] +name = "revision-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0ec466e5d8dca9965eb6871879677bef5590cf7525ad96cae14376efb75073" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "revision-derive" version = "0.11.0" @@ -3114,6 +3137,17 @@ dependencies = [ "paste", ] +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rmpv" version = "1.3.0" @@ -3505,12 +3539,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.10.8" @@ -3752,7 +3780,7 @@ dependencies = [ "pharos", "reblessive", "reqwest", - "revision", + "revision 0.11.0", "ring", "rust_decimal", "rustls", @@ -3782,6 +3810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ceb9f421e07af67b06c57ea7a8b08c3b4e4677de483435ca6d69bd00600a571" dependencies = [ "addr", + "affinitypool", "ahash 0.8.11", "ammonia", "any_ascii", @@ -3801,6 +3830,7 @@ dependencies = [ "dashmap", "deunicode", "dmp", + "ext-sort", "fst", "futures", "fuzzy-matcher", @@ -3824,13 +3854,13 @@ dependencies = [ "pharos", "phf", "pin-project-lite", - "quick_cache", + "quick_cache 0.5.2", "radix_trie", "rand 0.8.5", "rayon", "reblessive", "regex", - "revision", + "revision 0.11.0", "ring", "rmpv", "roaring", @@ -3847,7 +3877,9 @@ dependencies = [ "storekey", "strsim", "subtle", + "surrealkv", "sysinfo", + "tempfile", "thiserror 1.0.69", "tokio", "tracing", @@ -3856,12 +3888,34 @@ dependencies = [ "unicase", "url", "uuid", - "vart", + "vart 0.8.1", "wasm-bindgen-futures", "wasmtimer", "ws_stream_wasm", ] +[[package]] +name = "surrealkv" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28eec56aca077c245bf5f9e08876fdcce58b5361e77a0b94a92fd47e1990ad4" +dependencies = [ + "ahash 0.8.11", + "async-channel", + "bytes", + "chrono", + "crc32fast", + "futures", + "getrandom 0.2.15", + "lru", + "parking_lot", + "quick_cache 0.6.12", + "revision 0.10.0", + "tokio", + "vart 0.9.2", + "wasm-bindgen-futures", +] + [[package]] name = "syn" version = "1.0.109" @@ -4498,6 +4552,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87782b74f898179396e93c0efabb38de0d58d50bbd47eae00c71b3a1144dbbae" +[[package]] +name = "vart" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03dccea250abfe68c00eee55f95af111e041b75bc11796cb83d1c05c5029efd9" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 4fc295c..9b143b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,8 @@ jsonwebtoken = "9.3.1" chrono = "0.4.39" utoipa = { version = "5.3.1", features = ["axum_extras"] } utoipa-swagger-ui = { version = "9.0.0", features = ["axum"] } -redis = { version = "0.28.2", features = ["tokio-comp"] } lettre = { version = "0.11.12", features = ["tokio1-native-tls"] } -surrealdb = { version = "2.2.1", features = ["protocol-http"] } +surrealdb = { version = "2.2.1", features = ["kv-mem", "protocol-http"] } thiserror = "2.0.11" anyhow = "1.0.97" rand = "0.9.0" diff --git a/src/apps/mod.rs b/src/apps/mod.rs index be1600f..9d57e72 100644 --- a/src/apps/mod.rs +++ b/src/apps/mod.rs @@ -1,4 +1,4 @@ -use crate::{AppState, Env, RedisClient, SurrealClient}; +use crate::{AppState, Env, SurrealMemClient, SurrealWsClient}; use axum::{ http::{header, HeaderValue, Method}, Extension, Router, @@ -11,8 +11,14 @@ pub mod v2; pub use v1::*; -pub async fn apps(surrealdb: SurrealClient, redisdb: RedisClient) -> Router { - let state = AppState { surrealdb, redisdb }; +pub async fn apps( + surrealdb_ws: SurrealWsClient, + surrealdb_mem: SurrealMemClient, +) -> Router { + let state = AppState { + surrealdb_ws, + surrealdb_mem, + }; let env = Env::new(); let cors_origins = match env.rust_env.as_str() { "development" => vec!["http://localhost:3000"], diff --git a/src/apps/v1/auth/auth_repository.rs b/src/apps/v1/auth/auth_repository.rs index 8b01151..4fa1687 100644 --- a/src/apps/v1/auth/auth_repository.rs +++ b/src/apps/v1/auth/auth_repository.rs @@ -1,6 +1,8 @@ -use crate::{AppState, RedisKeyEnum, UsersSchema}; +use crate::{AppState, ResourceEnum, UsersSchema}; use anyhow::{anyhow, bail, Result}; -use redis::Commands; +use chrono::{Duration, Utc}; + +use super::AuthOtpSchema; pub struct AuthRepository<'a> { state: &'a AppState, @@ -11,76 +13,90 @@ impl<'a> AuthRepository<'a> { Self { state } } - pub fn query_store_user_data(&self, user: UsersSchema) -> Result { - let redis_key = format!("{}:{}", RedisKeyEnum::User, user.email.clone()); - match &self.state.redisdb.get_connection().and_then(|mut conn| { - conn.set_ex::<_, String, ()>( - &redis_key, - serde_json::to_string(&user).unwrap_or_default(), - 86400, - ) - }) { - Ok(_) => Ok("Success store user data".to_string()), - Err(err) => Ok(format!("Redis storage failed: {}", err)), + pub async fn query_store_user(&self, user: UsersSchema) -> Result { + let user_clone = user.clone(); + let record: Option = self + .state + .surrealdb_mem + .update((ResourceEnum::UsersCache.to_string(), user.email)) + .content(user_clone) + .await?; + match record { + Some(_) => Ok("Success store user data".to_string()), + None => bail!("Failed store user data"), } } - pub fn query_get_stored_user(&self, email: String) -> Result { - let redis_key = format!("{}:{}", RedisKeyEnum::User, email); - let mut conn = self.state.redisdb.get_connection()?; - let data: Option = conn.get(&redis_key)?; - match data { - Some(user_json) => { - let user: UsersSchema = serde_json::from_str(&user_json)?; - Ok(user) - } + pub async fn query_get_stored_user(&self, email: String) -> Result { + let user: Option = self + .state + .surrealdb_mem + .select((ResourceEnum::UsersCache.to_string(), email)) + .await?; + match user { + Some(u) => Ok(u), None => bail!("No stored user data found"), } } - pub fn query_get_stored_otp(&self, email: String) -> Result { - let redis_key = format!("{}:{}", RedisKeyEnum::Otp, email); - let mut conn = match self.state.redisdb.get_connection() { - Ok(conn) => conn, - Err(e) => { - return Err(anyhow::anyhow!("Failed to get Redis connection: {}", e)) + pub async fn query_delete_stored_user(&self, email: String) -> Result { + let record: Option = self + .state + .surrealdb_mem + .delete((ResourceEnum::UsersCache.to_string(), email)) + .await?; + match record { + Some(_) => Ok("Success delete stored user".to_string()), + None => bail!("Failed delete stored user"), + } + } + + pub async fn query_get_stored_otp(&self, email: String) -> Result { + let otp: Option = self + .state + .surrealdb_mem + .select((ResourceEnum::OtpCache.to_string(), &email)) + .await?; + match otp { + Some(data) => { + if Utc::now() > data.expires_at { + let _: Option = self + .state + .surrealdb_mem + .delete((ResourceEnum::OtpCache.to_string(), &email)) + .await?; + Err(anyhow!("OTP expired")) + } else { + Ok(data.otp) + } } - }; - let data: Option = match conn.get(&redis_key) { - Ok(data) => data, - Err(e) => return Err(anyhow::anyhow!("Failed to get data from Redis: {}", e)), - }; - match data { - Some(otp_str) => match otp_str.parse::() { - Ok(otp) => Ok(otp), - Err(e) => Err(anyhow::anyhow!("Failed to parse OTP as u64: {}", e)), - }, - None => Err(anyhow::anyhow!("No stored OTP found")), + None => Err(anyhow!("No stored OTP found")), } } - pub fn query_store_otp(&self, email: String, otp: u32) -> Result { - let redis_key: String = format!("{}:{}", RedisKeyEnum::Otp, email); - let mut conn = match self.state.redisdb.get_connection() { - Ok(conn) => conn, - Err(e) => return Err(anyhow!("Failed to get Redis connection: {}", e)), - }; - let otp_str: String = otp.to_string(); - match conn.set_ex::<_, _, ()>(&redis_key, &otp_str, 300) { - Ok(_) => Ok("Success store otp".to_string()), - Err(e) => Err(anyhow!("Failed to store OTP in Redis: {}", e)), + pub async fn query_store_otp(&self, email: String, otp: u32) -> Result { + let expires_at = Utc::now() + Duration::seconds(300); // 5 menit + let record: Option = self + .state + .surrealdb_mem + .create((ResourceEnum::OtpCache.to_string(), email)) + .content(AuthOtpSchema { otp, expires_at }) + .await?; + match record { + Some(_) => Ok("Success store otp".to_string()), + None => bail!("Failed store otp"), } } - pub fn query_delete_stored_otp(&self, email: String) -> Result { - let redis_key = format!("{}:{}", RedisKeyEnum::Otp, email); - let mut conn = match self.state.redisdb.get_connection() { - Ok(conn) => conn, - Err(e) => return Err(anyhow!("Failed to get Redis connection: {}", e)), - }; - match conn.del::<_, ()>(&redis_key) { - Ok(_) => Ok("Successfully deleted OTP".to_string()), - Err(e) => Err(anyhow!("Failed to delete OTP from Redis: {}", e)), + pub async fn query_delete_stored_otp(&self, email: String) -> Result { + let record: Option = self + .state + .surrealdb_mem + .delete((ResourceEnum::OtpCache.to_string(), email)) + .await?; + match record { + Some(_) => Ok("Success delete stored otp".to_string()), + None => bail!("Failed delete stored otp"), } } } diff --git a/src/apps/v1/auth/auth_schema.rs b/src/apps/v1/auth/auth_schema.rs new file mode 100644 index 0000000..cf715ae --- /dev/null +++ b/src/apps/v1/auth/auth_schema.rs @@ -0,0 +1,8 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AuthOtpSchema { + pub otp: u32, + pub expires_at: DateTime, +} diff --git a/src/apps/v1/auth/auth_service.rs b/src/apps/v1/auth/auth_service.rs index e57de29..f07c349 100644 --- a/src/apps/v1/auth/auth_service.rs +++ b/src/apps/v1/auth/auth_service.rs @@ -83,7 +83,7 @@ impl AuthService { }, }; - if let Err(_) = auth_repo.query_store_user_data(user) { + if let Err(_) = auth_repo.query_store_user(user).await { return common_response(StatusCode::BAD_REQUEST, "Failed to store data"); } @@ -130,20 +130,37 @@ impl AuthService { let otp = generate_otp::OtpManager::generate_otp(); - auth_repo + match auth_repo .query_store_otp(new_user.email.clone(), otp.clone()) - .unwrap(); + .await + { + Ok(_) => { + let message = format!("your otp code is {}", otp); + if let Err(err) = send_email(&new_user.email, "OTP Verification", &message) { + return common_response( + StatusCode::INTERNAL_SERVER_ERROR, + &err.to_string(), + ); + } + } + Err(err) => { + return common_response(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string()); + } + } - let message = format!("your otp code is {}", otp); + let role_thing = Thing::from(( + ResourceEnum::Roles.to_string(), + Id::String(Uuid::new_v4().to_string()), + )); - send_email(&new_user.email.clone(), "OTP Verification", &message).unwrap(); - - let role_thing = - Thing::from((ResourceEnum::Roles.to_string(), Id::String("".to_string()))); + let user_thing = Thing::from(( + ResourceEnum::Users.to_string(), + Id::String(Uuid::new_v4().to_string()), + )); match user_repo .query_create_user(UsersSchema { - id: Uuid::new_v4().to_string(), + id: user_thing, email: new_user.email.clone(), fullname: new_user.fullname.clone(), password: new_user.password.clone(), @@ -160,7 +177,7 @@ impl AuthService { }) .await { - Ok(_) => common_response(StatusCode::CREATED, "Registration successful"), + Ok(msg) => common_response(StatusCode::CREATED, &msg), Err(err) => { common_response(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string()) } @@ -174,7 +191,7 @@ impl AuthService { let repository = AuthRepository::new(state); let otp = generate_otp::OtpManager::generate_otp(); let message = format!("Your OTP code is {}", otp); - match repository.query_store_otp(payload.email.clone(), otp) { + match repository.query_store_otp(payload.email.clone(), otp).await { Ok(_) => match send_email(&payload.email, "OTP Verification", &message) { Ok(_) => common_response(StatusCode::OK, "OTP resent successfully"), Err(err) => common_response(StatusCode::BAD_REQUEST, &err.to_string()), @@ -224,25 +241,26 @@ impl AuthService { let user_repo = UsersRepository::new(state); let auth_repo = AuthRepository::new(state); - match auth_repo.query_get_stored_otp(payload.email.clone()) { + match auth_repo.query_get_stored_otp(payload.email.clone()).await { Ok(stored_otp) => { let user_otp = payload.otp; let is_otp_valid = stored_otp == user_otp; if is_otp_valid { match user_repo - .query_active_inactive_user(UsersActiveInactiveSchema { - email: payload.email.clone(), - is_active: true, - }) + .query_active_inactive_user( + payload.email.clone(), + UsersActiveInactiveSchema { is_active: true }, + ) .await { Ok(_) => { - if let Err(e) = - auth_repo.query_delete_stored_otp(payload.email.clone()) + if let Err(e) = auth_repo + .query_delete_stored_otp(payload.email.clone()) + .await { return common_response( StatusCode::INTERNAL_SERVER_ERROR, - &format!("Failed to delete OTP: {}", e), + &e.to_string(), ); } common_response(StatusCode::OK, "Email verified successfully") @@ -250,7 +268,10 @@ impl AuthService { Err(err) => common_response(StatusCode::BAD_REQUEST, &err.to_string()), } } else { - if let Err(e) = auth_repo.query_delete_stored_otp(payload.email.clone()) { + if let Err(e) = auth_repo + .query_delete_stored_otp(payload.email.clone()) + .await + { return common_response( StatusCode::INTERNAL_SERVER_ERROR, &format!("Failed to delete OTP: {}", e), diff --git a/src/apps/v1/auth/mod.rs b/src/apps/v1/auth/mod.rs index 4645812..dd94e19 100644 --- a/src/apps/v1/auth/mod.rs +++ b/src/apps/v1/auth/mod.rs @@ -4,10 +4,12 @@ pub mod auth_controller; pub mod auth_dto; pub mod auth_middleware; pub mod auth_repository; +pub mod auth_schema; pub mod auth_service; pub use auth_dto::*; pub use auth_repository::*; +pub use auth_schema::*; pub use auth_service::*; pub fn auth_router() -> Router { diff --git a/src/apps/v1/users/users_repository.rs b/src/apps/v1/users/users_repository.rs index b829450..a3a95d2 100644 --- a/src/apps/v1/users/users_repository.rs +++ b/src/apps/v1/users/users_repository.rs @@ -1,5 +1,5 @@ use super::{UsersActiveInactiveSchema, UsersSchema, UsersSetNewPasswordSchema}; -use crate::{AppState, ResourceEnum}; +use crate::{AppState, AuthOtpSchema, ResourceEnum}; use anyhow::{bail, Result}; pub struct UsersRepository<'a> { @@ -12,9 +12,28 @@ impl<'a> UsersRepository<'a> { } pub async fn query_user_by_email(&self, email: String) -> Result { - let db = &self.state.surrealdb; + let db = &self.state.surrealdb_ws; + let sql = format!( + "SELECT * FROM {} WHERE email = $email", + ResourceEnum::Users.to_string() + ); + let mut response: Vec = db + .query(sql) + .bind(("email", email.clone())) + .await? + .take(0)?; + + if let Some(user) = response.pop() { + Ok(user) + } else { + bail!("User not found") + } + } + + pub async fn query_user_by_id(&self, id: String) -> Result { + let db = &self.state.surrealdb_ws; let result = db - .select((ResourceEnum::Users.to_string(), email.clone())) + .select((ResourceEnum::Users.to_string(), id.clone())) .await?; match result { Some(response) => Ok(response), @@ -23,9 +42,9 @@ impl<'a> UsersRepository<'a> { } pub async fn query_create_user(&self, data: UsersSchema) -> Result { - let db = &self.state.surrealdb; + let db = &self.state.surrealdb_ws; let record: Option = db - .create((ResourceEnum::Users.to_string(), &data.id)) + .create(ResourceEnum::Users.to_string()) .content(data) .await?; match record { @@ -34,17 +53,11 @@ impl<'a> UsersRepository<'a> { } } - pub async fn query_active_inactive_user( - &self, - data: UsersActiveInactiveSchema, - ) -> Result { - let db = &self.state.surrealdb; - let record: Option = db - .update((ResourceEnum::Users.to_string(), &data.email)) - .merge(UsersActiveInactiveSchema { - email: data.email.clone(), - is_active: data.is_active.clone(), - }) + pub async fn query_update_user(&self, data: UsersSchema) -> Result { + let db = &self.state.surrealdb_ws; + let record: Option = db + .update((ResourceEnum::Users.to_string(), &data.id.id.to_string())) + .merge(data) .await?; match record { Some(_) => Ok("Success update user".into()), @@ -52,11 +65,32 @@ impl<'a> UsersRepository<'a> { } } + pub async fn query_active_inactive_user( + &self, + email: String, + data: UsersActiveInactiveSchema, + ) -> Result { + let db = &self.state.surrealdb_ws; + let user = self.query_user_by_email(email.clone()).await?; + let table = user.id.tb.as_str(); + let id = user.id.id.to_string(); + let result: Option = db + .update((table, id)) + .merge(UsersActiveInactiveSchema { + is_active: data.is_active, + }) + .await?; + match result { + Some(_) => Ok("Success update user".to_string()), + None => bail!("Failed to update user"), + } + } + pub async fn query_update_password_user( &self, data: UsersSetNewPasswordSchema, ) -> Result { - let db = &self.state.surrealdb; + let db = &self.state.surrealdb_ws; let record: Option = db .update((ResourceEnum::Users.to_string(), &data.email)) .merge(UsersSetNewPasswordSchema { diff --git a/src/apps/v1/users/users_schema.rs b/src/apps/v1/users/users_schema.rs index 34d2eb9..53ae4b9 100644 --- a/src/apps/v1/users/users_schema.rs +++ b/src/apps/v1/users/users_schema.rs @@ -1,12 +1,13 @@ -use serde::{Deserialize, Serialize}; -use surrealdb::sql::{Id, Thing}; - use crate::ResourceEnum; +use serde::{Deserialize, Serialize}; +use surrealdb::{ + sql::{Id, Thing}, + Uuid, +}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct UsersSchema { - pub id: String, - pub role_id: String, + pub id: Thing, pub fullname: String, pub email: String, pub password: String, @@ -29,8 +30,10 @@ pub struct UsersSchema { impl Default for UsersSchema { fn default() -> Self { UsersSchema { - id: String::new(), - role_id: String::new(), + id: Thing::from(( + ResourceEnum::Users.to_string(), + Id::String(Uuid::new_v4().to_string()), + )), fullname: String::new(), email: String::new(), password: String::new(), @@ -44,11 +47,11 @@ impl Default for UsersSchema { religion: None, gender: None, birthdate: None, + is_profile_completed: false, role: Thing::from(( ResourceEnum::Roles.to_string(), - Id::String("".to_string()), + Id::String(Uuid::new_v4().to_string()), )), - is_profile_completed: false, created_at: None, updated_at: None, } @@ -63,6 +66,5 @@ pub struct UsersSetNewPasswordSchema { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct UsersActiveInactiveSchema { - pub email: String, pub is_active: bool, } diff --git a/src/entities/common_dto.rs b/src/entities/common_dto.rs index 1ccb16d..4fbc098 100644 --- a/src/entities/common_dto.rs +++ b/src/entities/common_dto.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; -use surrealdb::{engine::remote::ws::Client, Surreal}; +use surrealdb::{ + engine::{local::Db, remote::ws::Client}, + Surreal, +}; use utoipa::{IntoParams, ToSchema}; #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] @@ -37,11 +40,11 @@ pub struct ResponseListSuccessDto { pub meta: Option, } -pub type SurrealClient = Surreal; -pub type RedisClient = redis::Client; +pub type SurrealWsClient = Surreal; +pub type SurrealMemClient = Surreal; #[derive(Clone)] pub struct AppState { - pub surrealdb: SurrealClient, - pub redisdb: RedisClient, + pub surrealdb_ws: SurrealWsClient, + pub surrealdb_mem: SurrealMemClient, } diff --git a/src/libs/axum/mod.rs b/src/libs/axum/mod.rs index 6c6c122..ca3a9bd 100644 --- a/src/libs/axum/mod.rs +++ b/src/libs/axum/mod.rs @@ -1,18 +1,19 @@ -use crate::{redisdb_init, Env, RedisClient}; -use crate::{surrealdb_init, SurrealClient}; +use crate::{ + surrealdb_init_mem, surrealdb_init_ws, Env, SurrealMemClient, SurrealWsClient, +}; use axum::{serve, Router}; use std::{future::Future, net::SocketAddr}; use tokio::net::TcpListener; pub async fn axum_init(router_fn: F) where - F: FnOnce(SurrealClient, RedisClient) -> Fut, + F: FnOnce(SurrealWsClient, SurrealMemClient) -> Fut, Fut: Future, { let env = Env::new(); - let surrealdb = surrealdb_init().await.expect("Failed surrealdb"); - let redisdb = redisdb_init().await.expect("Failed redisdb"); - let router = router_fn(surrealdb, redisdb).await; + let surrealdb_ws = surrealdb_init_ws().await.expect("Failed surrealdb ws"); + let surrealdb_mem = surrealdb_init_mem().await.expect("Failed surrealdb mem"); + let router = router_fn(surrealdb_ws, surrealdb_mem).await; let port = env.port; let addr = SocketAddr::from(([0, 0, 0, 0], port)); let listener = TcpListener::bind(&addr).await.unwrap(); diff --git a/src/libs/mod.rs b/src/libs/mod.rs index ae30feb..b96e744 100644 --- a/src/libs/mod.rs +++ b/src/libs/mod.rs @@ -3,7 +3,6 @@ pub mod axum; pub mod enviroment; pub mod jsonwebtoken; pub mod lettre; -pub mod redisdb; pub mod surrealdb; pub use argon::*; @@ -11,5 +10,4 @@ pub use axum::*; pub use enviroment::*; pub use jsonwebtoken::*; pub use lettre::*; -pub use redisdb::*; pub use surrealdb::*; diff --git a/src/libs/redisdb/key.rs b/src/libs/redisdb/key.rs deleted file mode 100644 index c073ca0..0000000 --- a/src/libs/redisdb/key.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::fmt; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RedisKeyEnum { - User, - Token, - Otp, -} - -impl fmt::Display for RedisKeyEnum { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let str = match self { - RedisKeyEnum::User => "user", - RedisKeyEnum::Token => "token", - RedisKeyEnum::Otp => "otp", - }; - write!(f, "{}", str) - } -} diff --git a/src/libs/redisdb/mod.rs b/src/libs/redisdb/mod.rs deleted file mode 100644 index fed6e1a..0000000 --- a/src/libs/redisdb/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -use super::Env; -use redis::Client; -use redis::RedisResult; - -pub mod key; -pub use key::*; - -pub async fn redisdb_init() -> RedisResult { - let env = Env::new(); - let url = format!("redis://{}", env.redisdb_url); - let client = Client::open(url)?; - Ok(client) -} diff --git a/src/libs/surrealdb/mod.rs b/src/libs/surrealdb/mod.rs index fd170c6..ad22f79 100644 --- a/src/libs/surrealdb/mod.rs +++ b/src/libs/surrealdb/mod.rs @@ -1,5 +1,6 @@ use super::Env; -use crate::SurrealClient; +use crate::{SurrealMemClient, SurrealWsClient}; +use surrealdb::engine::local::Mem; use surrealdb::engine::remote::ws::{Client, Ws}; use surrealdb::opt::auth::Root; use surrealdb::{Result, Surreal}; @@ -7,7 +8,7 @@ use surrealdb::{Result, Surreal}; pub mod resource; pub use resource::*; -pub async fn surrealdb_init() -> Result { +pub async fn surrealdb_init_ws() -> Result { let env = Env::new(); let db = Surreal::::init(); db.connect::(env.surrealdb_url.clone()).await?; @@ -21,3 +22,12 @@ pub async fn surrealdb_init() -> Result { .await?; Ok(db) } + +pub async fn surrealdb_init_mem() -> Result { + let env = Env::new(); + let db = Surreal::new::(()).await?; + db.use_ns(&env.surrealdb_namespace) + .use_db(&env.surrealdb_dbname) + .await?; + Ok(db) +} diff --git a/src/libs/surrealdb/resource.rs b/src/libs/surrealdb/resource.rs index 7666b28..05eaeb8 100644 --- a/src/libs/surrealdb/resource.rs +++ b/src/libs/surrealdb/resource.rs @@ -2,6 +2,8 @@ use std::fmt; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ResourceEnum { + OtpCache, + UsersCache, GachaItems, GachaClaims, GachaRolls, @@ -15,6 +17,8 @@ impl fmt::Display for ResourceEnum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let str = match self { ResourceEnum::Users => "app_users", + ResourceEnum::UsersCache => "app_users_cache", + ResourceEnum::OtpCache => "app_otp_cache", ResourceEnum::Roles => "app_roles", ResourceEnum::Permissions => "app_permissions", ResourceEnum::RolesPermissions => "app_roles_permissions", diff --git a/src/main.rs b/src/main.rs index f0335b2..facdbb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,5 +2,8 @@ use najm_course_api::{apps, axum_init}; #[tokio::main] async fn main() { - axum_init(|db, redis| async { apps(db, redis).await }).await; + axum_init(|surrealdb_ws, surrealdb_mem| async { + apps(surrealdb_ws, surrealdb_mem).await + }) + .await; }