feat: implement surrealdb and redis

This commit is contained in:
Maulana Sodiqin
2025-02-25 02:00:19 +07:00
parent 9abab01b9c
commit 44de53a49d
18 changed files with 2501 additions and 137 deletions
Generated
+2363 -105
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -17,6 +17,8 @@ utoipa = { version = "5.3.1", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "9.0.0", features = ["axum"] } utoipa-swagger-ui = { version = "9.0.0", features = ["axum"] }
redis = "0.28.2" redis = "0.28.2"
lettre = { version = "0.11.12", features = ["tokio1-native-tls"] } lettre = { version = "0.11.12", features = ["tokio1-native-tls"] }
surrealdb = "2.2.1"
thiserror = "2.0.11"
[profile.release] [profile.release]
lto = "fat" lto = "fat"
+8 -9
View File
@@ -1,15 +1,14 @@
use axum::{routing::get, Router}; use crate::{AppState, RedisClient, SurrealClient};
use v1::auth_router; use axum::{Extension, Router};
pub mod v1; pub mod v1;
pub mod v2; pub mod v2;
pub async fn apps() -> Router { pub async fn apps(surrealdb: SurrealClient, redisdb: RedisClient) -> Router {
let v1_routes = Router::new() let state = AppState { surrealdb, redisdb };
.nest("/auth", auth_router())
.route("/", get(|| async { "Comming Soon v1" }));
let v2_routes = Router::new().route("/", get(|| async { "Comming Soon v2" })); Router::new()
.nest("/v1", v1::routes().await)
Router::new().nest("/v1", v1_routes).nest("/v2", v2_routes) .nest("/v2", v2::routes().await)
.layer(Extension(state))
} }
+9 -4
View File
@@ -1,6 +1,11 @@
use super::{mutation_login, AuthLoginRequestDto}; use axum::{response::IntoResponse, Extension, Json};
use axum::{response::Response, Json};
pub async fn post_login(Json(payload): Json<AuthLoginRequestDto>) -> Response { use super::{mutation_login, AuthLoginRequestDto};
mutation_login(payload).await use crate::AppState;
pub async fn post_login(
Extension(state): Extension<AppState>,
Json(payload): Json<AuthLoginRequestDto>,
) -> impl IntoResponse {
mutation_login(payload, &state).await
} }
+24 -2
View File
@@ -1,8 +1,30 @@
use super::auth_dto::AuthLoginRequestDto; use super::auth_dto::AuthLoginRequestDto;
use crate::{success_response, ResponseSuccessDto}; use crate::{success_response, AppState, ResponseSuccessDto};
use axum::response::Response; use axum::response::Response;
pub async fn mutation_login(params: AuthLoginRequestDto) -> Response { const USERS_KEY: &str = "users";
pub async fn mutation_login(
params: AuthLoginRequestDto,
state: &AppState,
) -> Response {
let _users: Option<AuthLoginRequestDto> = state
.surrealdb
.select((USERS_KEY, &*params.email))
.await
.unwrap();
let mut redis_conn = state
.redisdb
.get_connection()
.expect("failed to get connection");
redis::cmd("SET")
.arg("some_key")
.arg("some_value")
.query::<()>(&mut redis_conn)
.expect("failed to set value");
let response = ResponseSuccessDto { let response = ResponseSuccessDto {
data: AuthLoginRequestDto { data: AuthLoginRequestDto {
email: params.email, email: params.email,
+5 -1
View File
@@ -1,3 +1,7 @@
use axum::Router;
pub mod auth; pub mod auth;
pub use auth::*; pub async fn routes() -> Router {
Router::new().nest("/auth", auth::auth_router())
}
View File
View File
+9
View File
@@ -0,0 +1,9 @@
use axum::{response::IntoResponse, routing::post, Router};
async fn dummy_login() -> impl IntoResponse {
"Logged in successfully"
}
pub fn auth_router() -> Router {
Router::new().route("/login", post(dummy_login))
}
+9
View File
@@ -0,0 +1,9 @@
use axum::Router;
pub mod auth;
pub use auth::*;
pub async fn routes() -> Router {
let public_routes = Router::new().nest("/auth", auth::auth_router());
Router::new().merge(public_routes)
}
+10
View File
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use surrealdb::{engine::remote::ws::Client, Surreal};
use utoipa::{IntoParams, ToSchema}; use utoipa::{IntoParams, ToSchema};
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
@@ -35,3 +36,12 @@ pub struct ResponseListSuccessDto<T: Serialize> {
pub data: T, pub data: T,
pub meta: Option<MetaResponseDto>, pub meta: Option<MetaResponseDto>,
} }
pub type SurrealClient = Surreal<Client>;
pub type RedisClient = redis::Client;
#[derive(Clone)]
pub struct AppState {
pub surrealdb: SurrealClient,
pub redisdb: RedisClient,
}
+27
View File
@@ -0,0 +1,27 @@
pub mod error {
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::response::Response;
use axum::Json;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("database error")]
Db,
}
impl IntoResponse for Error {
fn into_response(self) -> Response {
(StatusCode::INTERNAL_SERVER_ERROR, Json(self.to_string()))
.into_response()
}
}
impl From<surrealdb::Error> for Error {
fn from(error: surrealdb::Error) -> Self {
eprintln!("{error}");
Self::Db
}
}
}
+2
View File
@@ -1,3 +1,5 @@
pub mod common_dto; pub mod common_dto;
pub mod error_dto;
pub use common_dto::*; pub use common_dto::*;
pub use error_dto::*;
+11 -3
View File
@@ -3,21 +3,29 @@ use std::env;
use std::{future::Future, net::SocketAddr}; use std::{future::Future, net::SocketAddr};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use crate::{redisdb_init, RedisClient};
use crate::{surrealdb_init, SurrealClient};
pub async fn axum_init<F, Fut>(router_fn: F) pub async fn axum_init<F, Fut>(router_fn: F)
where where
F: Fn() -> Fut, F: FnOnce(SurrealClient, RedisClient) -> Fut,
Fut: Future<Output = Router>, Fut: Future<Output = Router>,
{ {
let router = router_fn().await; let surrealdb = surrealdb_init().await.expect("Failed surrealdb");
let redisdb = redisdb_init().await.expect("Failed redisdb");
let router = router_fn(surrealdb, redisdb).await;
let addr = SocketAddr::from(( let addr = SocketAddr::from((
[0, 0, 0, 0], [0, 0, 0, 0],
env::var("PORT") env::var("PORT")
.unwrap_or("3000".to_string()) .unwrap_or_else(|_| "3000".to_string())
.parse() .parse()
.unwrap(), .unwrap(),
)); ));
let listener = TcpListener::bind(&addr).await.unwrap(); let listener = TcpListener::bind(&addr).await.unwrap();
println!("Listening on http://{}", addr); println!("Listening on http://{}", addr);
match serve(listener, router).await { match serve(listener, router).await {
Ok(_) => println!("Server stopped gracefully."), Ok(_) => println!("Server stopped gracefully."),
Err(err) => println!("Server encountered an error: {}", err), Err(err) => println!("Server encountered an error: {}", err),
+2
View File
@@ -4,6 +4,7 @@ pub mod jsonwebtoken;
pub mod lettre; pub mod lettre;
pub mod redis; pub mod redis;
pub mod seaorm; pub mod seaorm;
pub mod surrealdb;
pub use argon::*; pub use argon::*;
pub use axum::*; pub use axum::*;
@@ -11,3 +12,4 @@ pub use jsonwebtoken::*;
pub use lettre::*; pub use lettre::*;
pub use redis::*; pub use redis::*;
pub use seaorm::*; pub use seaorm::*;
pub use surrealdb::*;
+7 -11
View File
@@ -1,20 +1,16 @@
use redis::Client;
use redis::RedisResult;
use std::env; use std::env;
use redis::Client; pub async fn redisdb_init() -> RedisResult<Client> {
let host_name =
pub fn db_redis() -> redis::Connection { env::var("REDIS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string());
let host_name = env::var("REDIS_HOSTNAME").unwrap_or("localhost".to_string());
let uri_scheme = if env::var("IS_TLS").is_ok() { let uri_scheme = if env::var("IS_TLS").is_ok() {
"rediss" "rediss"
} else { } else {
"redis" "redis"
}; };
let url = format!("{}://{}", uri_scheme, host_name); let url = format!("{}://{}", uri_scheme, host_name);
let client = Client::open(url)?;
Client::open(url) Ok(client)
.expect("Invalid connection URL")
.get_connection()
.expect("Failed to connect to Redis")
} }
+11
View File
@@ -0,0 +1,11 @@
use surrealdb::engine::remote::ws::{Client, Ws};
use surrealdb::{Result, Surreal};
use crate::SurrealClient;
pub async fn surrealdb_init() -> Result<SurrealClient> {
let db = Surreal::<Client>::init();
db.connect::<Ws>("ws://localhost:8000").await?;
db.use_ns("test").use_db("test").await?;
Ok(db)
}
+2 -2
View File
@@ -1,6 +1,6 @@
use imphnen_cms_be::{apps, libs::axum_init}; use imphnen_cms_be::{apps, axum_init};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
axum_init(apps).await; axum_init(|db, redis| async { apps(db, redis).await }).await;
} }