feat: microservice ready
This commit is contained in:
Generated
+114
@@ -1849,6 +1849,31 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "imphnen-backend-service"
|
name = "imphnen-backend-service"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-gateway-service",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imphnen-dimentorin-service"
|
||||||
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -1881,6 +1906,79 @@ dependencies = [
|
|||||||
"utoipa",
|
"utoipa",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imphnen-gacha-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imphnen-gateway-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-iam-service",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imphnen-iam-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imphnen-libs"
|
name = "imphnen-libs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -4159,6 +4257,22 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-iam-service",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
|
|||||||
+2
-4
@@ -1,10 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"imphnen-apis",
|
"imphnen-*",
|
||||||
"imphnen-utils",
|
"tests",
|
||||||
"imphnen-libs",
|
|
||||||
"imphnen-entities",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
use crate::{AppState, Env, SurrealMemClient, SurrealWsClient};
|
|
||||||
use axum::{
|
|
||||||
http::{header, HeaderValue, Method},
|
|
||||||
response::Redirect,
|
|
||||||
routing::get,
|
|
||||||
Extension, Router,
|
|
||||||
};
|
|
||||||
use tower_http::cors::CorsLayer;
|
|
||||||
use utoipa_swagger_ui::SwaggerUi;
|
|
||||||
|
|
||||||
pub mod v1;
|
|
||||||
pub mod v2;
|
|
||||||
|
|
||||||
pub use v1::*;
|
|
||||||
|
|
||||||
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"],
|
|
||||||
"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();
|
|
||||||
|
|
||||||
let cors_middleware = 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);
|
|
||||||
|
|
||||||
Router::new()
|
|
||||||
.route("/", get(Redirect::to("/docs")))
|
|
||||||
.nest("/v1", v1::routes().await)
|
|
||||||
.nest("/v2", v2::routes().await)
|
|
||||||
.merge(SwaggerUi::new("/docs").url("/openapi.json", v1::docs_router()))
|
|
||||||
.layer(cors_middleware)
|
|
||||||
.layer(Extension(state))
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
common_response, extract_email, AppState, UsersItemDtoRaw, UsersRepository,
|
|
||||||
};
|
|
||||||
use axum::{
|
|
||||||
extract::Request, http::StatusCode, middleware::Next, response::Response,
|
|
||||||
Extension,
|
|
||||||
};
|
|
||||||
use std::convert::Infallible;
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
use utoipa::OpenApi;
|
|
||||||
|
|
||||||
pub mod docs_controller;
|
|
||||||
pub use docs_controller::*;
|
|
||||||
|
|
||||||
pub fn docs_router() -> utoipa::openapi::OpenApi {
|
|
||||||
ApiDoc::openapi()
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
use axum::{middleware::from_fn, Router};
|
|
||||||
pub mod auth;
|
|
||||||
pub mod docs;
|
|
||||||
pub mod gacha;
|
|
||||||
pub mod permissions;
|
|
||||||
pub mod roles;
|
|
||||||
pub mod users;
|
|
||||||
|
|
||||||
pub use auth::*;
|
|
||||||
pub use docs::*;
|
|
||||||
pub use permissions::*;
|
|
||||||
pub use roles::*;
|
|
||||||
pub use users::*;
|
|
||||||
|
|
||||||
pub async fn routes() -> Router {
|
|
||||||
let public_routes = Router::new().nest("/auth", auth_router());
|
|
||||||
let protected_routes = Router::new()
|
|
||||||
.nest("/users", users_router())
|
|
||||||
.nest("/roles", roles_router())
|
|
||||||
.nest("/permissions", permissions_router())
|
|
||||||
.layer(from_fn(auth::auth_middleware::auth_middleware));
|
|
||||||
Router::new().merge(public_routes).merge(protected_routes)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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))
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use imphnen_backend_service::{apps, axum_init};
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
axum_init(|surrealdb_ws, surrealdb_mem| async {
|
|
||||||
apps(surrealdb_ws, surrealdb_mem).await
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use imphnen_backend_service::{apps, axum_init};
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
axum_init(|surrealdb_ws, surrealdb_mem| async {
|
|
||||||
apps(surrealdb_ws, surrealdb_mem).await
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "imphnen-backend-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
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
|
||||||
|
imphnen-gateway-service = { version = "0.1.0", path = "../imphnen-gateway-service" }
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use imphnen_gateway_service::gateway_service;
|
||||||
|
use imphnen_libs::axum_init;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
axum_init(|surrealdb_ws, surrealdb_mem| async {
|
||||||
|
gateway_service(surrealdb_ws, surrealdb_mem).await
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use imphnen_gateway_service::gateway_service;
|
||||||
|
use imphnen_libs::axum_init;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
axum_init(|surrealdb_ws, surrealdb_mem| async {
|
||||||
|
gateway_service(surrealdb_ws, surrealdb_mem).await
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "imphnen-dimentorin-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
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
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pub fn add(left: u64, right: u64) -> u64 {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "imphnen-backend-service"
|
name = "imphnen-gacha-service"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use imphnen_entities::*;
|
||||||
|
use imphnen_libs::*;
|
||||||
|
use imphnen_utils::*;
|
||||||
|
|
||||||
|
pub mod v1;
|
||||||
|
pub use imphnen_entities::*;
|
||||||
|
pub use imphnen_libs::*;
|
||||||
|
pub use imphnen_utils::*;
|
||||||
|
pub use v1::*;
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
use super::{GachaClaimSchema, GachaItemSchema, GachaRollSchema};
|
use super::{GachaClaimSchema, GachaItemSchema, GachaRollSchema};
|
||||||
use crate::{
|
use crate::{
|
||||||
get_id, make_thing, query_list_with_meta, AppState, MetaRequestDto, ResourceEnum,
|
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id,
|
||||||
ResponseListSuccessDto,
|
make_thing, query_list_with_meta,
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{Result, bail};
|
||||||
|
|
||||||
pub struct GachaRepository<'a> {
|
pub struct GachaRepository<'a> {
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod gacha;
|
||||||
|
pub use gacha::*;
|
||||||
@@ -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
|
||||||
+7
-5
@@ -1,9 +1,7 @@
|
|||||||
use crate::{
|
use imphnen_iam_service::{
|
||||||
v1::{
|
auth, permissions, roles, users, AuthLoginRequestDto, AuthLoginResponsetDto, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto,
|
||||||
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
|
||||||
}, AuthNewPasswordRequestDto, AuthRefreshTokenRequestDto, MessageResponseDto, MetaRequestDto, MetaResponseDto, PermissionsItemDto, PermissionsRequestDto, ResponseListSuccessDto, ResponseSuccessDto, RolesItemDto, RolesRequestCreateDto, RolesRequestUpdateDto, TokenDto, UsersCreateRequestDto, UsersDetailItemDto, UsersItemDto, UsersListItemDto, UsersUpdateRequestDto
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use utoipa::{
|
use utoipa::{
|
||||||
openapi::security::{Http, HttpAuthScheme, SecurityScheme},
|
openapi::security::{Http, HttpAuthScheme, SecurityScheme},
|
||||||
Modify, OpenApi,
|
Modify, OpenApi,
|
||||||
@@ -101,3 +99,7 @@ impl Modify for SecurityAddon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn docs_router() -> utoipa::openapi::OpenApi {
|
||||||
|
ApiDoc::openapi()
|
||||||
|
}
|
||||||
@@ -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()))
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "imphnen-iam-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
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
|
||||||
@@ -3,11 +3,11 @@ use imphnen_entities::*;
|
|||||||
use imphnen_libs::*;
|
use imphnen_libs::*;
|
||||||
use imphnen_utils::*;
|
use imphnen_utils::*;
|
||||||
|
|
||||||
pub mod apps;
|
pub mod v1;
|
||||||
|
|
||||||
pub use apps::*;
|
|
||||||
pub use imphnen_entities::*;
|
pub use imphnen_entities::*;
|
||||||
pub use imphnen_libs::*;
|
pub use imphnen_libs::*;
|
||||||
|
pub use v1::*;
|
||||||
|
|
||||||
pub fn create_test_user(
|
pub fn create_test_user(
|
||||||
email: &str,
|
email: &str,
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
use axum::{routing::post, Router};
|
use axum::{Router, routing::post};
|
||||||
|
|
||||||
pub mod auth_controller;
|
pub mod auth_controller;
|
||||||
pub mod auth_dto;
|
pub mod auth_dto;
|
||||||
pub mod auth_middleware;
|
|
||||||
pub mod auth_repository;
|
pub mod auth_repository;
|
||||||
pub mod auth_schema;
|
pub mod auth_schema;
|
||||||
pub mod auth_service;
|
pub mod auth_service;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod auth_controller_test;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod auth_repository_test;
|
|
||||||
|
|
||||||
pub use auth_dto::*;
|
pub use auth_dto::*;
|
||||||
pub use auth_repository::*;
|
pub use auth_repository::*;
|
||||||
pub use auth_schema::*;
|
pub use auth_schema::*;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use axum::Router;
|
||||||
|
|
||||||
|
pub mod auth;
|
||||||
|
pub mod permissions;
|
||||||
|
pub mod roles;
|
||||||
|
pub mod users;
|
||||||
|
|
||||||
|
pub use auth::*;
|
||||||
|
pub use permissions::*;
|
||||||
|
pub use roles::*;
|
||||||
|
pub use users::*;
|
||||||
|
|
||||||
|
pub fn iam_public_routes() -> Router {
|
||||||
|
Router::new().nest("/auth", auth_router())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iam_protected_routes() -> Router {
|
||||||
|
Router::new()
|
||||||
|
.nest("/users", users_router())
|
||||||
|
.nest("/roles", roles_router())
|
||||||
|
.nest("/permissions", permissions_router())
|
||||||
|
}
|
||||||
+1
-6
@@ -1,6 +1,6 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
routing::{delete, get, post, put},
|
|
||||||
Router,
|
Router,
|
||||||
|
routing::{delete, get, post, put},
|
||||||
};
|
};
|
||||||
pub mod permissions_controller;
|
pub mod permissions_controller;
|
||||||
pub mod permissions_dto;
|
pub mod permissions_dto;
|
||||||
@@ -10,11 +10,6 @@ pub mod permissions_repository;
|
|||||||
pub mod permissions_schema;
|
pub mod permissions_schema;
|
||||||
pub mod permissions_service;
|
pub mod permissions_service;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod permissions_controller_test;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod permissions_repository_test;
|
|
||||||
|
|
||||||
pub use permissions_controller::*;
|
pub use permissions_controller::*;
|
||||||
pub use permissions_dto::*;
|
pub use permissions_dto::*;
|
||||||
pub use permissions_enum::*;
|
pub use permissions_enum::*;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
routing::{delete, get, post, put},
|
|
||||||
Router,
|
Router,
|
||||||
|
routing::{delete, get, post, put},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod roles_controller;
|
pub mod roles_controller;
|
||||||
@@ -10,11 +10,6 @@ pub mod roles_repository;
|
|||||||
pub mod roles_schema;
|
pub mod roles_schema;
|
||||||
pub mod roles_service;
|
pub mod roles_service;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod roles_controller_test;
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod roles_repository_test;
|
|
||||||
|
|
||||||
pub use roles_controller::*;
|
pub use roles_controller::*;
|
||||||
pub use roles_dto::*;
|
pub use roles_dto::*;
|
||||||
pub use roles_enum::*;
|
pub use roles_enum::*;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
||||||
|
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
||||||
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
|
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
|
||||||
|
axum.workspace = true
|
||||||
|
axum-test.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
|
surrealdb.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
+16
-15
@@ -1,8 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
apps::v1::auth::auth_router, create_mock_app_state, create_test_user,
|
AuthOtpSchema, AuthRepository, OtpManager, ResourceEnum, RolesRepository,
|
||||||
encode_refresh_token, AuthOtpSchema, OtpManager, ResourceEnum, RolesRepository,
|
UsersRepository, auth::auth_router, create_mock_app_state, create_test_user,
|
||||||
|
encode_refresh_token,
|
||||||
};
|
};
|
||||||
use axum::{http::StatusCode, Extension};
|
use axum::{Extension, http::StatusCode};
|
||||||
use axum_test::TestServer;
|
use axum_test::TestServer;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use surrealdb::Uuid;
|
use surrealdb::Uuid;
|
||||||
@@ -31,7 +32,7 @@ async fn test_login_should_fail_with_wrong_password() {
|
|||||||
.nest("/v1/auth", auth_router())
|
.nest("/v1/auth", auth_router())
|
||||||
.layer(Extension(state.clone()));
|
.layer(Extension(state.clone()));
|
||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
let repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let repo = UsersRepository::new(&state);
|
||||||
let role_repo = RolesRepository::new(&state);
|
let role_repo = RolesRepository::new(&state);
|
||||||
let role_id = role_repo
|
let role_id = role_repo
|
||||||
.query_role_by_name("Student".to_string())
|
.query_role_by_name("Student".to_string())
|
||||||
@@ -58,7 +59,7 @@ async fn test_login_should_fail_if_user_not_active() {
|
|||||||
.nest("/v1/auth", auth_router())
|
.nest("/v1/auth", auth_router())
|
||||||
.layer(Extension(state.clone()));
|
.layer(Extension(state.clone()));
|
||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
let repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let repo = UsersRepository::new(&state);
|
||||||
let role_repo = RolesRepository::new(&state);
|
let role_repo = RolesRepository::new(&state);
|
||||||
let role_id = role_repo
|
let role_id = role_repo
|
||||||
.query_role_by_name("Student".to_string())
|
.query_role_by_name("Student".to_string())
|
||||||
@@ -91,7 +92,7 @@ async fn test_login_should_succeed() {
|
|||||||
.nest("/v1/auth", auth_router())
|
.nest("/v1/auth", auth_router())
|
||||||
.layer(Extension(state.clone()));
|
.layer(Extension(state.clone()));
|
||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
let repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let repo = UsersRepository::new(&state);
|
||||||
let role_repo = RolesRepository::new(&state);
|
let role_repo = RolesRepository::new(&state);
|
||||||
let role_id = role_repo
|
let role_id = role_repo
|
||||||
.query_role_by_name("Student".to_string())
|
.query_role_by_name("Student".to_string())
|
||||||
@@ -183,7 +184,7 @@ async fn test_register_should_fail_if_email_already_taken() {
|
|||||||
.nest("/v1/auth", auth_router())
|
.nest("/v1/auth", auth_router())
|
||||||
.layer(Extension(state.clone()));
|
.layer(Extension(state.clone()));
|
||||||
let server = TestServer::new(app).unwrap();
|
let server = TestServer::new(app).unwrap();
|
||||||
let repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let repo = UsersRepository::new(&state);
|
||||||
let role_repo = RolesRepository::new(&state);
|
let role_repo = RolesRepository::new(&state);
|
||||||
let role_id = role_repo
|
let role_id = role_repo
|
||||||
.query_role_by_name("Student".to_string())
|
.query_role_by_name("Student".to_string())
|
||||||
@@ -353,7 +354,7 @@ async fn test_forgot_password_should_succeed() {
|
|||||||
let mut user =
|
let mut user =
|
||||||
create_test_user("forgot@example.com", "Forgot User", true, &role_id);
|
create_test_user("forgot@example.com", "Forgot User", true, &role_id);
|
||||||
user.password = crate::hash_password("secret").unwrap();
|
user.password = crate::hash_password("secret").unwrap();
|
||||||
let user_repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let user_repo = UsersRepository::new(&state);
|
||||||
user_repo.query_create_user(user).await.unwrap();
|
user_repo.query_create_user(user).await.unwrap();
|
||||||
let payload = json!({ "email": "forgot@example.com" });
|
let payload = json!({ "email": "forgot@example.com" });
|
||||||
let res = server.post("/v1/auth/forgot").json(&payload).await;
|
let res = server.post("/v1/auth/forgot").json(&payload).await;
|
||||||
@@ -463,8 +464,8 @@ async fn test_verify_email_should_succeed() {
|
|||||||
.id;
|
.id;
|
||||||
let user = create_test_user("verify@example.com", "Verify User", false, &role_id);
|
let user = create_test_user("verify@example.com", "Verify User", false, &role_id);
|
||||||
let otp = OtpManager::generate_otp();
|
let otp = OtpManager::generate_otp();
|
||||||
let user_repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let user_repo = UsersRepository::new(&state);
|
||||||
let auth_repo = crate::apps::v1::auth::AuthRepository::new(&state);
|
let auth_repo = AuthRepository::new(&state);
|
||||||
user_repo.query_create_user(user.clone()).await.unwrap();
|
user_repo.query_create_user(user.clone()).await.unwrap();
|
||||||
auth_repo
|
auth_repo
|
||||||
.query_store_otp(user.email.clone(), otp)
|
.query_store_otp(user.email.clone(), otp)
|
||||||
@@ -497,8 +498,8 @@ async fn test_verify_email_should_fail_with_wrong_otp() {
|
|||||||
.id;
|
.id;
|
||||||
let user = create_test_user("wrongotp@example.com", "Wrong OTP", false, &role_id);
|
let user = create_test_user("wrongotp@example.com", "Wrong OTP", false, &role_id);
|
||||||
let otp = OtpManager::generate_otp();
|
let otp = OtpManager::generate_otp();
|
||||||
let user_repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let user_repo = UsersRepository::new(&state);
|
||||||
let auth_repo = crate::apps::v1::auth::AuthRepository::new(&state);
|
let auth_repo = AuthRepository::new(&state);
|
||||||
user_repo.query_create_user(user.clone()).await.unwrap();
|
user_repo.query_create_user(user.clone()).await.unwrap();
|
||||||
auth_repo
|
auth_repo
|
||||||
.query_store_otp(user.email.clone(), otp)
|
.query_store_otp(user.email.clone(), otp)
|
||||||
@@ -530,10 +531,10 @@ async fn test_verify_email_should_fail_if_otp_expired() {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.id;
|
.id;
|
||||||
let user = create_test_user("expired@example.com", "Expired OTP", false, &role_id);
|
let user = create_test_user("expired@example.com", "Expired OTP", false, &role_id);
|
||||||
let user_repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let user_repo = UsersRepository::new(&state);
|
||||||
user_repo.query_create_user(user.clone()).await.unwrap();
|
user_repo.query_create_user(user.clone()).await.unwrap();
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
let expired_otp = crate::apps::v1::auth::AuthOtpSchema {
|
let expired_otp = AuthOtpSchema {
|
||||||
otp: 123456,
|
otp: 123456,
|
||||||
expires_at: Utc::now() - Duration::minutes(1),
|
expires_at: Utc::now() - Duration::minutes(1),
|
||||||
};
|
};
|
||||||
@@ -576,7 +577,7 @@ async fn test_new_password_should_succeed() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let email = "resetme@example.com";
|
let email = "resetme@example.com";
|
||||||
let repo = crate::apps::v1::users::UsersRepository::new(&state);
|
let repo = UsersRepository::new(&state);
|
||||||
let role_repo = RolesRepository::new(&state);
|
let role_repo = RolesRepository::new(&state);
|
||||||
let role_id = role_repo
|
let role_id = role_repo
|
||||||
.query_role_by_name("Student".to_string())
|
.query_role_by_name("Student".to_string())
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
pub mod auth_controller_test;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod auth_repository_test;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod auth;
|
||||||
|
pub mod permissions;
|
||||||
|
pub mod roles;
|
||||||
|
pub mod users;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
pub mod permissions_controller_test;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod permissions_repository_test;
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
apps::v1::permissions::{PermissionsRepository, PermissionsSchema},
|
|
||||||
create_mock_app_state,
|
create_mock_app_state,
|
||||||
|
permissions::{PermissionsRepository, PermissionsSchema},
|
||||||
};
|
};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
pub mod roles_controller_test;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod roles_repository_test;
|
||||||
+11
-13
@@ -1,18 +1,16 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
apps::v1::{
|
ResourceEnum, create_mock_app_state, get_iso_date, make_thing,
|
||||||
permissions::{
|
permissions::{
|
||||||
permissions_repository::PermissionsRepository,
|
permissions_repository::PermissionsRepository,
|
||||||
permissions_schema::PermissionsSchema,
|
permissions_schema::PermissionsSchema,
|
||||||
},
|
},
|
||||||
roles::{
|
roles::{
|
||||||
roles_dto::{RolesRequestCreateDto, RolesRequestUpdateDto},
|
roles_dto::{RolesRequestCreateDto, RolesRequestUpdateDto},
|
||||||
roles_repository::RolesRepository,
|
roles_repository::RolesRepository,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
create_mock_app_state, get_iso_date, make_thing, ResourceEnum,
|
|
||||||
};
|
};
|
||||||
use surrealdb::sql::Thing;
|
|
||||||
use surrealdb::Uuid;
|
use surrealdb::Uuid;
|
||||||
|
use surrealdb::sql::Thing;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_create_role_should_succeed() {
|
async fn test_query_create_role_should_succeed() {
|
||||||
@@ -133,8 +131,8 @@ async fn test_query_delete_role_should_soft_delete() {
|
|||||||
assert!(deleted.is_err());
|
assert!(deleted.is_err());
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_provided(
|
async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_provided()
|
||||||
) {
|
{
|
||||||
let state = create_mock_app_state().await;
|
let state = create_mock_app_state().await;
|
||||||
let repo = RolesRepository::new(&state);
|
let repo = RolesRepository::new(&state);
|
||||||
let perm_repo = PermissionsRepository::new(&state);
|
let perm_repo = PermissionsRepository::new(&state);
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
pub mod users_controller_test;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod users_repository_test;
|
||||||
+4
-4
@@ -1,10 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
apps::v1::users_router, create_mock_app_state, RolesEnum, RolesRepository,
|
RolesEnum, RolesRepository, UsersActiveInactiveRequestDto, UsersCreateRequestDto,
|
||||||
UsersActiveInactiveRequestDto, UsersCreateRequestDto, UsersRepository,
|
UsersRepository, UsersUpdateRequestDto, users_router,
|
||||||
UsersUpdateRequestDto,
|
|
||||||
};
|
};
|
||||||
use axum::{http::StatusCode, Extension};
|
use axum::{Extension, http::StatusCode};
|
||||||
use axum_test::TestServer;
|
use axum_test::TestServer;
|
||||||
|
use imphnen_utils::create_mock_app_state;
|
||||||
use surrealdb::Uuid;
|
use surrealdb::Uuid;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pub use imphnen_entities::*;
|
||||||
|
pub use imphnen_iam_service::*;
|
||||||
|
pub use imphnen_utils::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod iam_service;
|
||||||
Reference in New Issue
Block a user