feat: microservice ready

This commit is contained in:
Maulana Sodiqin
2025-04-06 22:13:46 +07:00
parent 07a93ff0b7
commit 31f9b6cfea
80 changed files with 477 additions and 240 deletions
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "imphnen-gateway-service"
version = "0.1.0"
edition = "2024"
[dependencies]
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
utoipa.workspace = true
lazy_static.workspace = true
regex.workspace = true
validator.workspace = true
axum-test.workspace = true
surrealdb.workspace = true
rand.workspace = true
tokio.workspace = true
chrono.workspace = true
anyhow.workspace = true
tower-http.workspace = true
utoipa-swagger-ui.workspace = true
+105
View File
@@ -0,0 +1,105 @@
use imphnen_iam_service::{
auth, permissions, roles, users, AuthLoginRequestDto, AuthLoginResponsetDto, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto,
AuthNewPasswordRequestDto, AuthRefreshTokenRequestDto, MessageResponseDto, MetaRequestDto, MetaResponseDto, PermissionsItemDto, PermissionsRequestDto, ResponseListSuccessDto, ResponseSuccessDto, RolesItemDto, RolesRequestCreateDto, RolesRequestUpdateDto, TokenDto, UsersCreateRequestDto, UsersDetailItemDto, UsersItemDto, UsersListItemDto, UsersUpdateRequestDto
};
use utoipa::{
openapi::security::{Http, HttpAuthScheme, SecurityScheme},
Modify, OpenApi,
};
#[derive(OpenApi)]
#[openapi(
paths(
auth::auth_controller::post_login,
auth::auth_controller::post_register,
auth::auth_controller::post_verify_email,
auth::auth_controller::post_resend_otp,
auth::auth_controller::post_refresh_token,
auth::auth_controller::post_forgot_password,
auth::auth_controller::post_new_password,
users::users_controller::post_create_user,
users::users_controller::put_update_user,
users::users_controller::put_update_user_me,
users::users_controller::patch_user_active_status,
users::users_controller::delete_user,
users::users_controller::get_user_by_id,
users::users_controller::get_user_me,
users::users_controller::get_user_list,
roles::roles_controller::get_role_list,
roles::roles_controller::get_role_by_id,
roles::roles_controller::post_create_role,
roles::roles_controller::put_update_role,
roles::roles_controller::delete_role,
permissions::permissions_controller::get_permission_list,
permissions::permissions_controller::get_permission_by_id,
permissions::permissions_controller::post_create_permission,
permissions::permissions_controller::put_update_permission,
permissions::permissions_controller::delete_permission
),
components(
schemas(
MetaRequestDto,
MetaResponseDto,
MessageResponseDto,
AuthLoginRequestDto,
AuthLoginResponsetDto,
AuthVerifyEmailRequestDto,
AuthResendOtpRequestDto,
AuthNewPasswordRequestDto,
AuthRefreshTokenRequestDto,
ResponseSuccessDto<TokenDto>,
RolesItemDto,
RolesRequestCreateDto,
RolesRequestUpdateDto,
PermissionsRequestDto,
PermissionsItemDto,
UsersItemDto,
UsersListItemDto,
UsersUpdateRequestDto,
UsersCreateRequestDto,
ResponseSuccessDto<AuthLoginResponsetDto>,
ResponseListSuccessDto<Vec<RolesItemDto>>,
ResponseSuccessDto<RolesItemDto>,
ResponseListSuccessDto<Vec<UsersListItemDto>>,
ResponseSuccessDto<UsersDetailItemDto>,
ResponseListSuccessDto<Vec<PermissionsItemDto>>,
ResponseSuccessDto<PermissionsItemDto>
)
),
info(
title = "IMPHNEN Backend Service",
description = "IMPHNEN Backend Service for Provide Gacha, Dimentorin and Backoffice Web App",
version = "0.1.0",
contact(
name = "Maulana Sodiqin",
url = ""
),
license(
name = "MIT",
url = "https://opensource.org/licenses/MIT"
)
),
modifiers(&SecurityAddon),
tags(
(name = "Authentication", description = "List of Authentication Endpoints"),
)
)]
pub struct ApiDoc;
struct SecurityAddon;
impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
if let Some(components) = openapi.components.as_mut() {
components.add_security_scheme(
"Bearer",
SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)),
);
}
}
}
pub fn docs_router() -> utoipa::openapi::OpenApi {
ApiDoc::openapi()
}
+32
View File
@@ -0,0 +1,32 @@
use axum::{
Extension, Router, middleware::from_fn, response::Redirect, routing::get,
};
use imphnen_entities::{AppState, SurrealMemClient, SurrealWsClient};
use imphnen_iam_service::{iam_protected_routes, iam_public_routes};
pub mod docs;
pub mod middleware;
pub use docs::*;
pub use middleware::*;
use utoipa_swagger_ui::SwaggerUi;
pub async fn gateway_service(
surrealdb_ws: SurrealWsClient,
surrealdb_mem: SurrealMemClient,
) -> Router {
let state = AppState {
surrealdb_ws,
surrealdb_mem,
};
let public_routes = iam_public_routes();
let protected_routes = iam_protected_routes().layer(from_fn(auth_middleware));
Router::new()
.route("/", get(Redirect::to("/docs")))
.nest("/v1", public_routes.merge(protected_routes))
.merge(SwaggerUi::new("/docs").url("/openapi.json", docs_router()))
.layer(cors_middleware())
.layer(Extension(state.clone()))
}
+73
View File
@@ -0,0 +1,73 @@
use axum::{
Extension,
extract::Request,
http::{HeaderValue, Method, StatusCode, header},
middleware::Next,
response::Response,
};
use imphnen_iam_service::{UsersItemDtoRaw, UsersRepository};
use imphnen_libs::{AppState, Env};
use imphnen_utils::{common_response, extract_email};
use std::convert::Infallible;
use tower_http::cors::CorsLayer;
pub async fn auth_middleware(
Extension(state): Extension<AppState>,
mut req: Request,
next: Next,
) -> Result<Response, Infallible> {
let headers = req.headers();
let email = match extract_email(headers) {
Some(email) => email,
None => {
return Ok(common_response(
StatusCode::UNAUTHORIZED,
"Invalid or expired token",
));
}
};
let repository = UsersRepository::new(&state);
let user: Option<UsersItemDtoRaw> =
match repository.query_user_by_email(email).await {
Ok(user) => Some(user),
Err(err) => {
return Ok(common_response(
StatusCode::INTERNAL_SERVER_ERROR,
&err.to_string(),
));
}
};
if user.is_none() {
return Ok(common_response(
StatusCode::UNAUTHORIZED,
"Unauthorized user",
));
}
req.extensions_mut().insert(user.unwrap());
Ok(next.run(req).await)
}
pub fn cors_middleware() -> CorsLayer {
let env = Env::new();
let cors_origins = match env.rust_env.as_str() {
"development" => vec!["http://localhost:3000"],
"production" => {
vec!["https://gacha.imphnen.dev", "https://imphnen.dev"]
}
_ => vec![
"http://localhost:3000",
"https://gacha.imphnen.dev",
"https://imphnen.dev",
],
};
let allowed_origins: Vec<HeaderValue> = cors_origins
.into_iter()
.filter_map(|origin| origin.parse::<HeaderValue>().ok())
.collect();
CorsLayer::new()
.allow_origin(allowed_origins)
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE])
.allow_credentials(true)
}