From ab9110763321db97ce8a2a9a2c4db967dd8074ec Mon Sep 17 00:00:00 2001 From: yuanzui-cf Date: Wed, 8 Apr 2026 09:30:39 +0800 Subject: [PATCH 1/5] feat(auth): add reset-password action page --- auth/src/routers.rs | 5 +- auth/src/routers/controllers/actions.rs | 75 +++- auth/src/routers/controllers/auth.rs | 12 +- auth/src/services/actions/mod.rs | 2 + auth/src/services/actions/reset_password.rs | 153 +++++++ auth/src/services/auth/forget_password.rs | 8 +- auth/src/services/auth/reset_password.rs | 5 - .../templates/actions/reset_password.html | 399 ++++++++++++++++++ 8 files changed, 635 insertions(+), 24 deletions(-) create mode 100644 auth/src/services/actions/reset_password.rs create mode 100644 crates/assets/assets/templates/actions/reset_password.html diff --git a/auth/src/routers.rs b/auth/src/routers.rs index b50ebac..535e34c 100644 --- a/auth/src/routers.rs +++ b/auth/src/routers.rs @@ -59,7 +59,9 @@ pub fn get_router(app: Router, config: &Config) -> Router { }; let action = { - let route = Router::new().route("/verify-email", get(actions::verify_email)); + let route = Router::new() + .route("/verify-email", get(actions::verify_email)) + .route("/reset-password", get(actions::reset_password)); let route = Router::new().nest("/actions", route); if config.dev_mode { route.layer(public_cors.clone()) @@ -77,7 +79,6 @@ pub fn get_router(app: Router, config: &Config) -> Router { .route("/register", post(auth::register)) .route("/verify-email", post(auth::verify_email)) .route("/forget-password", post(auth::forget_password)) - .route("/reset-password", get(auth::reset_password)) .route( "/reset-password/token", patch(auth::reset_password_with_token), diff --git a/auth/src/routers/controllers/actions.rs b/auth/src/routers/controllers/actions.rs index ca26806..447ac28 100644 --- a/auth/src/routers/controllers/actions.rs +++ b/auth/src/routers/controllers/actions.rs @@ -3,8 +3,14 @@ use axum::{ http::StatusCode, response::{Html, IntoResponse, Response as AxumResponse}, }; +use redis::aio::MultiplexedConnection; +use token::{TokenStore, services::PasswordResetTokenService}; -use crate::{services::actions::ActionsVerifyEmailService, state::AppState}; +use crate::{ + internal::error::{AppError, AppErrorKind}, + services::actions::{ActionsResetPasswordService, ActionsVerifyEmailService}, + state::AppState, +}; pub async fn verify_email( State(state): State, @@ -33,3 +39,70 @@ pub async fn verify_email( }, } } + +pub async fn reset_password( + State(state): State, + Query(req): Query, +) -> AxumResponse { + let token_valid = match req.token() { + Some(token) => { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return render_reset_password_error(AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.redis", + err, + )); + }, + }; + + match has_valid_reset_password_token(&mut redis, token).await { + Ok(valid) => valid, + Err(err) => return render_reset_password_error(err), + } + }, + None => req.token.is_none(), + }; + + match req.render_reset_password_page(&state.config, token_valid) { + Ok(html) => Html(html).into_response(), + Err(err) => render_reset_password_error(err), + } +} + +async fn has_valid_reset_password_token( + redis: &mut MultiplexedConnection, + token: &str, +) -> Result { + ::get(redis, token) + .await + .map(|payload| payload.is_some()) + .map_err(|err| { + AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.get_token", + err, + ) + }) +} + +fn render_reset_password_error(err: AppError) -> AxumResponse { + let source = err.source_ref().map(ToString::to_string); + tracing::error!( + op = err.op, + kind = ?err.kind, + detail = ?err.detail, + source = ?source, + "failed to render reset-password action page" + ); + ( + StatusCode::INTERNAL_SERVER_ERROR, + Html( + "Reset Password \ + Error

Unable to load reset-password page. Please try again \ + later.

", + ), + ) + .into_response() +} diff --git a/auth/src/routers/controllers/auth.rs b/auth/src/routers/controllers/auth.rs index 509afa1..4648893 100644 --- a/auth/src/routers/controllers/auth.rs +++ b/auth/src/routers/controllers/auth.rs @@ -1,7 +1,4 @@ -use axum::{ - extract::{Query, State}, - http::StatusCode, -}; +use axum::extract::State; use axum_extra::extract::CookieJar; use redis::aio::MultiplexedConnection; use token::services::SessionService; @@ -142,13 +139,6 @@ pub async fn logout( ) } -/// Auth reset password page placeholder -pub async fn reset_password(Query(query): Query) -> StatusCode { - // TODO: Reset Password Page - let _token = query.token; - StatusCode::OK -} - /// Auth reset password with token pub async fn reset_password_with_token( State(state): State, diff --git a/auth/src/services/actions/mod.rs b/auth/src/services/actions/mod.rs index 7b3c454..123d11d 100644 --- a/auth/src/services/actions/mod.rs +++ b/auth/src/services/actions/mod.rs @@ -1,3 +1,5 @@ +pub mod reset_password; pub mod verify_email; +pub use reset_password::*; pub use verify_email::*; diff --git a/auth/src/services/actions/reset_password.rs b/auth/src/services/actions/reset_password.rs new file mode 100644 index 0000000..43fff9f --- /dev/null +++ b/auth/src/services/actions/reset_password.rs @@ -0,0 +1,153 @@ +use anyhow::anyhow; +use assets::AssetManager; +use minijinja::{AutoEscape, Environment, context}; +use serde::Deserialize; + +use crate::internal::{ + config::Config, + error::{AppError, AppErrorKind}, +}; + +const ACTION_RESET_PASSWORD_PATH: &str = "/actions/reset-password"; +const USER_INFO_API_PATH: &str = "/api/v1/user/info"; +const RESET_PASSWORD_WITH_TOKEN_API_PATH: &str = "/api/v1/auth/reset-password/token"; +const RESET_PASSWORD_WITH_PASSWORD_API_PATH: &str = "/api/v1/auth/reset-password/password"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ResetPasswordPageMode { + Token, + SessionCheck, + Invalid, +} + +#[derive(Deserialize)] +pub struct ActionsResetPasswordService { + #[serde(default)] + pub token: Option, +} + +impl ActionsResetPasswordService { + pub fn build_reset_password_action_url(config: &Config, token: &str) -> String { + format!( + "{}{}?token={token}", + config.domain.trim_end_matches('/'), + ACTION_RESET_PASSWORD_PATH + ) + } + + pub fn token(&self) -> Option<&str> { + self.token + .as_deref() + .map(str::trim) + .filter(|token| !token.is_empty()) + } + + pub fn render_reset_password_page( + &self, + config: &Config, + token_valid: bool, + ) -> Result { + let mode = self.page_mode(token_valid); + let initial_error = if mode == ResetPasswordPageMode::Invalid { + String::from("Invalid or expired reset link.") + } else { + String::new() + }; + let auth_check_api = self.auth_check_api_path(token_valid); + let submit_api = self.submit_api_path(token_valid); + let success_message = match mode { + ResetPasswordPageMode::Token => { + String::from("Password reset successfully. You can sign in with the new password.") + }, + ResetPasswordPageMode::SessionCheck => { + String::from("Password updated successfully. Please sign in again.") + }, + ResetPasswordPageMode::Invalid => String::new(), + }; + + let file = AssetManager::get("templates/actions/reset_password.html").ok_or_else(|| { + AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.load_template", + anyhow!("templates/actions/reset_password.html not found"), + ) + })?; + let source = String::from_utf8(file.data.into_owned()).map_err(|err| { + AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.read_template", + err, + ) + })?; + + let mut env = Environment::new(); + env.set_auto_escape_callback(|_| AutoEscape::Html); + env.add_template("actions.reset-password", &source) + .map_err(|err| { + AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.parse_template", + err, + ) + })?; + + env.get_template("actions.reset-password") + .map_err(|err| { + AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.get_template", + err, + ) + })? + .render(context! { + token => self.token(), + mode => mode.as_str(), + auth_check_api => auth_check_api, + submit_api => submit_api, + site_name => config.site.name.clone(), + initial_error => initial_error, + success_message => success_message, + }) + .map_err(|err| { + AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.render_template", + err, + ) + }) + } + + fn page_mode(&self, token_valid: bool) -> ResetPasswordPageMode { + match self.token() { + Some(_) if token_valid => ResetPasswordPageMode::Token, + Some(_) => ResetPasswordPageMode::Invalid, + None if self.token.is_some() => ResetPasswordPageMode::Invalid, + None => ResetPasswordPageMode::SessionCheck, + } + } + + fn auth_check_api_path(&self, token_valid: bool) -> &'static str { + match self.page_mode(token_valid) { + ResetPasswordPageMode::SessionCheck => USER_INFO_API_PATH, + _ => "", + } + } + + fn submit_api_path(&self, token_valid: bool) -> &'static str { + match self.page_mode(token_valid) { + ResetPasswordPageMode::Token => RESET_PASSWORD_WITH_TOKEN_API_PATH, + ResetPasswordPageMode::SessionCheck => RESET_PASSWORD_WITH_PASSWORD_API_PATH, + ResetPasswordPageMode::Invalid => "", + } + } +} + +impl ResetPasswordPageMode { + fn as_str(self) -> &'static str { + match self { + ResetPasswordPageMode::Token => "token", + ResetPasswordPageMode::SessionCheck => "session-check", + ResetPasswordPageMode::Invalid => "invalid", + } + } +} diff --git a/auth/src/services/auth/forget_password.rs b/auth/src/services/auth/forget_password.rs index 7681950..1ddf2f6 100644 --- a/auth/src/services/auth/forget_password.rs +++ b/auth/src/services/auth/forget_password.rs @@ -11,6 +11,7 @@ use crate::{ mail::Mailer, }, models::users, + services::actions::ActionsResetPasswordService, }; const RESET_TOKEN_TTL_SECONDS: u64 = 15 * 60; @@ -60,11 +61,8 @@ impl ForgetPasswordService { ) })?; - let reset_url = format!( - "{}/api/v1/auth/reset-password?token={}", - config.domain.trim_end_matches('/'), - token - ); + let reset_url = + ActionsResetPasswordService::build_reset_password_action_url(config, &token); if let Err(err) = mailer .send_mail( diff --git a/auth/src/services/auth/reset_password.rs b/auth/src/services/auth/reset_password.rs index ed06d1c..3730e73 100644 --- a/auth/src/services/auth/reset_password.rs +++ b/auth/src/services/auth/reset_password.rs @@ -9,11 +9,6 @@ use crate::{ models::users, }; -#[derive(Deserialize)] -pub struct ResetPasswordQuery { - pub token: String, -} - #[derive(Deserialize)] pub struct ResetPasswordWithTokenService { pub token: String, diff --git a/crates/assets/assets/templates/actions/reset_password.html b/crates/assets/assets/templates/actions/reset_password.html new file mode 100644 index 0000000..ac7c186 --- /dev/null +++ b/crates/assets/assets/templates/actions/reset_password.html @@ -0,0 +1,399 @@ + + + + + + Reset Password - {{ site_name }} + + + +
+

Account Security

+

Reset your password

+

+ +
+ + + + + + + + +

+
+ + + + From 858075eb49c8ab2028b26b8bf5beb7bab60cb168 Mon Sep 17 00:00:00 2001 From: yuanzui-cf Date: Fri, 10 Apr 2026 17:45:33 +0800 Subject: [PATCH 2/5] refactor(auth): Reorganize routers and services into VSA features --- auth/src/features/actions/mod.rs | 20 ++ .../actions/reset_password.rs | 76 ++++- .../actions/verify_email.rs | 42 ++- auth/src/features/api/mod.rs | 10 + .../api/v1}/auth/forget_password.rs | 40 ++- .../api/v1}/auth/login.rs | 46 +++ auth/src/features/api/v1/auth/logout.rs | 57 ++++ auth/src/features/api/v1/auth/mod.rs | 44 +++ .../api/v1}/auth/register.rs | 48 ++++ .../api/v1}/auth/reset_password.rs | 78 +++++- .../api/v1}/auth/verify_email.rs | 31 ++ auth/src/features/api/v1/common/mod.rs | 13 + auth/src/features/api/v1/common/not_found.rs | 5 + .../api/v1/common/ping.rs} | 6 +- auth/src/features/api/v1/mod.rs | 15 + .../api/v1}/users/delete.rs | 68 +++++ .../api/v1}/users/info.rs | 33 +++ auth/src/features/api/v1/users/mod.rs | 51 ++++ .../api/v1}/users/setting.rs | 27 ++ .../api/v1}/users/update.rs | 33 +++ auth/src/features/assets.rs | 56 ++++ auth/src/features/mod.rs | 13 + auth/src/main.rs | 8 +- auth/src/routers.rs | 208 -------------- auth/src/routers/controllers.rs | 4 - auth/src/routers/controllers/actions.rs | 108 ------- auth/src/routers/controllers/auth.rs | 265 ------------------ auth/src/routers/controllers/users.rs | 153 ---------- auth/src/routers/cors.rs | 24 ++ auth/src/routers/mod.rs | 6 + auth/src/services/actions/mod.rs | 5 - auth/src/services/auth/mod.rs | 11 - auth/src/services/mod.rs | 3 - auth/src/services/users/mod.rs | 9 - 34 files changed, 833 insertions(+), 783 deletions(-) create mode 100644 auth/src/features/actions/mod.rs rename auth/src/{services => features}/actions/reset_password.rs (69%) rename auth/src/{services => features}/actions/verify_email.rs (67%) create mode 100644 auth/src/features/api/mod.rs rename auth/src/{services => features/api/v1}/auth/forget_password.rs (70%) rename auth/src/{services => features/api/v1}/auth/login.rs (67%) create mode 100644 auth/src/features/api/v1/auth/logout.rs create mode 100644 auth/src/features/api/v1/auth/mod.rs rename auth/src/{services => features/api/v1}/auth/register.rs (86%) rename auth/src/{services => features/api/v1}/auth/reset_password.rs (63%) rename auth/src/{services => features/api/v1}/auth/verify_email.rs (69%) create mode 100644 auth/src/features/api/v1/common/mod.rs create mode 100644 auth/src/features/api/v1/common/not_found.rs rename auth/src/{routers/controllers/common.rs => features/api/v1/common/ping.rs} (53%) create mode 100644 auth/src/features/api/v1/mod.rs rename auth/src/{services => features/api/v1}/users/delete.rs (59%) rename auth/src/{services => features/api/v1}/users/info.rs (72%) create mode 100644 auth/src/features/api/v1/users/mod.rs rename auth/src/{services => features/api/v1}/users/setting.rs (68%) rename auth/src/{services => features/api/v1}/users/update.rs (85%) create mode 100644 auth/src/features/assets.rs create mode 100644 auth/src/features/mod.rs delete mode 100644 auth/src/routers.rs delete mode 100644 auth/src/routers/controllers.rs delete mode 100644 auth/src/routers/controllers/actions.rs delete mode 100644 auth/src/routers/controllers/auth.rs delete mode 100644 auth/src/routers/controllers/users.rs create mode 100644 auth/src/routers/cors.rs create mode 100644 auth/src/routers/mod.rs delete mode 100644 auth/src/services/actions/mod.rs delete mode 100644 auth/src/services/auth/mod.rs delete mode 100644 auth/src/services/mod.rs delete mode 100644 auth/src/services/users/mod.rs diff --git a/auth/src/features/actions/mod.rs b/auth/src/features/actions/mod.rs new file mode 100644 index 0000000..6477162 --- /dev/null +++ b/auth/src/features/actions/mod.rs @@ -0,0 +1,20 @@ +use axum::{Router, routing::get}; + +use crate::{internal::config::Config, routers::cors, state::AppState}; + +pub mod reset_password; +pub mod verify_email; + +pub fn router(config: &Config) -> Router { + let cors = if config.dev_mode { + cors::get_public_cors() + } else { + cors::get_internal_cors() + }; + + let route = Router::new() + .route("/verify-email", get(verify_email::controller)) + .route("/reset-password", get(reset_password::controller)); + + Router::new().nest("/actions", route).layer(cors) +} diff --git a/auth/src/services/actions/reset_password.rs b/auth/src/features/actions/reset_password.rs similarity index 69% rename from auth/src/services/actions/reset_password.rs rename to auth/src/features/actions/reset_password.rs index 43fff9f..4995378 100644 --- a/auth/src/services/actions/reset_password.rs +++ b/auth/src/features/actions/reset_password.rs @@ -1,13 +1,83 @@ use anyhow::anyhow; use assets::AssetManager; +use axum::{ + extract::{Query, State}, + http::StatusCode, + response::{Html, IntoResponse, Response}, +}; use minijinja::{AutoEscape, Environment, context}; use serde::Deserialize; +use token::{TokenStore, services::PasswordResetTokenService}; -use crate::internal::{ - config::Config, - error::{AppError, AppErrorKind}, +use crate::{ + internal::{ + config::Config, + error::{AppError, AppErrorKind}, + }, + state::AppState, }; +pub async fn controller( + State(state): State, + Query(req): Query, +) -> Response { + let token_valid = match req.token() { + Some(token) => { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return render_reset_password_error(AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.redis", + err, + )); + }, + }; + + match PasswordResetTokenService::get(&mut redis, token) + .await + .map(|payload| payload.is_some()) + .map_err(|err| { + AppError::infra( + AppErrorKind::InternalError, + "actions.reset_password.get_token", + err, + ) + }) { + Ok(valid) => valid, + Err(err) => return render_reset_password_error(err), + } + }, + None => req.token.is_none(), + }; + + match req.render_reset_password_page(&state.config, token_valid) { + Ok(html) => Html(html).into_response(), + Err(err) => render_reset_password_error(err), + } +} + +fn render_reset_password_error(err: AppError) -> Response { + let source = err.source_ref().map(ToString::to_string); + tracing::error!( + op = err.op, + kind = ?err.kind, + detail = ?err.detail, + source = ?source, + "failed to render reset-password action page" + ); + + ( + StatusCode::INTERNAL_SERVER_ERROR, + Html( + "Reset Password \ + Error

Unable to load reset-password page. Please try again \ + later.

", + ), + ) + .into_response() +} + const ACTION_RESET_PASSWORD_PATH: &str = "/actions/reset-password"; const USER_INFO_API_PATH: &str = "/api/v1/user/info"; const RESET_PASSWORD_WITH_TOKEN_API_PATH: &str = "/api/v1/auth/reset-password/token"; diff --git a/auth/src/services/actions/verify_email.rs b/auth/src/features/actions/verify_email.rs similarity index 67% rename from auth/src/services/actions/verify_email.rs rename to auth/src/features/actions/verify_email.rs index 75a6d2f..50e1919 100644 --- a/auth/src/services/actions/verify_email.rs +++ b/auth/src/features/actions/verify_email.rs @@ -1,13 +1,49 @@ use anyhow::anyhow; use assets::AssetManager; +use axum::{ + extract::{Query, State}, + http::StatusCode, + response::{Html, IntoResponse, Response}, +}; use minijinja::{AutoEscape, Environment, context}; use serde::Deserialize; -use crate::internal::{ - config::Config, - error::{AppError, AppErrorKind}, +use crate::{ + internal::{ + config::Config, + error::{AppError, AppErrorKind}, + }, + state::AppState, }; +pub async fn controller( + State(state): State, + Query(req): Query, +) -> Response { + match req.render_verify_email_page(&state.config) { + Ok(html) => Html(html).into_response(), + Err(err) => { + let source = err.source_ref().map(ToString::to_string); + tracing::error!( + op = err.op, + kind = ?err.kind, + detail = ?err.detail, + source = ?source, + "failed to render verify-email action page" + ); + ( + StatusCode::INTERNAL_SERVER_ERROR, + Html( + "Verification \ + Error

Unable to load verification page. Please try \ + again later.

", + ), + ) + .into_response() + }, + } +} + #[derive(Deserialize)] pub struct ActionsVerifyEmailService { #[serde(default)] diff --git a/auth/src/features/api/mod.rs b/auth/src/features/api/mod.rs new file mode 100644 index 0000000..e517f7e --- /dev/null +++ b/auth/src/features/api/mod.rs @@ -0,0 +1,10 @@ +use axum::Router; + +use crate::{internal::config::Config, state::AppState}; + +pub mod v1; + +pub fn router(config: &Config) -> Router { + let route = Router::new().merge(v1::router(config)); + Router::new().nest("/api", route) +} diff --git a/auth/src/services/auth/forget_password.rs b/auth/src/features/api/v1/auth/forget_password.rs similarity index 70% rename from auth/src/services/auth/forget_password.rs rename to auth/src/features/api/v1/auth/forget_password.rs index 1ddf2f6..bec6923 100644 --- a/auth/src/services/auth/forget_password.rs +++ b/auth/src/features/api/v1/auth/forget_password.rs @@ -1,3 +1,4 @@ +use axum::extract::State; use minijinja::context; use redis::aio::MultiplexedConnection; use sea_orm::DatabaseConnection; @@ -5,15 +6,52 @@ use serde::Deserialize; use token::services::PasswordResetTokenService; use crate::{ + features::actions::reset_password::ActionsResetPasswordService, internal::{ config::Config, error::{AppError, AppErrorKind}, mail::Mailer, }, models::users, - services::actions::ActionsResetPasswordService, + routers::{ + extractor::Json, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, + state::AppState, }; +pub async fn controller( + State(state): State, + Json(req): Json, +) -> Response { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "auth.controller.forget_password.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ); + }, + }; + + match req + .forget_password(&state.db, &mut redis, &state.config, state.mail.as_deref()) + .await + { + Ok(message) => Response::new( + ResponseCode::OK.into(), + ResponseCode::OK.into(), + Some(message), + ), + Err(err) => app_error_to_response(err), + } +} + const RESET_TOKEN_TTL_SECONDS: u64 = 15 * 60; #[derive(Deserialize)] diff --git a/auth/src/services/auth/login.rs b/auth/src/features/api/v1/auth/login.rs similarity index 67% rename from auth/src/services/auth/login.rs rename to auth/src/features/api/v1/auth/login.rs index f7ea337..876e7fe 100644 --- a/auth/src/services/auth/login.rs +++ b/auth/src/features/api/v1/auth/login.rs @@ -1,3 +1,5 @@ +use axum::extract::State; +use axum_extra::extract::CookieJar; use redis::aio::MultiplexedConnection; use sea_orm::DatabaseConnection; use serde::{Deserialize, Serialize}; @@ -9,8 +11,52 @@ use crate::{ session::SESSION_TTL_SECONDS, }, models::users, + routers::{ + extractor::{GuestAccess, Json}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie, + }, + state::AppState, }; +pub async fn controller( + _: GuestAccess, + State(state): State, + jar: CookieJar, + Json(req): Json, +) -> (CookieJar, Response) { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return ( + jar, + app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "auth.controller.login.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ), + ); + }, + }; + + match req.login(&state.db, &mut redis).await { + Ok((data, sid)) => { + let session_cookie = cookie::build_session_cookie(sid, !state.config.dev_mode); + let jar = jar.add(session_cookie); + + ( + jar, + Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), + ) + }, + Err(err) => (jar, app_error_to_response(err)), + } +} + /// Response structure for login API #[derive(Deserialize, Serialize)] pub struct LoginResponse { diff --git a/auth/src/features/api/v1/auth/logout.rs b/auth/src/features/api/v1/auth/logout.rs new file mode 100644 index 0000000..91e45cc --- /dev/null +++ b/auth/src/features/api/v1/auth/logout.rs @@ -0,0 +1,57 @@ +use axum::extract::State; +use axum_extra::extract::CookieJar; +use token::services::SessionService; + +use crate::{ + internal::error::{AppError, AppErrorKind}, + routers::{ + extractor::LoginAccess, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie::CookieJarExt, + }, + state::AppState, +}; + +pub async fn controller( + login: LoginAccess, + State(state): State, + jar: CookieJar, +) -> (CookieJar, Response) { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return ( + jar, + app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "auth.controller.logout.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ), + ); + }, + }; + + let session = login.session; + + if let Err(err) = SessionService::delete(&mut redis, &session).await { + return ( + jar, + app_error_to_response(AppError::infra( + AppErrorKind::InternalError, + "auth.controller.logout.delete_session", + err, + )), + ); + } + + let jar = jar.remove_session_cookie(); + + ( + jar, + Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), None), + ) +} diff --git a/auth/src/features/api/v1/auth/mod.rs b/auth/src/features/api/v1/auth/mod.rs new file mode 100644 index 0000000..478d324 --- /dev/null +++ b/auth/src/features/api/v1/auth/mod.rs @@ -0,0 +1,44 @@ +use axum::{ + Router, + routing::{any, patch, post}, +}; + +use crate::{ + internal::config::Config, + routers::{cors, middleware::permission::PermissionAccess}, + state::AppState, +}; + +mod forget_password; +mod login; +mod logout; +mod register; +mod reset_password; +mod verify_email; + +pub fn router(config: &Config) -> Router { + let cors = if config.dev_mode { + cors::get_public_cors() + } else { + cors::get_internal_cors() + }; + + let route = Router::new() + .route("/login", post(login::controller)) + .route("/logout", any(logout::controller)) + .route("/register", post(register::controller)) + .route("/verify-email", post(verify_email::controller)) + .route("/forget-password", post(forget_password::controller)) + .route( + "/reset-password/token", + patch(reset_password::controller_with_token), + ) + .route( + "/reset-password/password", + patch(reset_password::controller_with_password).layer(PermissionAccess::any(&[ + "user:reset_password:self", + "user:reset_password:other", + ])), + ); + Router::new().nest("/auth", route).layer(cors) +} diff --git a/auth/src/services/auth/register.rs b/auth/src/features/api/v1/auth/register.rs similarity index 86% rename from auth/src/services/auth/register.rs rename to auth/src/features/api/v1/auth/register.rs index ed127f8..d5fc0ff 100644 --- a/auth/src/services/auth/register.rs +++ b/auth/src/features/api/v1/auth/register.rs @@ -1,5 +1,6 @@ use std::sync::OnceLock; +use axum::extract::State; use crypto::password::{PasswordHashAlgorithm, PasswordManager}; use minijinja::context; use redis::aio::MultiplexedConnection; @@ -16,8 +17,55 @@ use crate::{ mail::Mailer, }, models::{common::ModelError, users}, + routers::{ + extractor::{GuestAccess, Json}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, + state::AppState, }; +pub async fn controller( + _guest: GuestAccess, + State(state): State, + Json(req): Json, +) -> Response { + let mut redis: Option = if state.mail.is_some() { + match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => Some(redis), + Err(err) => { + return app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "auth.controller.register.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ); + }, + } + } else { + None + }; + + match req + .register( + &state.db, + &state.config, + state.mail.as_deref(), + redis.as_mut(), + ) + .await + { + Ok(message) => Response::new( + ResponseCode::OK.into(), + ResponseCode::OK.into(), + Some(message), + ), + Err(err) => app_error_to_response(err), + } +} + static EMAIL_RE: OnceLock = OnceLock::new(); const REGISTER_TOKEN_TTL_SECONDS: u64 = 60 * 60; const REGISTER_TOKEN_REUSE_MIN_TTL_SECONDS: u64 = 60; diff --git a/auth/src/services/auth/reset_password.rs b/auth/src/features/api/v1/auth/reset_password.rs similarity index 63% rename from auth/src/services/auth/reset_password.rs rename to auth/src/features/api/v1/auth/reset_password.rs index 3730e73..da8a27b 100644 --- a/auth/src/services/auth/reset_password.rs +++ b/auth/src/features/api/v1/auth/reset_password.rs @@ -1,14 +1,90 @@ +use axum::extract::State; +use axum_extra::extract::CookieJar; use crypto::password::PasswordManager; use redis::aio::MultiplexedConnection; use sea_orm::DatabaseConnection; use serde::Deserialize; -use token::services::PasswordResetTokenService; +use token::services::{PasswordResetTokenService, SessionService}; use crate::{ internal::error::{AppError, AppErrorKind}, models::users, + routers::{ + extractor::{Json, LoginAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie::CookieJarExt, + }, + state::AppState, }; +pub async fn controller_with_token( + State(state): State, + Json(req): Json, +) -> Response { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "auth.controller.reset_password_token.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ); + }, + }; + + match req.reset_password(&state.db, &mut redis).await { + Ok(()) => ResponseCode::OK.into(), + Err(err) => app_error_to_response(err), + } +} + +pub async fn controller_with_password( + login: LoginAccess, + State(state): State, + jar: CookieJar, + Json(req): Json, +) -> (CookieJar, Response) { + if let Err(err) = req.reset_password(&state.db, &login.user.0).await { + return (jar, app_error_to_response(err)); + } + + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return ( + jar, + app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "auth.controller.reset_password_password.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ), + ); + }, + }; + + if let Err(err) = SessionService::delete(&mut redis, &login.session).await { + return ( + jar, + app_error_to_response(AppError::infra( + AppErrorKind::InternalError, + "auth.controller.reset_password_password.delete_session", + err, + )), + ); + } + + let jar = jar.remove_session_cookie(); + + (jar, ResponseCode::OK.into()) +} + #[derive(Deserialize)] pub struct ResetPasswordWithTokenService { pub token: String, diff --git a/auth/src/services/auth/verify_email.rs b/auth/src/features/api/v1/auth/verify_email.rs similarity index 69% rename from auth/src/services/auth/verify_email.rs rename to auth/src/features/api/v1/auth/verify_email.rs index f849fda..442c25d 100644 --- a/auth/src/services/auth/verify_email.rs +++ b/auth/src/features/api/v1/auth/verify_email.rs @@ -1,3 +1,4 @@ +use axum::extract::State; use redis::aio::MultiplexedConnection; use sea_orm::DatabaseConnection; use serde::Deserialize; @@ -6,8 +7,38 @@ use token::{TokenStore, services::RegisterTokenService}; use crate::{ internal::error::{AppError, AppErrorKind}, models::users, + routers::{ + extractor::Json, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, + state::AppState, }; +pub async fn controller( + State(state): State, + Json(req): Json, +) -> Response { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "auth.controller.verify_email.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ); + }, + }; + + match req.verify_email(&state.db, &mut redis).await { + Ok(_) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), None), + Err(err) => app_error_to_response(err), + } +} + #[derive(Deserialize)] pub struct VerifyEmailService { pub token: String, diff --git a/auth/src/features/api/v1/common/mod.rs b/auth/src/features/api/v1/common/mod.rs new file mode 100644 index 0000000..d602cdc --- /dev/null +++ b/auth/src/features/api/v1/common/mod.rs @@ -0,0 +1,13 @@ +use axum::{Router, routing::any}; + +use crate::{routers::cors, state::AppState}; + +pub mod not_found; +pub mod ping; + +pub fn router() -> Router { + Router::new() + .route("/ping", any(ping::controller)) + .fallback(not_found::controller) + .layer(cors::get_public_cors()) +} diff --git a/auth/src/features/api/v1/common/not_found.rs b/auth/src/features/api/v1/common/not_found.rs new file mode 100644 index 0000000..6f21d18 --- /dev/null +++ b/auth/src/features/api/v1/common/not_found.rs @@ -0,0 +1,5 @@ +use crate::routers::serializer::{Response, ResponseCode}; + +pub async fn controller() -> Response { + ResponseCode::NotFound.into() +} diff --git a/auth/src/routers/controllers/common.rs b/auth/src/features/api/v1/common/ping.rs similarity index 53% rename from auth/src/routers/controllers/common.rs rename to auth/src/features/api/v1/common/ping.rs index e5fdb72..6c54891 100644 --- a/auth/src/routers/controllers/common.rs +++ b/auth/src/features/api/v1/common/ping.rs @@ -1,9 +1,5 @@ use crate::routers::serializer::{Response, ResponseCode}; -pub async fn not_found() -> Response { - ResponseCode::NotFound.into() -} - -pub async fn ping() -> Response { +pub async fn controller() -> Response { Response::new(ResponseCode::OK.into(), "pong".into(), None) } diff --git a/auth/src/features/api/v1/mod.rs b/auth/src/features/api/v1/mod.rs new file mode 100644 index 0000000..753a6bd --- /dev/null +++ b/auth/src/features/api/v1/mod.rs @@ -0,0 +1,15 @@ +use axum::Router; + +use crate::{internal::config::Config, state::AppState}; + +pub mod auth; +pub mod common; +pub mod users; + +pub fn router(config: &Config) -> Router { + let route = Router::new() + .merge(auth::router(config)) + .merge(users::router()) + .merge(common::router()); + Router::new().nest("/v1", route) +} diff --git a/auth/src/services/users/delete.rs b/auth/src/features/api/v1/users/delete.rs similarity index 59% rename from auth/src/services/users/delete.rs rename to auth/src/features/api/v1/users/delete.rs index 742dd1c..2932dac 100644 --- a/auth/src/services/users/delete.rs +++ b/auth/src/features/api/v1/users/delete.rs @@ -1,11 +1,79 @@ +use axum::extract::{Path, State}; +use axum_extra::extract::CookieJar; use sea_orm::DatabaseConnection; use serde::{Deserialize, Serialize}; +use token::services::SessionService; use crate::{ internal::error::{AppError, AppErrorKind}, models::{permission, role, users}, + routers::{ + extractor::{Json, LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie::CookieJarExt, + }, + state::AppState, }; +pub async fn controller( + login: LoginAccess, + State(state): State, + jar: CookieJar, + Json(req): Json, +) -> (CookieJar, Response) { + let mut redis = match state.redis.get_multiplexed_tokio_connection().await { + Ok(redis) => redis, + Err(err) => { + return ( + jar, + app_error_to_response( + AppError::infra( + AppErrorKind::InternalError, + "users.controller.delete.redis", + err, + ) + .with_detail("Unable to connect to redis"), + ), + ); + }, + }; + + let session = login.session; + + if let Err(err) = req.delete(&state.db, login.user.0).await { + return (jar, app_error_to_response(err)); + } + + if let Err(err) = SessionService::delete(&mut redis, &session).await { + return ( + jar, + app_error_to_response(AppError::infra( + AppErrorKind::InternalError, + "users.controller.delete.delete_session", + err, + )), + ); + } + + let jar = jar.remove_session_cookie(); + + (jar, ResponseCode::OK.into()) +} + +pub async fn controller_by_uid( + OperatorAccess(login): OperatorAccess, + State(state): State, + Path(uid): Path, +) -> Response { + let service = AdminDeleteService; + + match service.delete(&state.db, uid, login.level).await { + Ok(()) => ResponseCode::OK.into(), + Err(err) => app_error_to_response(err), + } +} + /// Service handling user delete operations #[derive(Deserialize, Serialize)] pub struct DeleteService { diff --git a/auth/src/services/users/info.rs b/auth/src/features/api/v1/users/info.rs similarity index 72% rename from auth/src/services/users/info.rs rename to auth/src/features/api/v1/users/info.rs index fc926ea..4aa44d8 100644 --- a/auth/src/services/users/info.rs +++ b/auth/src/features/api/v1/users/info.rs @@ -1,3 +1,4 @@ +use axum::extract::{Path, State}; use sea_orm::DatabaseConnection; use serde::{Deserialize, Serialize}; @@ -7,8 +8,40 @@ use crate::{ user_info::{self, Gender}, user_settings, users, }, + routers::{ + extractor::{LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, + state::AppState, }; +pub async fn controller( + State(state): State, + login: LoginAccess, +) -> Response { + let service = InfoService; + let (user, info, settings) = login.user; + + match service.info(&state.db, user, info, settings, None).await { + Ok(data) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), + Err(err) => app_error_to_response(err), + } +} + +pub async fn controller_by_uid( + OperatorAccess(login): OperatorAccess, + State(state): State, + Path(uid): Path, +) -> Response { + let service = InfoService; + + match service.info_by_uid(&state.db, uid, login.user.0).await { + Ok(data) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), + Err(err) => app_error_to_response(err), + } +} + #[derive(Serialize, Deserialize)] pub struct InfoResponse { pub uid: i32, diff --git a/auth/src/features/api/v1/users/mod.rs b/auth/src/features/api/v1/users/mod.rs new file mode 100644 index 0000000..b0042b2 --- /dev/null +++ b/auth/src/features/api/v1/users/mod.rs @@ -0,0 +1,51 @@ +use axum::{ + Router, + routing::{any, delete, patch}, +}; + +use crate::{routers::middleware::permission::PermissionAccess, state::AppState}; + +mod delete; +mod info; +mod setting; +mod update; + +pub fn router() -> Router { + let route = Router::new() + .route( + "/info", + any(info::controller).layer(PermissionAccess::all(&["user:read:self"])), + ) + .route( + "/info/{uid}", + any(info::controller_by_uid).layer(PermissionAccess::any(&[ + "user:read:active", + "user:read:all", + ])), + ) + .route( + "/setting", + any(setting::controller).layer(PermissionAccess::all(&["user:read:self"])), + ) + .route( + "/setting/{uid}", + any(setting::controller_by_uid).layer(PermissionAccess::all(&["user:read:all"])), + ) + .route( + "/delete", + delete(delete::controller).layer(PermissionAccess::all(&["user:delete:self"])), + ) + .route( + "/delete/{uid}", + delete(delete::controller_by_uid).layer(PermissionAccess::all(&["user:delete:all"])), + ) + .route( + "/update", + patch(update::controller).layer(PermissionAccess::all(&["user:update:self"])), + ) + .route( + "/update/{uid}", + patch(update::controller_by_uid).layer(PermissionAccess::all(&["user:update:all"])), + ); + Router::new().nest("/user", route) +} diff --git a/auth/src/services/users/setting.rs b/auth/src/features/api/v1/users/setting.rs similarity index 68% rename from auth/src/services/users/setting.rs rename to auth/src/features/api/v1/users/setting.rs index a08d218..a4fc8a5 100644 --- a/auth/src/services/users/setting.rs +++ b/auth/src/features/api/v1/users/setting.rs @@ -1,11 +1,38 @@ +use axum::extract::{Path, State}; use sea_orm::DatabaseConnection; use serde::{Deserialize, Serialize}; use crate::{ internal::error::{AppError, AppErrorKind}, models::{common::ModelError, user_settings}, + routers::{ + extractor::{LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, + state::AppState, }; +pub async fn controller(login: LoginAccess) -> Response { + let service = SettingService; + let data = service.setting(login.user.2); + + Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)) +} + +pub async fn controller_by_uid( + OperatorAccess(_login): OperatorAccess, + State(state): State, + Path(uid): Path, +) -> Response { + let service = SettingService; + + match service.setting_by_uid(&state.db, uid).await { + Ok(data) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), + Err(err) => app_error_to_response(err), + } +} + #[derive(Serialize, Deserialize)] pub struct SettingResponse { pub uid: i32, diff --git a/auth/src/services/users/update.rs b/auth/src/features/api/v1/users/update.rs similarity index 85% rename from auth/src/services/users/update.rs rename to auth/src/features/api/v1/users/update.rs index b699e24..c2d1515 100644 --- a/auth/src/services/users/update.rs +++ b/auth/src/features/api/v1/users/update.rs @@ -1,3 +1,4 @@ +use axum::extract::{Path, State}; use sea_orm::{ ActiveModelTrait, ActiveValue::Set, DatabaseConnection, DbErr, IntoActiveModel, TransactionTrait, @@ -12,8 +13,40 @@ use crate::{ user_info::{self, Gender}, user_settings, users, }, + routers::{ + extractor::{Json, LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, + state::AppState, }; +pub async fn controller( + login: LoginAccess, + State(state): State, + Json(req): Json, +) -> Response { + match req + .update(&state.db, login.user.0, login.user.1, login.user.2) + .await + { + Ok(()) => ResponseCode::OK.into(), + Err(err) => app_error_to_response(err), + } +} + +pub async fn controller_by_uid( + OperatorAccess(login): OperatorAccess, + State(state): State, + Path(uid): Path, + Json(req): Json, +) -> Response { + match req.update_by_uid(&state.db, uid, login.level).await { + Ok(()) => ResponseCode::OK.into(), + Err(err) => app_error_to_response(err), + } +} + #[derive(Deserialize, Serialize)] pub struct UpdateService { pub nickname: Option, diff --git a/auth/src/features/assets.rs b/auth/src/features/assets.rs new file mode 100644 index 0000000..cd7fd63 --- /dev/null +++ b/auth/src/features/assets.rs @@ -0,0 +1,56 @@ +use assets::AssetManager; +use axum::{ + body::Body, + extract::Request, + http::{HeaderValue, Method, Response, StatusCode, header}, + response::IntoResponse, +}; + +use crate::routers::utils::content_type; + +pub async fn controller(request: Request) -> impl IntoResponse { + let method = request.method().clone(); + if method != Method::GET && method != Method::HEAD { + return StatusCode::NOT_FOUND.into_response(); + } + + let raw_path = request.uri().path().trim_start_matches('/'); + let mut candidates = Vec::new(); + if raw_path.is_empty() { + candidates.push("public/index.html".to_owned()); + } else { + candidates.push(format!("public/{raw_path}")); + + if raw_path.ends_with('/') || !raw_path.contains('.') { + let trimmed = raw_path.trim_end_matches('/'); + if trimmed.is_empty() { + candidates.push("public/index.html".to_owned()); + } else { + candidates.push(format!("public/{trimmed}/index.html")); + } + } + } + + for candidate in candidates { + if let Some(file) = AssetManager::get(&candidate) { + let mut response = Response::new(Body::from(file.data.into_owned())); + *response.status_mut() = StatusCode::OK; + + if let Ok(etag) = HeaderValue::from_str(&format!( + "\"{}\"", + base16ct::lower::encode_string(&file.metadata.sha256_hash()) + )) { + response.headers_mut().insert(header::ETAG, etag); + } + + response.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static(content_type::from_path(&candidate)), + ); + + return response; + } + } + + StatusCode::NOT_FOUND.into_response() +} diff --git a/auth/src/features/mod.rs b/auth/src/features/mod.rs new file mode 100644 index 0000000..bac9438 --- /dev/null +++ b/auth/src/features/mod.rs @@ -0,0 +1,13 @@ +use axum::Router; + +use crate::{internal::config::Config, state::AppState}; + +pub mod actions; +pub mod api; +pub mod assets; + +pub fn router(app: Router, config: &Config) -> Router { + app.merge(api::router(config)) + .merge(actions::router(config)) + .fallback(assets::controller) +} diff --git a/auth/src/main.rs b/auth/src/main.rs index c23389b..8f98640 100644 --- a/auth/src/main.rs +++ b/auth/src/main.rs @@ -3,10 +3,10 @@ mod init; +mod features; mod internal; mod models; mod routers; -mod services; mod state; use std::{io, sync::Arc}; @@ -16,7 +16,7 @@ use colored::Colorize; use tokio::{net::TcpListener, signal, sync::oneshot}; use tracing::{error, info}; -use crate::{internal::config::Config, routers::get_router}; +use crate::internal::config::Config; async fn shutdown_signal() { let ctrl_c = async { @@ -87,8 +87,8 @@ async fn main() -> anyhow::Result<()> { }) .await; - let app = - get_router(Router::new(), &config).with_state(state::APP_STATE.get().unwrap().clone()); + let app = features::router(Router::new(), &config) + .with_state(state::APP_STATE.get().unwrap().clone()); let addr = format!("{}:{}", &host, config.port); diff --git a/auth/src/routers.rs b/auth/src/routers.rs deleted file mode 100644 index 535e34c..0000000 --- a/auth/src/routers.rs +++ /dev/null @@ -1,208 +0,0 @@ -pub mod controllers; -pub mod extractor; -pub mod middleware; -pub mod response; -pub mod serializer; -pub mod utils; - -use assets::AssetManager; -use axum::{ - Router, - body::Body, - extract::Request, - http::{HeaderValue, Method, StatusCode, header}, - response::{IntoResponse, Response}, - routing::{any, delete, get, patch, post}, -}; -use tower::ServiceBuilder; -use tower_http::{cors, cors::CorsLayer}; - -use crate::{ - internal::config::Config, - routers::{ - controllers::{actions, auth, common, users}, - middleware::permission::PermissionAccess, - utils::content_type, - }, - state::AppState, -}; - -pub fn get_router(app: Router, config: &Config) -> Router { - // CORS - let public_cors = { - let cors = CorsLayer::new() - .allow_methods([ - Method::GET, - Method::POST, - Method::PATCH, - Method::DELETE, - Method::OPTIONS, - ]) - .allow_origin(cors::Any); - ServiceBuilder::new().layer(cors).into_inner() - }; - let internal_cors = { - let cors = CorsLayer::new().allow_methods([ - Method::GET, - Method::POST, - Method::PATCH, - Method::DELETE, - Method::OPTIONS, - ]); - ServiceBuilder::new().layer(cors).into_inner() - }; - - // Oauth - let oauth = { - let oauth = Router::new(); - Router::new().nest("/oauth", oauth) - }; - - let action = { - let route = Router::new() - .route("/verify-email", get(actions::verify_email)) - .route("/reset-password", get(actions::reset_password)); - let route = Router::new().nest("/actions", route); - if config.dev_mode { - route.layer(public_cors.clone()) - } else { - route.layer(internal_cors.clone()) - } - }; - - let api_v1 = { - // Auth - let auth = { - let route = Router::new() - .route("/login", post(auth::login)) - .route("/logout", any(auth::logout)) - .route("/register", post(auth::register)) - .route("/verify-email", post(auth::verify_email)) - .route("/forget-password", post(auth::forget_password)) - .route( - "/reset-password/token", - patch(auth::reset_password_with_token), - ) - .route( - "/reset-password/password", - patch(auth::reset_password_with_password).layer(PermissionAccess::any(&[ - "user:reset_password:self", - "user:reset_password:other", - ])), - ); - let route = Router::new().nest("/auth", route); - if config.dev_mode { - route.layer(public_cors.clone()) - } else { - route.layer(internal_cors.clone()) - } - }; - - // User - let user = { - let route = Router::new() - .route( - "/info", - any(users::info).layer(PermissionAccess::all(&["user:read:self"])), - ) - .route( - "/info/{uid}", - any(users::info_by_uid).layer(PermissionAccess::any(&[ - "user:read:active", - "user:read:all", - ])), - ) - .route( - "/setting", - any(users::setting).layer(PermissionAccess::all(&["user:read:self"])), - ) - .route( - "/setting/{uid}", - any(users::setting_by_uid).layer(PermissionAccess::all(&["user:read:all"])), - ) - .route( - "/delete", - delete(users::delete).layer(PermissionAccess::all(&["user:delete:self"])), - ) - .route( - "/delete/{uid}", - delete(users::delete_by_uid).layer(PermissionAccess::all(&["user:delete:all"])), - ) - .route( - "/update", - patch(users::update).layer(PermissionAccess::all(&["user:update:self"])), - ) - .route( - "/update/{uid}", - patch(users::update_by_uid).layer(PermissionAccess::all(&["user:update:all"])), - ); - Router::new().nest("/user", route) - }; - - let common = Router::new() - .route("/ping", any(common::ping)) - .fallback(common::not_found) - .layer(public_cors); - - let route = Router::new().merge(auth).merge(user).merge(common); - Router::new().nest("/v1", route) - }; - - // API - let api = { - let route = Router::new().merge(api_v1); - Router::new().nest("/api", route) - }; - - app.merge(api) - .merge(oauth) - .merge(action) - .fallback(static_asset_fallback) -} - -async fn static_asset_fallback(request: Request) -> impl IntoResponse { - let method = request.method().clone(); - if method != Method::GET && method != Method::HEAD { - return StatusCode::NOT_FOUND.into_response(); - } - - let raw_path = request.uri().path().trim_start_matches('/'); - let mut candidates = Vec::new(); - if raw_path.is_empty() { - candidates.push("public/index.html".to_owned()); - } else { - candidates.push(format!("public/{raw_path}")); - - if raw_path.ends_with('/') || !raw_path.contains('.') { - let trimmed = raw_path.trim_end_matches('/'); - if trimmed.is_empty() { - candidates.push("public/index.html".to_owned()); - } else { - candidates.push(format!("public/{trimmed}/index.html")); - } - } - } - - for candidate in candidates { - if let Some(file) = AssetManager::get(&candidate) { - let mut response = Response::new(Body::from(file.data.into_owned())); - *response.status_mut() = StatusCode::OK; - - if let Ok(etag) = HeaderValue::from_str(&format!( - "\"{}\"", - base16ct::lower::encode_string(&file.metadata.sha256_hash()) - )) { - response.headers_mut().insert(header::ETAG, etag); - } - - response.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static(content_type::from_path(&candidate)), - ); - - return response; - } - } - - StatusCode::NOT_FOUND.into_response() -} diff --git a/auth/src/routers/controllers.rs b/auth/src/routers/controllers.rs deleted file mode 100644 index 8283a8e..0000000 --- a/auth/src/routers/controllers.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod actions; -pub mod auth; -pub mod common; -pub mod users; diff --git a/auth/src/routers/controllers/actions.rs b/auth/src/routers/controllers/actions.rs deleted file mode 100644 index 447ac28..0000000 --- a/auth/src/routers/controllers/actions.rs +++ /dev/null @@ -1,108 +0,0 @@ -use axum::{ - extract::{Query, State}, - http::StatusCode, - response::{Html, IntoResponse, Response as AxumResponse}, -}; -use redis::aio::MultiplexedConnection; -use token::{TokenStore, services::PasswordResetTokenService}; - -use crate::{ - internal::error::{AppError, AppErrorKind}, - services::actions::{ActionsResetPasswordService, ActionsVerifyEmailService}, - state::AppState, -}; - -pub async fn verify_email( - State(state): State, - Query(req): Query, -) -> AxumResponse { - match req.render_verify_email_page(&state.config) { - Ok(html) => Html(html).into_response(), - Err(err) => { - let source = err.source_ref().map(ToString::to_string); - tracing::error!( - op = err.op, - kind = ?err.kind, - detail = ?err.detail, - source = ?source, - "failed to render verify-email action page" - ); - ( - StatusCode::INTERNAL_SERVER_ERROR, - Html( - "Verification \ - Error

Unable to load verification page. Please try \ - again later.

", - ), - ) - .into_response() - }, - } -} - -pub async fn reset_password( - State(state): State, - Query(req): Query, -) -> AxumResponse { - let token_valid = match req.token() { - Some(token) => { - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return render_reset_password_error(AppError::infra( - AppErrorKind::InternalError, - "actions.reset_password.redis", - err, - )); - }, - }; - - match has_valid_reset_password_token(&mut redis, token).await { - Ok(valid) => valid, - Err(err) => return render_reset_password_error(err), - } - }, - None => req.token.is_none(), - }; - - match req.render_reset_password_page(&state.config, token_valid) { - Ok(html) => Html(html).into_response(), - Err(err) => render_reset_password_error(err), - } -} - -async fn has_valid_reset_password_token( - redis: &mut MultiplexedConnection, - token: &str, -) -> Result { - ::get(redis, token) - .await - .map(|payload| payload.is_some()) - .map_err(|err| { - AppError::infra( - AppErrorKind::InternalError, - "actions.reset_password.get_token", - err, - ) - }) -} - -fn render_reset_password_error(err: AppError) -> AxumResponse { - let source = err.source_ref().map(ToString::to_string); - tracing::error!( - op = err.op, - kind = ?err.kind, - detail = ?err.detail, - source = ?source, - "failed to render reset-password action page" - ); - ( - StatusCode::INTERNAL_SERVER_ERROR, - Html( - "Reset Password \ - Error

Unable to load reset-password page. Please try again \ - later.

", - ), - ) - .into_response() -} diff --git a/auth/src/routers/controllers/auth.rs b/auth/src/routers/controllers/auth.rs deleted file mode 100644 index 4648893..0000000 --- a/auth/src/routers/controllers/auth.rs +++ /dev/null @@ -1,265 +0,0 @@ -use axum::extract::State; -use axum_extra::extract::CookieJar; -use redis::aio::MultiplexedConnection; -use token::services::SessionService; - -use crate::{ - internal::error::{AppError, AppErrorKind}, - routers::{ - extractor::{GuestAccess, Json, LoginAccess}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - utils::cookie::{self, CookieJarExt}, - }, - services::auth, - state::AppState, -}; - -/// Auth register -pub async fn register( - _guest: GuestAccess, - State(state): State, - Json(req): Json, -) -> Response { - let mut redis: Option = if state.mail.is_some() { - match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => Some(redis), - Err(err) => { - return app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "auth.controller.register.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ); - }, - } - } else { - None - }; - - match req - .register( - &state.db, - &state.config, - state.mail.as_deref(), - redis.as_mut(), - ) - .await - { - Ok(message) => Response::new( - ResponseCode::OK.into(), - ResponseCode::OK.into(), - Some(message), - ), - Err(err) => app_error_to_response(err), - } -} - -/// Auth login -pub async fn login( - _: GuestAccess, - State(state): State, - jar: CookieJar, - Json(req): Json, -) -> (CookieJar, Response) { - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return ( - jar, - app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "auth.controller.login.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ), - ); - }, - }; - - match req.login(&state.db, &mut redis).await { - Ok((data, sid)) => { - let session_cookie = cookie::build_session_cookie(sid, !state.config.dev_mode); - let jar = jar.add(session_cookie); - - ( - jar, - Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), - ) - }, - Err(err) => (jar, app_error_to_response(err)), - } -} - -/// Auth logout -pub async fn logout( - login: LoginAccess, - State(state): State, - jar: CookieJar, -) -> (CookieJar, Response) { - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return ( - jar, - app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "auth.controller.logout.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ), - ); - }, - }; - - let session = login.session; - - if let Err(err) = SessionService::delete(&mut redis, &session).await { - return ( - jar, - app_error_to_response(AppError::infra( - AppErrorKind::InternalError, - "auth.controller.logout.delete_session", - err, - )), - ); - } - - let jar = jar.remove_session_cookie(); - - ( - jar, - Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), None), - ) -} - -/// Auth reset password with token -pub async fn reset_password_with_token( - State(state): State, - Json(req): Json, -) -> Response { - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "auth.controller.reset_password_token.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ); - }, - }; - - match req.reset_password(&state.db, &mut redis).await { - Ok(()) => ResponseCode::OK.into(), - Err(err) => app_error_to_response(err), - } -} - -/// Auth reset password with current password -pub async fn reset_password_with_password( - login: LoginAccess, - State(state): State, - jar: CookieJar, - Json(req): Json, -) -> (CookieJar, Response) { - if let Err(err) = req.reset_password(&state.db, &login.user.0).await { - return (jar, app_error_to_response(err)); - } - - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return ( - jar, - app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "auth.controller.reset_password_password.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ), - ); - }, - }; - - if let Err(err) = SessionService::delete(&mut redis, &login.session).await { - return ( - jar, - app_error_to_response(AppError::infra( - AppErrorKind::InternalError, - "auth.controller.reset_password_password.delete_session", - err, - )), - ); - } - - let jar = jar.remove_session_cookie(); - - (jar, ResponseCode::OK.into()) -} - -/// Auth forget password -pub async fn forget_password( - State(state): State, - Json(req): Json, -) -> Response { - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "auth.controller.forget_password.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ); - }, - }; - - match req - .forget_password(&state.db, &mut redis, &state.config, state.mail.as_deref()) - .await - { - Ok(message) => Response::new( - ResponseCode::OK.into(), - ResponseCode::OK.into(), - Some(message), - ), - Err(err) => app_error_to_response(err), - } -} - -pub async fn verify_email( - State(state): State, - Json(req): Json, -) -> Response { - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "auth.controller.verify_email.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ); - }, - }; - - match req.verify_email(&state.db, &mut redis).await { - Ok(_) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), None), - Err(err) => app_error_to_response(err), - } -} diff --git a/auth/src/routers/controllers/users.rs b/auth/src/routers/controllers/users.rs deleted file mode 100644 index bba0b56..0000000 --- a/auth/src/routers/controllers/users.rs +++ /dev/null @@ -1,153 +0,0 @@ -use axum::extract::{Path, State}; -use axum_extra::extract::CookieJar; -use token::services::SessionService; - -use crate::{ - internal::error::{AppError, AppErrorKind}, - routers::{ - extractor::{Json, LoginAccess, OperatorAccess}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - utils::cookie::CookieJarExt, - }, - services::users, - state::AppState, -}; - -/// User info -pub async fn info( - State(state): State, - login: LoginAccess, -) -> Response { - let service = users::InfoService; - let (user, info, settings) = login.user; - - match service.info(&state.db, user, info, settings, None).await { - Ok(data) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), - Err(err) => app_error_to_response(err), - } -} - -/// User setting -pub async fn setting(login: LoginAccess) -> Response { - let service = users::SettingService; - let data = service.setting(login.user.2); - - Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)) -} - -/// User setting by uid -pub async fn setting_by_uid( - OperatorAccess(_login): OperatorAccess, - State(state): State, - Path(uid): Path, -) -> Response { - let service = users::SettingService; - - match service.setting_by_uid(&state.db, uid).await { - Ok(data) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), - Err(err) => app_error_to_response(err), - } -} - -/// User info by uid -pub async fn info_by_uid( - OperatorAccess(login): OperatorAccess, - State(state): State, - Path(uid): Path, -) -> Response { - let service = users::InfoService; - - match service.info_by_uid(&state.db, uid, login.user.0).await { - Ok(data) => Response::new(ResponseCode::OK.into(), ResponseCode::OK.into(), Some(data)), - Err(err) => app_error_to_response(err), - } -} - -/// User delete -pub async fn delete( - login: LoginAccess, - State(state): State, - jar: CookieJar, - Json(req): Json, -) -> (CookieJar, Response) { - let mut redis = match state.redis.get_multiplexed_tokio_connection().await { - Ok(redis) => redis, - Err(err) => { - return ( - jar, - app_error_to_response( - AppError::infra( - AppErrorKind::InternalError, - "users.controller.delete.redis", - err, - ) - .with_detail("Unable to connect to redis"), - ), - ); - }, - }; - - let session = login.session; - - if let Err(err) = req.delete(&state.db, login.user.0).await { - return (jar, app_error_to_response(err)); - } - - if let Err(err) = SessionService::delete(&mut redis, &session).await { - return ( - jar, - app_error_to_response(AppError::infra( - AppErrorKind::InternalError, - "users.controller.delete.delete_session", - err, - )), - ); - } - - let jar = jar.remove_session_cookie(); - - (jar, ResponseCode::OK.into()) -} - -/// User delete by uid -pub async fn delete_by_uid( - OperatorAccess(login): OperatorAccess, - State(state): State, - Path(uid): Path, -) -> Response { - let service = users::AdminDeleteService; - - match service.delete(&state.db, uid, login.level).await { - Ok(()) => ResponseCode::OK.into(), - Err(err) => app_error_to_response(err), - } -} - -/// User update -pub async fn update( - login: LoginAccess, - State(state): State, - Json(req): Json, -) -> Response { - match req - .update(&state.db, login.user.0, login.user.1, login.user.2) - .await - { - Ok(()) => ResponseCode::OK.into(), - Err(err) => app_error_to_response(err), - } -} - -/// User update by uid -pub async fn update_by_uid( - OperatorAccess(login): OperatorAccess, - State(state): State, - Path(uid): Path, - Json(req): Json, -) -> Response { - match req.update_by_uid(&state.db, uid, login.level).await { - Ok(()) => ResponseCode::OK.into(), - Err(err) => app_error_to_response(err), - } -} diff --git a/auth/src/routers/cors.rs b/auth/src/routers/cors.rs new file mode 100644 index 0000000..637ff9e --- /dev/null +++ b/auth/src/routers/cors.rs @@ -0,0 +1,24 @@ +use axum::http::Method; +use tower_http::{cors, cors::CorsLayer}; + +pub fn get_public_cors() -> CorsLayer { + CorsLayer::new() + .allow_methods([ + Method::GET, + Method::POST, + Method::PATCH, + Method::DELETE, + Method::OPTIONS, + ]) + .allow_origin(cors::Any) +} + +pub fn get_internal_cors() -> CorsLayer { + CorsLayer::new().allow_methods([ + Method::GET, + Method::POST, + Method::PATCH, + Method::DELETE, + Method::OPTIONS, + ]) +} diff --git a/auth/src/routers/mod.rs b/auth/src/routers/mod.rs new file mode 100644 index 0000000..663740b --- /dev/null +++ b/auth/src/routers/mod.rs @@ -0,0 +1,6 @@ +pub mod cors; +pub mod extractor; +pub mod middleware; +pub mod response; +pub mod serializer; +pub mod utils; diff --git a/auth/src/services/actions/mod.rs b/auth/src/services/actions/mod.rs deleted file mode 100644 index 123d11d..0000000 --- a/auth/src/services/actions/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod reset_password; -pub mod verify_email; - -pub use reset_password::*; -pub use verify_email::*; diff --git a/auth/src/services/auth/mod.rs b/auth/src/services/auth/mod.rs deleted file mode 100644 index 3c3cf1b..0000000 --- a/auth/src/services/auth/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod forget_password; -mod login; -mod register; -mod reset_password; -mod verify_email; - -pub use forget_password::*; -pub use login::*; -pub use register::*; -pub use reset_password::*; -pub use verify_email::*; diff --git a/auth/src/services/mod.rs b/auth/src/services/mod.rs deleted file mode 100644 index 189c4f5..0000000 --- a/auth/src/services/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod actions; -pub mod auth; -pub mod users; diff --git a/auth/src/services/users/mod.rs b/auth/src/services/users/mod.rs deleted file mode 100644 index 88448d5..0000000 --- a/auth/src/services/users/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod delete; -mod info; -mod setting; -mod update; - -pub use delete::*; -pub use info::*; -pub use setting::*; -pub use update::*; From 6b119d4cc4ddc57ad62e1edc61e269f15f1eaef4 Mon Sep 17 00:00:00 2001 From: yuanzui-cf Date: Mon, 13 Apr 2026 08:20:03 +0800 Subject: [PATCH 3/5] refactor(auth): Split domain and database infra modules --- auth/src/{models => domain}/mod.rs | 9 --------- auth/src/{models => domain}/permission.rs | 2 +- auth/src/{models => domain}/role.rs | 2 +- auth/src/{models => domain}/role_permissions.rs | 0 auth/src/{models => domain}/user_info.rs | 0 auth/src/{models => domain}/user_role.rs | 0 auth/src/{models => domain}/user_settings.rs | 2 +- auth/src/{models => domain}/users.rs | 6 +++--- auth/src/features/api/v1/auth/forget_password.rs | 2 +- auth/src/features/api/v1/auth/login.rs | 2 +- auth/src/features/api/v1/auth/register.rs | 3 ++- auth/src/features/api/v1/auth/reset_password.rs | 2 +- auth/src/features/api/v1/auth/verify_email.rs | 2 +- auth/src/features/api/v1/users/delete.rs | 2 +- auth/src/features/api/v1/users/info.rs | 4 ++-- auth/src/features/api/v1/users/setting.rs | 3 ++- auth/src/features/api/v1/users/update.rs | 4 ++-- auth/src/{models/common.rs => infra/database/error.rs} | 0 auth/src/{models => infra/database}/init.rs | 7 +++---- .../database}/migration/m20241010_000000_create_table.rs | 0 .../database}/migration/m20241201_000000_add_userinfo.rs | 0 .../migration/m20250709_000000_create_permission_role.rs | 0 .../migration/m20250713_000000_create_user_role.rs | 0 .../migration/m20260311_000000_create_user_settings.rs | 0 .../migration/m20260311_010000_add_audit_columns.rs | 0 auth/src/{models => infra/database}/migration/mod.rs | 0 auth/src/infra/database/mod.rs | 7 +++++++ auth/src/infra/mod.rs | 1 + auth/src/init.rs | 2 +- auth/src/main.rs | 3 ++- auth/src/routers/extractor/login.rs | 2 +- auth/src/routers/middleware/permission.rs | 2 +- 32 files changed, 35 insertions(+), 34 deletions(-) rename auth/src/{models => domain}/mod.rs (60%) rename auth/src/{models => domain}/permission.rs (97%) rename auth/src/{models => domain}/role.rs (97%) rename auth/src/{models => domain}/role_permissions.rs (100%) rename auth/src/{models => domain}/user_info.rs (100%) rename auth/src/{models => domain}/user_role.rs (100%) rename auth/src/{models => domain}/user_settings.rs (97%) rename auth/src/{models => domain}/users.rs (98%) rename auth/src/{models/common.rs => infra/database/error.rs} (100%) rename auth/src/{models => infra/database}/init.rs (99%) rename auth/src/{models => infra/database}/migration/m20241010_000000_create_table.rs (100%) rename auth/src/{models => infra/database}/migration/m20241201_000000_add_userinfo.rs (100%) rename auth/src/{models => infra/database}/migration/m20250709_000000_create_permission_role.rs (100%) rename auth/src/{models => infra/database}/migration/m20250713_000000_create_user_role.rs (100%) rename auth/src/{models => infra/database}/migration/m20260311_000000_create_user_settings.rs (100%) rename auth/src/{models => infra/database}/migration/m20260311_010000_add_audit_columns.rs (100%) rename auth/src/{models => infra/database}/migration/mod.rs (100%) create mode 100644 auth/src/infra/database/mod.rs create mode 100644 auth/src/infra/mod.rs diff --git a/auth/src/models/mod.rs b/auth/src/domain/mod.rs similarity index 60% rename from auth/src/models/mod.rs rename to auth/src/domain/mod.rs index 38c5726..4af8e31 100644 --- a/auth/src/models/mod.rs +++ b/auth/src/domain/mod.rs @@ -1,7 +1,3 @@ -// Migration -pub mod migration; - -// Entity pub mod permission; pub mod role; pub mod role_permissions; @@ -9,8 +5,3 @@ pub mod user_info; pub mod user_role; pub mod user_settings; pub mod users; - -pub mod common; -mod init; - -pub use init::*; diff --git a/auth/src/models/permission.rs b/auth/src/domain/permission.rs similarity index 97% rename from auth/src/models/permission.rs rename to auth/src/domain/permission.rs index 9da6aa7..6d387b8 100644 --- a/auth/src/models/permission.rs +++ b/auth/src/domain/permission.rs @@ -1,7 +1,7 @@ use sea_orm::{JoinType, QuerySelect, entity::prelude::*}; use serde::{Deserialize, Serialize}; -use crate::models::common::ModelError; +use crate::infra::database::ModelError; /// # Permission Model #[derive(Debug, Clone, PartialEq, DeriveEntityModel, Serialize, Deserialize)] diff --git a/auth/src/models/role.rs b/auth/src/domain/role.rs similarity index 97% rename from auth/src/models/role.rs rename to auth/src/domain/role.rs index 84e86b6..aa7a9a5 100644 --- a/auth/src/models/role.rs +++ b/auth/src/domain/role.rs @@ -1,7 +1,7 @@ use sea_orm::{JoinType, QuerySelect, entity::prelude::*}; use serde::{Deserialize, Serialize}; -use crate::models::common::ModelError; +use crate::infra::database::ModelError; /// # Role Model #[derive(Debug, Clone, PartialEq, DeriveEntityModel, Serialize, Deserialize)] diff --git a/auth/src/models/role_permissions.rs b/auth/src/domain/role_permissions.rs similarity index 100% rename from auth/src/models/role_permissions.rs rename to auth/src/domain/role_permissions.rs diff --git a/auth/src/models/user_info.rs b/auth/src/domain/user_info.rs similarity index 100% rename from auth/src/models/user_info.rs rename to auth/src/domain/user_info.rs diff --git a/auth/src/models/user_role.rs b/auth/src/domain/user_role.rs similarity index 100% rename from auth/src/models/user_role.rs rename to auth/src/domain/user_role.rs diff --git a/auth/src/models/user_settings.rs b/auth/src/domain/user_settings.rs similarity index 97% rename from auth/src/models/user_settings.rs rename to auth/src/domain/user_settings.rs index defb10d..00766fc 100644 --- a/auth/src/models/user_settings.rs +++ b/auth/src/domain/user_settings.rs @@ -1,7 +1,7 @@ use sea_orm::{ActiveValue::Set, entity::prelude::*}; use serde::{Deserialize, Serialize}; -use crate::models::common::ModelError::{self, DBError, Empty, ParamsError}; +use crate::infra::database::ModelError::{self, DBError, Empty, ParamsError}; /// # User Settings Model #[derive(Debug, Clone, PartialEq, DeriveEntityModel, Serialize, Deserialize)] diff --git a/auth/src/models/users.rs b/auth/src/domain/users.rs similarity index 98% rename from auth/src/models/users.rs rename to auth/src/domain/users.rs index eedbb93..13baeef 100644 --- a/auth/src/models/users.rs +++ b/auth/src/domain/users.rs @@ -2,9 +2,9 @@ use crypto::password::PasswordManager; use sea_orm::{ActiveValue::Set, IntoActiveModel, JoinType, QuerySelect, entity::prelude::*}; use serde::{Deserialize, Serialize}; -use crate::models::{ - common::ModelError::{self, DBError, Empty, ParamsError}, - permission, role, user_info, user_role, user_settings, +use crate::{ + domain::{permission, role, user_info, user_role, user_settings}, + infra::database::ModelError::{self, DBError, Empty, ParamsError}, }; /// Status of the Account diff --git a/auth/src/features/api/v1/auth/forget_password.rs b/auth/src/features/api/v1/auth/forget_password.rs index bec6923..dea2dcc 100644 --- a/auth/src/features/api/v1/auth/forget_password.rs +++ b/auth/src/features/api/v1/auth/forget_password.rs @@ -6,13 +6,13 @@ use serde::Deserialize; use token::services::PasswordResetTokenService; use crate::{ + domain::users, features::actions::reset_password::ActionsResetPasswordService, internal::{ config::Config, error::{AppError, AppErrorKind}, mail::Mailer, }, - models::users, routers::{ extractor::Json, response::app_error_to_response, diff --git a/auth/src/features/api/v1/auth/login.rs b/auth/src/features/api/v1/auth/login.rs index 876e7fe..14e0450 100644 --- a/auth/src/features/api/v1/auth/login.rs +++ b/auth/src/features/api/v1/auth/login.rs @@ -6,11 +6,11 @@ use serde::{Deserialize, Serialize}; use token::services::SessionService; use crate::{ + domain::users, internal::{ error::{AppError, AppErrorKind}, session::SESSION_TTL_SECONDS, }, - models::users, routers::{ extractor::{GuestAccess, Json}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/auth/register.rs b/auth/src/features/api/v1/auth/register.rs index d5fc0ff..35f9956 100644 --- a/auth/src/features/api/v1/auth/register.rs +++ b/auth/src/features/api/v1/auth/register.rs @@ -11,12 +11,13 @@ use token::services::RegisterTokenService; use validator::Validatable; use crate::{ + domain::users, + infra::database::ModelError, internal::{ config::Config, error::{AppError, AppErrorKind}, mail::Mailer, }, - models::{common::ModelError, users}, routers::{ extractor::{GuestAccess, Json}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/auth/reset_password.rs b/auth/src/features/api/v1/auth/reset_password.rs index da8a27b..027bcc3 100644 --- a/auth/src/features/api/v1/auth/reset_password.rs +++ b/auth/src/features/api/v1/auth/reset_password.rs @@ -7,8 +7,8 @@ use serde::Deserialize; use token::services::{PasswordResetTokenService, SessionService}; use crate::{ + domain::users, internal::error::{AppError, AppErrorKind}, - models::users, routers::{ extractor::{Json, LoginAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/auth/verify_email.rs b/auth/src/features/api/v1/auth/verify_email.rs index 442c25d..2d8b77a 100644 --- a/auth/src/features/api/v1/auth/verify_email.rs +++ b/auth/src/features/api/v1/auth/verify_email.rs @@ -5,8 +5,8 @@ use serde::Deserialize; use token::{TokenStore, services::RegisterTokenService}; use crate::{ + domain::users, internal::error::{AppError, AppErrorKind}, - models::users, routers::{ extractor::Json, response::app_error_to_response, diff --git a/auth/src/features/api/v1/users/delete.rs b/auth/src/features/api/v1/users/delete.rs index 2932dac..89b4d53 100644 --- a/auth/src/features/api/v1/users/delete.rs +++ b/auth/src/features/api/v1/users/delete.rs @@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize}; use token::services::SessionService; use crate::{ + domain::{permission, role, users}, internal::error::{AppError, AppErrorKind}, - models::{permission, role, users}, routers::{ extractor::{Json, LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/users/info.rs b/auth/src/features/api/v1/users/info.rs index 4aa44d8..d24b805 100644 --- a/auth/src/features/api/v1/users/info.rs +++ b/auth/src/features/api/v1/users/info.rs @@ -3,11 +3,11 @@ use sea_orm::DatabaseConnection; use serde::{Deserialize, Serialize}; use crate::{ - internal::error::{AppError, AppErrorKind}, - models::{ + domain::{ user_info::{self, Gender}, user_settings, users, }, + internal::error::{AppError, AppErrorKind}, routers::{ extractor::{LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/users/setting.rs b/auth/src/features/api/v1/users/setting.rs index a4fc8a5..e4f3678 100644 --- a/auth/src/features/api/v1/users/setting.rs +++ b/auth/src/features/api/v1/users/setting.rs @@ -3,8 +3,9 @@ use sea_orm::DatabaseConnection; use serde::{Deserialize, Serialize}; use crate::{ + domain::user_settings, + infra::database::ModelError, internal::error::{AppError, AppErrorKind}, - models::{common::ModelError, user_settings}, routers::{ extractor::{LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/users/update.rs b/auth/src/features/api/v1/users/update.rs index c2d1515..0cb97c6 100644 --- a/auth/src/features/api/v1/users/update.rs +++ b/auth/src/features/api/v1/users/update.rs @@ -7,12 +7,12 @@ use serde::{Deserialize, Serialize}; use validator::Validatable; use crate::{ - internal::error::{AppError, AppErrorKind}, - models::{ + domain::{ role, user_info::{self, Gender}, user_settings, users, }, + internal::error::{AppError, AppErrorKind}, routers::{ extractor::{Json, LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/models/common.rs b/auth/src/infra/database/error.rs similarity index 100% rename from auth/src/models/common.rs rename to auth/src/infra/database/error.rs diff --git a/auth/src/models/init.rs b/auth/src/infra/database/init.rs similarity index 99% rename from auth/src/models/init.rs rename to auth/src/infra/database/init.rs index fcc773f..f07eb00 100644 --- a/auth/src/models/init.rs +++ b/auth/src/infra/database/init.rs @@ -6,15 +6,14 @@ use tracing::{info, log}; use uuid::Uuid; use crate::{ - internal::{config::Database as DatabaseType, utils}, - models::{ - common::ModelError, - migration::Migrator, + domain::{ permission::{ActiveModel as PermissionActiveModel, Entity as Permission}, role::{ActiveModel as RoleActiveModel, Entity as Role}, role_permissions::{ActiveModel as RolePermissionActiveModel, Entity as RolePermission}, users::{self, AccountStatus}, }, + infra::database::{ModelError, migration::Migrator}, + internal::{config::Database as DatabaseType, utils}, }; pub async fn init(sql: &DatabaseType) -> Result { diff --git a/auth/src/models/migration/m20241010_000000_create_table.rs b/auth/src/infra/database/migration/m20241010_000000_create_table.rs similarity index 100% rename from auth/src/models/migration/m20241010_000000_create_table.rs rename to auth/src/infra/database/migration/m20241010_000000_create_table.rs diff --git a/auth/src/models/migration/m20241201_000000_add_userinfo.rs b/auth/src/infra/database/migration/m20241201_000000_add_userinfo.rs similarity index 100% rename from auth/src/models/migration/m20241201_000000_add_userinfo.rs rename to auth/src/infra/database/migration/m20241201_000000_add_userinfo.rs diff --git a/auth/src/models/migration/m20250709_000000_create_permission_role.rs b/auth/src/infra/database/migration/m20250709_000000_create_permission_role.rs similarity index 100% rename from auth/src/models/migration/m20250709_000000_create_permission_role.rs rename to auth/src/infra/database/migration/m20250709_000000_create_permission_role.rs diff --git a/auth/src/models/migration/m20250713_000000_create_user_role.rs b/auth/src/infra/database/migration/m20250713_000000_create_user_role.rs similarity index 100% rename from auth/src/models/migration/m20250713_000000_create_user_role.rs rename to auth/src/infra/database/migration/m20250713_000000_create_user_role.rs diff --git a/auth/src/models/migration/m20260311_000000_create_user_settings.rs b/auth/src/infra/database/migration/m20260311_000000_create_user_settings.rs similarity index 100% rename from auth/src/models/migration/m20260311_000000_create_user_settings.rs rename to auth/src/infra/database/migration/m20260311_000000_create_user_settings.rs diff --git a/auth/src/models/migration/m20260311_010000_add_audit_columns.rs b/auth/src/infra/database/migration/m20260311_010000_add_audit_columns.rs similarity index 100% rename from auth/src/models/migration/m20260311_010000_add_audit_columns.rs rename to auth/src/infra/database/migration/m20260311_010000_add_audit_columns.rs diff --git a/auth/src/models/migration/mod.rs b/auth/src/infra/database/migration/mod.rs similarity index 100% rename from auth/src/models/migration/mod.rs rename to auth/src/infra/database/migration/mod.rs diff --git a/auth/src/infra/database/mod.rs b/auth/src/infra/database/mod.rs new file mode 100644 index 0000000..bdcc8d8 --- /dev/null +++ b/auth/src/infra/database/mod.rs @@ -0,0 +1,7 @@ +pub mod migration; + +mod error; +mod init; + +pub use error::*; +pub use init::*; diff --git a/auth/src/infra/mod.rs b/auth/src/infra/mod.rs new file mode 100644 index 0000000..8fd0a6b --- /dev/null +++ b/auth/src/infra/mod.rs @@ -0,0 +1 @@ +pub mod database; diff --git a/auth/src/init.rs b/auth/src/init.rs index 198978d..5f5ccb3 100644 --- a/auth/src/init.rs +++ b/auth/src/init.rs @@ -5,7 +5,7 @@ use tracing_subscriber::{ }; use crate::internal::{config::Redis, log::layer::LogLayer}; -pub use crate::{internal::mail::init as mail, models::init as db}; +pub use crate::{infra::database::init as db, internal::mail::init as mail}; /// Initialize the logger pub fn logger() { diff --git a/auth/src/main.rs b/auth/src/main.rs index 8f98640..1d9a001 100644 --- a/auth/src/main.rs +++ b/auth/src/main.rs @@ -3,9 +3,10 @@ mod init; +mod domain; mod features; +mod infra; mod internal; -mod models; mod routers; mod state; diff --git a/auth/src/routers/extractor/login.rs b/auth/src/routers/extractor/login.rs index d28813e..7a33aba 100644 --- a/auth/src/routers/extractor/login.rs +++ b/auth/src/routers/extractor/login.rs @@ -3,7 +3,7 @@ use axum_extra::extract::CookieJar; use token::services::{SessionLookup, SessionService}; use crate::{ - models::{role, user_info, user_settings, users}, + domain::{role, user_info, user_settings, users}, routers::serializer::ResponseCode, state::AppState, }; diff --git a/auth/src/routers/middleware/permission.rs b/auth/src/routers/middleware/permission.rs index 6a685c2..4901b8f 100644 --- a/auth/src/routers/middleware/permission.rs +++ b/auth/src/routers/middleware/permission.rs @@ -9,7 +9,7 @@ use axum_extra::extract::cookie::Cookie; use token::services::{SessionLookup, SessionService}; use tower::{Layer, Service}; -use crate::{models::permission, routers::serializer::ResponseCode, state::APP_STATE}; +use crate::{domain::permission, routers::serializer::ResponseCode, state::APP_STATE}; #[derive(Clone)] enum PermType { From 640ad422a240ea0338a0f23f90731a8899ade333 Mon Sep 17 00:00:00 2001 From: yuanzui-cf Date: Mon, 13 Apr 2026 08:41:26 +0800 Subject: [PATCH 4/5] refactor(infra): Move internal modules to infra namespace --- auth/src/features/actions/mod.rs | 2 +- auth/src/features/actions/reset_password.rs | 2 +- auth/src/features/actions/verify_email.rs | 2 +- auth/src/features/api/mod.rs | 2 +- .../src/features/api/v1/auth/forget_password.rs | 4 ++-- auth/src/features/api/v1/auth/login.rs | 2 +- auth/src/features/api/v1/auth/logout.rs | 2 +- auth/src/features/api/v1/auth/mod.rs | 2 +- auth/src/features/api/v1/auth/register.rs | 6 +++--- auth/src/features/api/v1/auth/reset_password.rs | 2 +- auth/src/features/api/v1/auth/verify_email.rs | 2 +- auth/src/features/api/v1/mod.rs | 2 +- auth/src/features/api/v1/users/delete.rs | 2 +- auth/src/features/api/v1/users/info.rs | 2 +- auth/src/features/api/v1/users/setting.rs | 6 ++++-- auth/src/features/api/v1/users/update.rs | 2 +- auth/src/features/mod.rs | 2 +- auth/src/{internal => infra}/config/common.rs | 0 auth/src/{internal => infra}/config/mod.rs | 0 .../config/structure/common.rs | 2 +- .../config/structure/database.rs | 0 .../config/structure/mail.rs | 0 .../{internal => infra}/config/structure/mod.rs | 0 .../config/structure/redis.rs | 0 .../config/structure/secure.rs | 2 +- .../config/structure/site.rs | 0 auth/src/infra/database/error.rs | 2 +- auth/src/infra/database/init.rs | 7 +++++-- auth/src/{internal => infra}/error.rs | 0 .../src/{internal/log => infra/logger}/layer.rs | 0 auth/src/{internal/log => infra/logger}/mod.rs | 0 .../{internal/log => infra/logger}/visitor.rs | 0 auth/src/{internal/mail.rs => infra/mailer.rs} | 2 +- auth/src/infra/mod.rs | 6 ++++++ auth/src/{internal => infra}/session.rs | 0 auth/src/{internal => infra}/utils/mod.rs | 0 auth/src/{internal => infra}/utils/rand.rs | 0 auth/src/init.rs | 4 ++-- auth/src/internal/mod.rs | 17 ----------------- auth/src/main.rs | 5 ++--- auth/src/routers/response.rs | 2 +- auth/src/routers/utils/cookie.rs | 2 +- auth/src/state.rs | 2 +- 43 files changed, 45 insertions(+), 52 deletions(-) rename auth/src/{internal => infra}/config/common.rs (100%) rename auth/src/{internal => infra}/config/mod.rs (100%) rename auth/src/{internal => infra}/config/structure/common.rs (93%) rename auth/src/{internal => infra}/config/structure/database.rs (100%) rename auth/src/{internal => infra}/config/structure/mail.rs (100%) rename auth/src/{internal => infra}/config/structure/mod.rs (100%) rename auth/src/{internal => infra}/config/structure/redis.rs (100%) rename auth/src/{internal => infra}/config/structure/secure.rs (94%) rename auth/src/{internal => infra}/config/structure/site.rs (100%) rename auth/src/{internal => infra}/error.rs (100%) rename auth/src/{internal/log => infra/logger}/layer.rs (100%) rename auth/src/{internal/log => infra/logger}/mod.rs (100%) rename auth/src/{internal/log => infra/logger}/visitor.rs (100%) rename auth/src/{internal/mail.rs => infra/mailer.rs} (98%) rename auth/src/{internal => infra}/session.rs (100%) rename auth/src/{internal => infra}/utils/mod.rs (100%) rename auth/src/{internal => infra}/utils/rand.rs (100%) delete mode 100644 auth/src/internal/mod.rs diff --git a/auth/src/features/actions/mod.rs b/auth/src/features/actions/mod.rs index 6477162..b19271b 100644 --- a/auth/src/features/actions/mod.rs +++ b/auth/src/features/actions/mod.rs @@ -1,6 +1,6 @@ use axum::{Router, routing::get}; -use crate::{internal::config::Config, routers::cors, state::AppState}; +use crate::{infra::config::Config, routers::cors, state::AppState}; pub mod reset_password; pub mod verify_email; diff --git a/auth/src/features/actions/reset_password.rs b/auth/src/features/actions/reset_password.rs index 4995378..2ab57d9 100644 --- a/auth/src/features/actions/reset_password.rs +++ b/auth/src/features/actions/reset_password.rs @@ -10,7 +10,7 @@ use serde::Deserialize; use token::{TokenStore, services::PasswordResetTokenService}; use crate::{ - internal::{ + infra::{ config::Config, error::{AppError, AppErrorKind}, }, diff --git a/auth/src/features/actions/verify_email.rs b/auth/src/features/actions/verify_email.rs index 50e1919..40ac835 100644 --- a/auth/src/features/actions/verify_email.rs +++ b/auth/src/features/actions/verify_email.rs @@ -9,7 +9,7 @@ use minijinja::{AutoEscape, Environment, context}; use serde::Deserialize; use crate::{ - internal::{ + infra::{ config::Config, error::{AppError, AppErrorKind}, }, diff --git a/auth/src/features/api/mod.rs b/auth/src/features/api/mod.rs index e517f7e..fa84959 100644 --- a/auth/src/features/api/mod.rs +++ b/auth/src/features/api/mod.rs @@ -1,6 +1,6 @@ use axum::Router; -use crate::{internal::config::Config, state::AppState}; +use crate::{infra::config::Config, state::AppState}; pub mod v1; diff --git a/auth/src/features/api/v1/auth/forget_password.rs b/auth/src/features/api/v1/auth/forget_password.rs index dea2dcc..3f16c0b 100644 --- a/auth/src/features/api/v1/auth/forget_password.rs +++ b/auth/src/features/api/v1/auth/forget_password.rs @@ -8,10 +8,10 @@ use token::services::PasswordResetTokenService; use crate::{ domain::users, features::actions::reset_password::ActionsResetPasswordService, - internal::{ + infra::{ config::Config, error::{AppError, AppErrorKind}, - mail::Mailer, + mailer::Mailer, }, routers::{ extractor::Json, diff --git a/auth/src/features/api/v1/auth/login.rs b/auth/src/features/api/v1/auth/login.rs index 14e0450..a79b259 100644 --- a/auth/src/features/api/v1/auth/login.rs +++ b/auth/src/features/api/v1/auth/login.rs @@ -7,7 +7,7 @@ use token::services::SessionService; use crate::{ domain::users, - internal::{ + infra::{ error::{AppError, AppErrorKind}, session::SESSION_TTL_SECONDS, }, diff --git a/auth/src/features/api/v1/auth/logout.rs b/auth/src/features/api/v1/auth/logout.rs index 91e45cc..096a8e0 100644 --- a/auth/src/features/api/v1/auth/logout.rs +++ b/auth/src/features/api/v1/auth/logout.rs @@ -3,7 +3,7 @@ use axum_extra::extract::CookieJar; use token::services::SessionService; use crate::{ - internal::error::{AppError, AppErrorKind}, + infra::error::{AppError, AppErrorKind}, routers::{ extractor::LoginAccess, response::app_error_to_response, diff --git a/auth/src/features/api/v1/auth/mod.rs b/auth/src/features/api/v1/auth/mod.rs index 478d324..70633de 100644 --- a/auth/src/features/api/v1/auth/mod.rs +++ b/auth/src/features/api/v1/auth/mod.rs @@ -4,7 +4,7 @@ use axum::{ }; use crate::{ - internal::config::Config, + infra::config::Config, routers::{cors, middleware::permission::PermissionAccess}, state::AppState, }; diff --git a/auth/src/features/api/v1/auth/register.rs b/auth/src/features/api/v1/auth/register.rs index 35f9956..41f1f0d 100644 --- a/auth/src/features/api/v1/auth/register.rs +++ b/auth/src/features/api/v1/auth/register.rs @@ -12,11 +12,11 @@ use validator::Validatable; use crate::{ domain::users, - infra::database::ModelError, - internal::{ + infra::{ config::Config, + database::ModelError, error::{AppError, AppErrorKind}, - mail::Mailer, + mailer::Mailer, }, routers::{ extractor::{GuestAccess, Json}, diff --git a/auth/src/features/api/v1/auth/reset_password.rs b/auth/src/features/api/v1/auth/reset_password.rs index 027bcc3..1b8c403 100644 --- a/auth/src/features/api/v1/auth/reset_password.rs +++ b/auth/src/features/api/v1/auth/reset_password.rs @@ -8,7 +8,7 @@ use token::services::{PasswordResetTokenService, SessionService}; use crate::{ domain::users, - internal::error::{AppError, AppErrorKind}, + infra::error::{AppError, AppErrorKind}, routers::{ extractor::{Json, LoginAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/auth/verify_email.rs b/auth/src/features/api/v1/auth/verify_email.rs index 2d8b77a..1e7544f 100644 --- a/auth/src/features/api/v1/auth/verify_email.rs +++ b/auth/src/features/api/v1/auth/verify_email.rs @@ -6,7 +6,7 @@ use token::{TokenStore, services::RegisterTokenService}; use crate::{ domain::users, - internal::error::{AppError, AppErrorKind}, + infra::error::{AppError, AppErrorKind}, routers::{ extractor::Json, response::app_error_to_response, diff --git a/auth/src/features/api/v1/mod.rs b/auth/src/features/api/v1/mod.rs index 753a6bd..094ef0e 100644 --- a/auth/src/features/api/v1/mod.rs +++ b/auth/src/features/api/v1/mod.rs @@ -1,6 +1,6 @@ use axum::Router; -use crate::{internal::config::Config, state::AppState}; +use crate::{infra::config::Config, state::AppState}; pub mod auth; pub mod common; diff --git a/auth/src/features/api/v1/users/delete.rs b/auth/src/features/api/v1/users/delete.rs index 89b4d53..3af7213 100644 --- a/auth/src/features/api/v1/users/delete.rs +++ b/auth/src/features/api/v1/users/delete.rs @@ -6,7 +6,7 @@ use token::services::SessionService; use crate::{ domain::{permission, role, users}, - internal::error::{AppError, AppErrorKind}, + infra::error::{AppError, AppErrorKind}, routers::{ extractor::{Json, LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/users/info.rs b/auth/src/features/api/v1/users/info.rs index d24b805..875ad53 100644 --- a/auth/src/features/api/v1/users/info.rs +++ b/auth/src/features/api/v1/users/info.rs @@ -7,7 +7,7 @@ use crate::{ user_info::{self, Gender}, user_settings, users, }, - internal::error::{AppError, AppErrorKind}, + infra::error::{AppError, AppErrorKind}, routers::{ extractor::{LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/users/setting.rs b/auth/src/features/api/v1/users/setting.rs index e4f3678..431edad 100644 --- a/auth/src/features/api/v1/users/setting.rs +++ b/auth/src/features/api/v1/users/setting.rs @@ -4,8 +4,10 @@ use serde::{Deserialize, Serialize}; use crate::{ domain::user_settings, - infra::database::ModelError, - internal::error::{AppError, AppErrorKind}, + infra::{ + database::ModelError, + error::{AppError, AppErrorKind}, + }, routers::{ extractor::{LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/features/api/v1/users/update.rs b/auth/src/features/api/v1/users/update.rs index 0cb97c6..f95ae78 100644 --- a/auth/src/features/api/v1/users/update.rs +++ b/auth/src/features/api/v1/users/update.rs @@ -12,7 +12,7 @@ use crate::{ user_info::{self, Gender}, user_settings, users, }, - internal::error::{AppError, AppErrorKind}, + infra::error::{AppError, AppErrorKind}, routers::{ extractor::{Json, LoginAccess, OperatorAccess}, response::app_error_to_response, diff --git a/auth/src/features/mod.rs b/auth/src/features/mod.rs index bac9438..02673e9 100644 --- a/auth/src/features/mod.rs +++ b/auth/src/features/mod.rs @@ -1,6 +1,6 @@ use axum::Router; -use crate::{internal::config::Config, state::AppState}; +use crate::{infra::config::Config, state::AppState}; pub mod actions; pub mod api; diff --git a/auth/src/internal/config/common.rs b/auth/src/infra/config/common.rs similarity index 100% rename from auth/src/internal/config/common.rs rename to auth/src/infra/config/common.rs diff --git a/auth/src/internal/config/mod.rs b/auth/src/infra/config/mod.rs similarity index 100% rename from auth/src/internal/config/mod.rs rename to auth/src/infra/config/mod.rs diff --git a/auth/src/internal/config/structure/common.rs b/auth/src/infra/config/structure/common.rs similarity index 93% rename from auth/src/internal/config/structure/common.rs rename to auth/src/infra/config/structure/common.rs index 7075bd1..3baca64 100644 --- a/auth/src/internal/config/structure/common.rs +++ b/auth/src/infra/config/structure/common.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::internal::config::{Database, Mail, Redis, Secure, Site, common::CONFIG_VERSION}; +use crate::infra::config::{Database, Mail, Redis, Secure, Site, common::CONFIG_VERSION}; /// Config of madoka_auth. #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/auth/src/internal/config/structure/database.rs b/auth/src/infra/config/structure/database.rs similarity index 100% rename from auth/src/internal/config/structure/database.rs rename to auth/src/infra/config/structure/database.rs diff --git a/auth/src/internal/config/structure/mail.rs b/auth/src/infra/config/structure/mail.rs similarity index 100% rename from auth/src/internal/config/structure/mail.rs rename to auth/src/infra/config/structure/mail.rs diff --git a/auth/src/internal/config/structure/mod.rs b/auth/src/infra/config/structure/mod.rs similarity index 100% rename from auth/src/internal/config/structure/mod.rs rename to auth/src/infra/config/structure/mod.rs diff --git a/auth/src/internal/config/structure/redis.rs b/auth/src/infra/config/structure/redis.rs similarity index 100% rename from auth/src/internal/config/structure/redis.rs rename to auth/src/infra/config/structure/redis.rs diff --git a/auth/src/internal/config/structure/secure.rs b/auth/src/infra/config/structure/secure.rs similarity index 94% rename from auth/src/internal/config/structure/secure.rs rename to auth/src/infra/config/structure/secure.rs index 1271846..bc74453 100644 --- a/auth/src/internal/config/structure/secure.rs +++ b/auth/src/infra/config/structure/secure.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::internal::utils; +use crate::infra::utils; fn default_jwt_secret() -> String { utils::rand::string(16) diff --git a/auth/src/internal/config/structure/site.rs b/auth/src/infra/config/structure/site.rs similarity index 100% rename from auth/src/internal/config/structure/site.rs rename to auth/src/infra/config/structure/site.rs diff --git a/auth/src/infra/database/error.rs b/auth/src/infra/database/error.rs index 0d16e07..33d60b2 100644 --- a/auth/src/infra/database/error.rs +++ b/auth/src/infra/database/error.rs @@ -2,7 +2,7 @@ use crypto::password::PasswordError; use sea_orm::DbErr; use thiserror::Error; -use crate::internal::error::{AppError, AppErrorKind}; +use crate::infra::error::{AppError, AppErrorKind}; #[derive(Debug, Error)] pub enum ModelError { diff --git a/auth/src/infra/database/init.rs b/auth/src/infra/database/init.rs index f07eb00..dad990a 100644 --- a/auth/src/infra/database/init.rs +++ b/auth/src/infra/database/init.rs @@ -12,8 +12,11 @@ use crate::{ role_permissions::{ActiveModel as RolePermissionActiveModel, Entity as RolePermission}, users::{self, AccountStatus}, }, - infra::database::{ModelError, migration::Migrator}, - internal::{config::Database as DatabaseType, utils}, + infra::{ + config::Database as DatabaseType, + database::{ModelError, migration::Migrator}, + utils, + }, }; pub async fn init(sql: &DatabaseType) -> Result { diff --git a/auth/src/internal/error.rs b/auth/src/infra/error.rs similarity index 100% rename from auth/src/internal/error.rs rename to auth/src/infra/error.rs diff --git a/auth/src/internal/log/layer.rs b/auth/src/infra/logger/layer.rs similarity index 100% rename from auth/src/internal/log/layer.rs rename to auth/src/infra/logger/layer.rs diff --git a/auth/src/internal/log/mod.rs b/auth/src/infra/logger/mod.rs similarity index 100% rename from auth/src/internal/log/mod.rs rename to auth/src/infra/logger/mod.rs diff --git a/auth/src/internal/log/visitor.rs b/auth/src/infra/logger/visitor.rs similarity index 100% rename from auth/src/internal/log/visitor.rs rename to auth/src/infra/logger/visitor.rs diff --git a/auth/src/internal/mail.rs b/auth/src/infra/mailer.rs similarity index 98% rename from auth/src/internal/mail.rs rename to auth/src/infra/mailer.rs index fd8388c..8f913fe 100644 --- a/auth/src/internal/mail.rs +++ b/auth/src/infra/mailer.rs @@ -9,7 +9,7 @@ use lettre::{ }; use minijinja::{AutoEscape, Environment, Value}; -use crate::internal::config::{Mail, MailSecure}; +use crate::infra::config::{Mail, MailSecure}; pub fn init(config: &Mail) -> Result { Mailer::new(config) diff --git a/auth/src/infra/mod.rs b/auth/src/infra/mod.rs index 8fd0a6b..273afd0 100644 --- a/auth/src/infra/mod.rs +++ b/auth/src/infra/mod.rs @@ -1 +1,7 @@ +pub mod config; pub mod database; +pub mod error; +pub mod logger; +pub mod mailer; +pub mod session; +pub mod utils; diff --git a/auth/src/internal/session.rs b/auth/src/infra/session.rs similarity index 100% rename from auth/src/internal/session.rs rename to auth/src/infra/session.rs diff --git a/auth/src/internal/utils/mod.rs b/auth/src/infra/utils/mod.rs similarity index 100% rename from auth/src/internal/utils/mod.rs rename to auth/src/infra/utils/mod.rs diff --git a/auth/src/internal/utils/rand.rs b/auth/src/infra/utils/rand.rs similarity index 100% rename from auth/src/internal/utils/rand.rs rename to auth/src/infra/utils/rand.rs diff --git a/auth/src/init.rs b/auth/src/init.rs index 5f5ccb3..f1386f2 100644 --- a/auth/src/init.rs +++ b/auth/src/init.rs @@ -4,8 +4,8 @@ use tracing_subscriber::{ Layer, filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt, }; -use crate::internal::{config::Redis, log::layer::LogLayer}; -pub use crate::{infra::database::init as db, internal::mail::init as mail}; +use crate::infra::{config::Redis, logger::layer::LogLayer}; +pub use crate::infra::{database::init as db, mailer::init as mailer}; /// Initialize the logger pub fn logger() { diff --git a/auth/src/internal/mod.rs b/auth/src/internal/mod.rs deleted file mode 100644 index 22a158d..0000000 --- a/auth/src/internal/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Configuration module -pub mod config; - -// Logging module -pub mod log; - -// Error module -pub mod error; - -// Session module -pub mod session; - -// Utility module -pub mod utils; - -// Mail module -pub mod mail; diff --git a/auth/src/main.rs b/auth/src/main.rs index 1d9a001..94d6866 100644 --- a/auth/src/main.rs +++ b/auth/src/main.rs @@ -6,7 +6,6 @@ mod init; mod domain; mod features; mod infra; -mod internal; mod routers; mod state; @@ -17,7 +16,7 @@ use colored::Colorize; use tokio::{net::TcpListener, signal, sync::oneshot}; use tracing::{error, info}; -use crate::internal::config::Config; +use crate::infra::config::Config; async fn shutdown_signal() { let ctrl_c = async { @@ -71,7 +70,7 @@ async fn main() -> anyhow::Result<()> { info!("Redis initialized."); let mail = if let Some(mail) = &config.mail { - Some(Arc::new(init::mail(mail)?)) + Some(Arc::new(init::mailer(mail)?)) } else { None }; diff --git a/auth/src/routers/response.rs b/auth/src/routers/response.rs index 67b2261..d607795 100644 --- a/auth/src/routers/response.rs +++ b/auth/src/routers/response.rs @@ -1,5 +1,5 @@ use crate::{ - internal::error::{AppError, AppErrorKind}, + infra::error::{AppError, AppErrorKind}, routers::serializer::{Response, ResponseCode}, }; diff --git a/auth/src/routers/utils/cookie.rs b/auth/src/routers/utils/cookie.rs index f47104c..22fc71d 100644 --- a/auth/src/routers/utils/cookie.rs +++ b/auth/src/routers/utils/cookie.rs @@ -3,7 +3,7 @@ use axum_extra::extract::{ cookie::{Cookie, SameSite}, }; -use crate::internal::session::SESSION_TTL_SECONDS; +use crate::infra::session::SESSION_TTL_SECONDS; pub trait CookieJarExt { fn remove_session_cookie(self) -> Self; diff --git a/auth/src/state.rs b/auth/src/state.rs index d13605b..3ac86f5 100644 --- a/auth/src/state.rs +++ b/auth/src/state.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use sea_orm::DatabaseConnection; use tokio::sync::OnceCell; -use crate::internal::{config, mail::Mailer}; +use crate::infra::{config, mailer::Mailer}; pub static APP_STATE: OnceCell = OnceCell::const_new(); From df8ab6ebef78c0606cedaca5744240cb17de2ed1 Mon Sep 17 00:00:00 2001 From: yuanzui-cf Date: Mon, 13 Apr 2026 08:49:43 +0800 Subject: [PATCH 5/5] refactor(infra): Move router HTTP modules into infra/http --- auth/src/features/actions/mod.rs | 5 ++++- auth/src/features/api/v1/auth/forget_password.rs | 10 +++++----- auth/src/features/api/v1/auth/login.rs | 12 ++++++------ auth/src/features/api/v1/auth/logout.rs | 14 ++++++++------ auth/src/features/api/v1/auth/mod.rs | 6 ++++-- auth/src/features/api/v1/auth/register.rs | 10 +++++----- auth/src/features/api/v1/auth/reset_password.rs | 14 ++++++++------ auth/src/features/api/v1/auth/verify_email.rs | 12 +++++++----- auth/src/features/api/v1/common/mod.rs | 2 +- auth/src/features/api/v1/common/not_found.rs | 2 +- auth/src/features/api/v1/common/ping.rs | 2 +- auth/src/features/api/v1/users/delete.rs | 14 ++++++++------ auth/src/features/api/v1/users/info.rs | 12 +++++++----- auth/src/features/api/v1/users/mod.rs | 2 +- auth/src/features/api/v1/users/setting.rs | 10 +++++----- auth/src/features/api/v1/users/update.rs | 12 +++++++----- auth/src/features/assets.rs | 2 +- auth/src/{routers => infra/http}/cors.rs | 0 .../src/{routers => infra/http}/extractor/guest.rs | 2 +- auth/src/{routers => infra/http}/extractor/json.rs | 2 +- .../src/{routers => infra/http}/extractor/login.rs | 2 +- auth/src/{routers => infra/http}/extractor/mod.rs | 0 .../{routers => infra/http}/extractor/operator.rs | 2 +- auth/src/{routers => infra/http}/middleware/mod.rs | 0 .../http}/middleware/permission.rs | 2 +- auth/src/{routers => infra/http}/mod.rs | 0 auth/src/{routers => infra/http}/response.rs | 6 +++--- .../{routers => infra/http}/serializer/common.rs | 0 .../{routers => infra/http}/serializer/error.rs | 0 auth/src/{routers => infra/http}/serializer/mod.rs | 0 .../{routers => infra/http}/utils/content_type.rs | 0 auth/src/{routers => infra/http}/utils/cookie.rs | 0 auth/src/{routers => infra/http}/utils/mod.rs | 0 auth/src/infra/mod.rs | 1 + auth/src/main.rs | 1 - 35 files changed, 88 insertions(+), 71 deletions(-) rename auth/src/{routers => infra/http}/cors.rs (100%) rename auth/src/{routers => infra/http}/extractor/guest.rs (94%) rename auth/src/{routers => infra/http}/extractor/json.rs (93%) rename auth/src/{routers => infra/http}/extractor/login.rs (97%) rename auth/src/{routers => infra/http}/extractor/mod.rs (100%) rename auth/src/{routers => infra/http}/extractor/operator.rs (90%) rename auth/src/{routers => infra/http}/middleware/mod.rs (100%) rename auth/src/{routers => infra/http}/middleware/permission.rs (98%) rename auth/src/{routers => infra/http}/mod.rs (100%) rename auth/src/{routers => infra/http}/response.rs (95%) rename auth/src/{routers => infra/http}/serializer/common.rs (100%) rename auth/src/{routers => infra/http}/serializer/error.rs (100%) rename auth/src/{routers => infra/http}/serializer/mod.rs (100%) rename auth/src/{routers => infra/http}/utils/content_type.rs (100%) rename auth/src/{routers => infra/http}/utils/cookie.rs (100%) rename auth/src/{routers => infra/http}/utils/mod.rs (100%) diff --git a/auth/src/features/actions/mod.rs b/auth/src/features/actions/mod.rs index b19271b..563122c 100644 --- a/auth/src/features/actions/mod.rs +++ b/auth/src/features/actions/mod.rs @@ -1,6 +1,9 @@ use axum::{Router, routing::get}; -use crate::{infra::config::Config, routers::cors, state::AppState}; +use crate::{ + infra::{config::Config, http::cors}, + state::AppState, +}; pub mod reset_password; pub mod verify_email; diff --git a/auth/src/features/api/v1/auth/forget_password.rs b/auth/src/features/api/v1/auth/forget_password.rs index 3f16c0b..0650102 100644 --- a/auth/src/features/api/v1/auth/forget_password.rs +++ b/auth/src/features/api/v1/auth/forget_password.rs @@ -11,13 +11,13 @@ use crate::{ infra::{ config::Config, error::{AppError, AppErrorKind}, + http::{ + extractor::Json, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, mailer::Mailer, }, - routers::{ - extractor::Json, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - }, state::AppState, }; diff --git a/auth/src/features/api/v1/auth/login.rs b/auth/src/features/api/v1/auth/login.rs index a79b259..1d9176f 100644 --- a/auth/src/features/api/v1/auth/login.rs +++ b/auth/src/features/api/v1/auth/login.rs @@ -9,14 +9,14 @@ use crate::{ domain::users, infra::{ error::{AppError, AppErrorKind}, + http::{ + extractor::{GuestAccess, Json}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie, + }, session::SESSION_TTL_SECONDS, }, - routers::{ - extractor::{GuestAccess, Json}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - utils::cookie, - }, state::AppState, }; diff --git a/auth/src/features/api/v1/auth/logout.rs b/auth/src/features/api/v1/auth/logout.rs index 096a8e0..919d920 100644 --- a/auth/src/features/api/v1/auth/logout.rs +++ b/auth/src/features/api/v1/auth/logout.rs @@ -3,12 +3,14 @@ use axum_extra::extract::CookieJar; use token::services::SessionService; use crate::{ - infra::error::{AppError, AppErrorKind}, - routers::{ - extractor::LoginAccess, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - utils::cookie::CookieJarExt, + infra::{ + error::{AppError, AppErrorKind}, + http::{ + extractor::LoginAccess, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie::CookieJarExt, + }, }, state::AppState, }; diff --git a/auth/src/features/api/v1/auth/mod.rs b/auth/src/features/api/v1/auth/mod.rs index 70633de..ffb8615 100644 --- a/auth/src/features/api/v1/auth/mod.rs +++ b/auth/src/features/api/v1/auth/mod.rs @@ -4,8 +4,10 @@ use axum::{ }; use crate::{ - infra::config::Config, - routers::{cors, middleware::permission::PermissionAccess}, + infra::{ + config::Config, + http::{cors, middleware::permission::PermissionAccess}, + }, state::AppState, }; diff --git a/auth/src/features/api/v1/auth/register.rs b/auth/src/features/api/v1/auth/register.rs index 41f1f0d..b861299 100644 --- a/auth/src/features/api/v1/auth/register.rs +++ b/auth/src/features/api/v1/auth/register.rs @@ -16,13 +16,13 @@ use crate::{ config::Config, database::ModelError, error::{AppError, AppErrorKind}, + http::{ + extractor::{GuestAccess, Json}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, mailer::Mailer, }, - routers::{ - extractor::{GuestAccess, Json}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - }, state::AppState, }; diff --git a/auth/src/features/api/v1/auth/reset_password.rs b/auth/src/features/api/v1/auth/reset_password.rs index 1b8c403..9ecdb6d 100644 --- a/auth/src/features/api/v1/auth/reset_password.rs +++ b/auth/src/features/api/v1/auth/reset_password.rs @@ -8,12 +8,14 @@ use token::services::{PasswordResetTokenService, SessionService}; use crate::{ domain::users, - infra::error::{AppError, AppErrorKind}, - routers::{ - extractor::{Json, LoginAccess}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - utils::cookie::CookieJarExt, + infra::{ + error::{AppError, AppErrorKind}, + http::{ + extractor::{Json, LoginAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie::CookieJarExt, + }, }, state::AppState, }; diff --git a/auth/src/features/api/v1/auth/verify_email.rs b/auth/src/features/api/v1/auth/verify_email.rs index 1e7544f..5bb4e62 100644 --- a/auth/src/features/api/v1/auth/verify_email.rs +++ b/auth/src/features/api/v1/auth/verify_email.rs @@ -6,11 +6,13 @@ use token::{TokenStore, services::RegisterTokenService}; use crate::{ domain::users, - infra::error::{AppError, AppErrorKind}, - routers::{ - extractor::Json, - response::app_error_to_response, - serializer::{Response, ResponseCode}, + infra::{ + error::{AppError, AppErrorKind}, + http::{ + extractor::Json, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, }, state::AppState, }; diff --git a/auth/src/features/api/v1/common/mod.rs b/auth/src/features/api/v1/common/mod.rs index d602cdc..ad94304 100644 --- a/auth/src/features/api/v1/common/mod.rs +++ b/auth/src/features/api/v1/common/mod.rs @@ -1,6 +1,6 @@ use axum::{Router, routing::any}; -use crate::{routers::cors, state::AppState}; +use crate::{infra::http::cors, state::AppState}; pub mod not_found; pub mod ping; diff --git a/auth/src/features/api/v1/common/not_found.rs b/auth/src/features/api/v1/common/not_found.rs index 6f21d18..0eb91cb 100644 --- a/auth/src/features/api/v1/common/not_found.rs +++ b/auth/src/features/api/v1/common/not_found.rs @@ -1,4 +1,4 @@ -use crate::routers::serializer::{Response, ResponseCode}; +use crate::infra::http::serializer::{Response, ResponseCode}; pub async fn controller() -> Response { ResponseCode::NotFound.into() diff --git a/auth/src/features/api/v1/common/ping.rs b/auth/src/features/api/v1/common/ping.rs index 6c54891..2cbd26a 100644 --- a/auth/src/features/api/v1/common/ping.rs +++ b/auth/src/features/api/v1/common/ping.rs @@ -1,4 +1,4 @@ -use crate::routers::serializer::{Response, ResponseCode}; +use crate::infra::http::serializer::{Response, ResponseCode}; pub async fn controller() -> Response { Response::new(ResponseCode::OK.into(), "pong".into(), None) diff --git a/auth/src/features/api/v1/users/delete.rs b/auth/src/features/api/v1/users/delete.rs index 3af7213..b15c275 100644 --- a/auth/src/features/api/v1/users/delete.rs +++ b/auth/src/features/api/v1/users/delete.rs @@ -6,12 +6,14 @@ use token::services::SessionService; use crate::{ domain::{permission, role, users}, - infra::error::{AppError, AppErrorKind}, - routers::{ - extractor::{Json, LoginAccess, OperatorAccess}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, - utils::cookie::CookieJarExt, + infra::{ + error::{AppError, AppErrorKind}, + http::{ + extractor::{Json, LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + utils::cookie::CookieJarExt, + }, }, state::AppState, }; diff --git a/auth/src/features/api/v1/users/info.rs b/auth/src/features/api/v1/users/info.rs index 875ad53..f0d7e00 100644 --- a/auth/src/features/api/v1/users/info.rs +++ b/auth/src/features/api/v1/users/info.rs @@ -7,11 +7,13 @@ use crate::{ user_info::{self, Gender}, user_settings, users, }, - infra::error::{AppError, AppErrorKind}, - routers::{ - extractor::{LoginAccess, OperatorAccess}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, + infra::{ + error::{AppError, AppErrorKind}, + http::{ + extractor::{LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, }, state::AppState, }; diff --git a/auth/src/features/api/v1/users/mod.rs b/auth/src/features/api/v1/users/mod.rs index b0042b2..3861f5c 100644 --- a/auth/src/features/api/v1/users/mod.rs +++ b/auth/src/features/api/v1/users/mod.rs @@ -3,7 +3,7 @@ use axum::{ routing::{any, delete, patch}, }; -use crate::{routers::middleware::permission::PermissionAccess, state::AppState}; +use crate::{infra::http::middleware::permission::PermissionAccess, state::AppState}; mod delete; mod info; diff --git a/auth/src/features/api/v1/users/setting.rs b/auth/src/features/api/v1/users/setting.rs index 431edad..9a9a338 100644 --- a/auth/src/features/api/v1/users/setting.rs +++ b/auth/src/features/api/v1/users/setting.rs @@ -7,11 +7,11 @@ use crate::{ infra::{ database::ModelError, error::{AppError, AppErrorKind}, - }, - routers::{ - extractor::{LoginAccess, OperatorAccess}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, + http::{ + extractor::{LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, }, state::AppState, }; diff --git a/auth/src/features/api/v1/users/update.rs b/auth/src/features/api/v1/users/update.rs index f95ae78..dfa365b 100644 --- a/auth/src/features/api/v1/users/update.rs +++ b/auth/src/features/api/v1/users/update.rs @@ -12,11 +12,13 @@ use crate::{ user_info::{self, Gender}, user_settings, users, }, - infra::error::{AppError, AppErrorKind}, - routers::{ - extractor::{Json, LoginAccess, OperatorAccess}, - response::app_error_to_response, - serializer::{Response, ResponseCode}, + infra::{ + error::{AppError, AppErrorKind}, + http::{ + extractor::{Json, LoginAccess, OperatorAccess}, + response::app_error_to_response, + serializer::{Response, ResponseCode}, + }, }, state::AppState, }; diff --git a/auth/src/features/assets.rs b/auth/src/features/assets.rs index cd7fd63..24f1ef2 100644 --- a/auth/src/features/assets.rs +++ b/auth/src/features/assets.rs @@ -6,7 +6,7 @@ use axum::{ response::IntoResponse, }; -use crate::routers::utils::content_type; +use crate::infra::http::utils::content_type; pub async fn controller(request: Request) -> impl IntoResponse { let method = request.method().clone(); diff --git a/auth/src/routers/cors.rs b/auth/src/infra/http/cors.rs similarity index 100% rename from auth/src/routers/cors.rs rename to auth/src/infra/http/cors.rs diff --git a/auth/src/routers/extractor/guest.rs b/auth/src/infra/http/extractor/guest.rs similarity index 94% rename from auth/src/routers/extractor/guest.rs rename to auth/src/infra/http/extractor/guest.rs index dc1d234..1cc9550 100644 --- a/auth/src/routers/extractor/guest.rs +++ b/auth/src/infra/http/extractor/guest.rs @@ -2,7 +2,7 @@ use axum::{extract::FromRequestParts, http::request::Parts}; use axum_extra::extract::CookieJar; use token::services::{SessionLookup, SessionService}; -use crate::{routers::serializer::ResponseCode, state::AppState}; +use crate::{infra::http::serializer::ResponseCode, state::AppState}; pub struct GuestAccess; diff --git a/auth/src/routers/extractor/json.rs b/auth/src/infra/http/extractor/json.rs similarity index 93% rename from auth/src/routers/extractor/json.rs rename to auth/src/infra/http/extractor/json.rs index 3ee14c0..b080c41 100644 --- a/auth/src/routers/extractor/json.rs +++ b/auth/src/infra/http/extractor/json.rs @@ -1,7 +1,7 @@ use axum::extract::{FromRequest, Request, rejection::JsonRejection}; use serde::de::DeserializeOwned; -use crate::routers::serializer::{Response, ResponseCode}; +use crate::infra::http::serializer::{Response, ResponseCode}; #[derive(Debug, Clone, Copy, Default)] pub struct Json(pub T); diff --git a/auth/src/routers/extractor/login.rs b/auth/src/infra/http/extractor/login.rs similarity index 97% rename from auth/src/routers/extractor/login.rs rename to auth/src/infra/http/extractor/login.rs index 7a33aba..502dffc 100644 --- a/auth/src/routers/extractor/login.rs +++ b/auth/src/infra/http/extractor/login.rs @@ -4,7 +4,7 @@ use token::services::{SessionLookup, SessionService}; use crate::{ domain::{role, user_info, user_settings, users}, - routers::serializer::ResponseCode, + infra::http::serializer::ResponseCode, state::AppState, }; diff --git a/auth/src/routers/extractor/mod.rs b/auth/src/infra/http/extractor/mod.rs similarity index 100% rename from auth/src/routers/extractor/mod.rs rename to auth/src/infra/http/extractor/mod.rs diff --git a/auth/src/routers/extractor/operator.rs b/auth/src/infra/http/extractor/operator.rs similarity index 90% rename from auth/src/routers/extractor/operator.rs rename to auth/src/infra/http/extractor/operator.rs index 3b5b12b..8d6c26f 100644 --- a/auth/src/routers/extractor/operator.rs +++ b/auth/src/infra/http/extractor/operator.rs @@ -1,7 +1,7 @@ use axum::{extract::FromRequestParts, http::request::Parts}; use crate::{ - routers::{extractor::LoginAccess, serializer::ResponseCode}, + infra::http::{extractor::LoginAccess, serializer::ResponseCode}, state::AppState, }; diff --git a/auth/src/routers/middleware/mod.rs b/auth/src/infra/http/middleware/mod.rs similarity index 100% rename from auth/src/routers/middleware/mod.rs rename to auth/src/infra/http/middleware/mod.rs diff --git a/auth/src/routers/middleware/permission.rs b/auth/src/infra/http/middleware/permission.rs similarity index 98% rename from auth/src/routers/middleware/permission.rs rename to auth/src/infra/http/middleware/permission.rs index 4901b8f..c1799c2 100644 --- a/auth/src/routers/middleware/permission.rs +++ b/auth/src/infra/http/middleware/permission.rs @@ -9,7 +9,7 @@ use axum_extra::extract::cookie::Cookie; use token::services::{SessionLookup, SessionService}; use tower::{Layer, Service}; -use crate::{domain::permission, routers::serializer::ResponseCode, state::APP_STATE}; +use crate::{domain::permission, infra::http::serializer::ResponseCode, state::APP_STATE}; #[derive(Clone)] enum PermType { diff --git a/auth/src/routers/mod.rs b/auth/src/infra/http/mod.rs similarity index 100% rename from auth/src/routers/mod.rs rename to auth/src/infra/http/mod.rs diff --git a/auth/src/routers/response.rs b/auth/src/infra/http/response.rs similarity index 95% rename from auth/src/routers/response.rs rename to auth/src/infra/http/response.rs index d607795..76e20b1 100644 --- a/auth/src/routers/response.rs +++ b/auth/src/infra/http/response.rs @@ -1,6 +1,6 @@ -use crate::{ - infra::error::{AppError, AppErrorKind}, - routers::serializer::{Response, ResponseCode}, +use crate::infra::{ + error::{AppError, AppErrorKind}, + http::serializer::{Response, ResponseCode}, }; impl From for ResponseCode { diff --git a/auth/src/routers/serializer/common.rs b/auth/src/infra/http/serializer/common.rs similarity index 100% rename from auth/src/routers/serializer/common.rs rename to auth/src/infra/http/serializer/common.rs diff --git a/auth/src/routers/serializer/error.rs b/auth/src/infra/http/serializer/error.rs similarity index 100% rename from auth/src/routers/serializer/error.rs rename to auth/src/infra/http/serializer/error.rs diff --git a/auth/src/routers/serializer/mod.rs b/auth/src/infra/http/serializer/mod.rs similarity index 100% rename from auth/src/routers/serializer/mod.rs rename to auth/src/infra/http/serializer/mod.rs diff --git a/auth/src/routers/utils/content_type.rs b/auth/src/infra/http/utils/content_type.rs similarity index 100% rename from auth/src/routers/utils/content_type.rs rename to auth/src/infra/http/utils/content_type.rs diff --git a/auth/src/routers/utils/cookie.rs b/auth/src/infra/http/utils/cookie.rs similarity index 100% rename from auth/src/routers/utils/cookie.rs rename to auth/src/infra/http/utils/cookie.rs diff --git a/auth/src/routers/utils/mod.rs b/auth/src/infra/http/utils/mod.rs similarity index 100% rename from auth/src/routers/utils/mod.rs rename to auth/src/infra/http/utils/mod.rs diff --git a/auth/src/infra/mod.rs b/auth/src/infra/mod.rs index 273afd0..e9a3b6c 100644 --- a/auth/src/infra/mod.rs +++ b/auth/src/infra/mod.rs @@ -1,6 +1,7 @@ pub mod config; pub mod database; pub mod error; +pub mod http; pub mod logger; pub mod mailer; pub mod session; diff --git a/auth/src/main.rs b/auth/src/main.rs index 94d6866..25cda98 100644 --- a/auth/src/main.rs +++ b/auth/src/main.rs @@ -6,7 +6,6 @@ mod init; mod domain; mod features; mod infra; -mod routers; mod state; use std::{io, sync::Arc};