Skip to main content

tdm_server_rust/web/
mod.rs

1//! HTTP 路由与控制器层 (Web Layer)
2//!
3//! 定义所有 API 端点的路由注册和 Handler 函数。
4//!
5//! ## 路由表 (`create_router()`)
6//!
7//! | 路由前缀 | Controller | 端点数 | 说明 |
8//! |---------|-----------|--------|------|
9//! | `/api/login`, `/api/reg` | [`login_controller`] | 5 | 登录/注册/邀请码 |
10//! | `/api/members` | [`member_controller`] | 12 | 组员 CRUD/岗位/常驻 |
11//! | `/api/authors` | [`author_controller`] | 7 | 作者管理 |
12//! | `/api/magazines` | [`magazine_controller`] | 7 | 杂志管理 |
13//! | `/api/evaluations` | [`evaluation_controller`] | 5 | 评价管理 |
14//! | `/api/questionnaires` | [`questionnaire_controller`] | 3 | 问卷提交查询 |
15//! | `/api/mangas` | [`manga_controller`] | 27 | 漫画/收藏/术语表 |
16//! | `/api/episodes` | [`episode_controller`] | 14 | 话数/发布/上传 |
17//! | `/api/manga/benefits` | [`manga_benefit_controller`] | 4 | 特典管理 |
18//! | `/api/reward` | [`reward_controller`] | 10 | 悬赏/兑换/抽奖 |
19//! | `/api/oss` | [`oss_controller`] | 4 | 文件上传下载 |
20//! | `/api/admin/taskTracking` | [`task_tracking_controller`] | 3 | 任务看板 |
21//!
22//! ## 中间件栈
23//!
24//! 所有 `/api/*` 路由经过:`error_log → auth → handler`
25//! dev 环境下额外添加:`debug_log` 中间件
26
27/// 作者管理 API 路由
28pub mod author_controller;
29/// dev 环境 API(SkyWalking 302 + health + errors)
30/// OpenAPI / Swagger UI 静态资源路由
31pub mod doc_controller;
32/// 话数管理 API 路由
33pub mod episode_controller;
34/// 评价管理 API 路由
35pub mod evaluation_controller;
36/// 登录/注册 API 路由
37pub mod login_controller;
38/// 杂志管理 API 路由
39pub mod magazine_controller;
40/// 漫画特典 API 路由
41pub mod manga_benefit_controller;
42/// 漫画管理 API 路由
43pub mod manga_controller;
44/// 组员管理 API 路由
45pub mod member_controller;
46/// OSS 文件上传/下载 API 路由
47pub mod oss_controller;
48/// 问卷管理 API 路由
49pub mod questionnaire_controller;
50/// 悬赏/兑换/抽奖 API 路由
51pub mod reward_controller;
52/// 任务看板 API 路由
53pub mod task_tracking_controller;
54
55use crate::{
56    app::AppState,
57    dev,
58    middleware::{auth_middleware, debug_log_middleware, error_log_middleware},
59    telemetry::telemetry_http_span_middleware,
60};
61use axum::http::header;
62use axum::http::{HeaderName, StatusCode};
63use axum::middleware;
64use axum::response::IntoResponse;
65use axum::Router;
66use tower_http::cors::CorsLayer;
67
68/// 创建带 `/api` 前缀的完整路由
69pub fn create_router(state: AppState) -> Router {
70    let api = Router::new()
71        .merge(login_controller::routes())
72        .nest("/members", member_controller::routes())
73        .nest("/authors", author_controller::routes())
74        .nest("/magazines", magazine_controller::routes())
75        .nest("/evaluations", evaluation_controller::routes())
76        .nest("/questionnaires", questionnaire_controller::routes())
77        .nest("/mangas", manga_controller::routes())
78        .nest("/episodes", episode_controller::routes())
79        .nest("/manga/benefits", manga_benefit_controller::routes())
80        .nest("/reward", reward_controller::routes())
81        .nest("/oss", oss_controller::routes())
82        .nest("/admin/taskTracking", task_tracking_controller::routes());
83
84    let api = api
85        .fallback(api_not_found)
86        .layer(middleware::from_fn(error_log_middleware))
87        .layer(middleware::from_fn_with_state(state.clone(), auth_middleware))
88        .with_state(state.clone());
89
90    let cors = CorsLayer::permissive().expose_headers([
91        HeaderName::from_static("server-timing"),
92        header::LOCATION,
93        HeaderName::from_static("x-download-filename"),
94        header::CONTENT_DISPOSITION,
95    ]);
96
97    let mut app = Router::new()
98        .nest("/api", api)
99        .layer(cors)
100        .with_state(state.clone());
101
102    if state.config.telemetry.enabled {
103        app = app.layer(middleware::from_fn(telemetry_http_span_middleware));
104    }
105
106    if matches!(state.config.profile.as_str(), "dev" | "dev-h2") {
107        let springdoc = state.config.springdoc.clone();
108        app = app.merge(doc_controller::routes(springdoc));
109        app = app.merge(dev::routes(state.clone()));
110        app = app.layer(middleware::from_fn(debug_log_middleware));
111    }
112
113    app
114}
115
116/// 未匹配的 `/api/*` 路由
117#[tracing::instrument(skip_all, level = "info")]
118async fn api_not_found() -> impl IntoResponse {
119    (
120        StatusCode::NOT_FOUND,
121        r#"{"code":404,"msg":"接口不存在","data":null}"#,
122    )
123}