api/routes/
mod.rs

1//! HTTP route entry point for `/api/...`.
2//!
3//! This module defines all HTTP entry points under the `/api` namespace.
4//! Routes are organized by domain (e.g., authentication, users, modules, health),
5//! each protected via appropriate access control middleware.  
6//!
7//! Route groups include:
8//! - `/health` → Health check endpoint (public)
9//! - `/auth` → Authentication endpoints (login, token handling, public)
10//! - `/users` → User management endpoints (admin-only)
11//! - `/modules` → Module management, personnel, and assignments (authenticated users)
12//! - `/me` → User-specific endpoints (announcements, tickets, assignments)
13
14use crate::auth::guards::{require_admin, require_authenticated};
15use crate::routes::auth::get::get_avatar;
16use crate::routes::{
17    auth::auth_routes,
18    health::health_routes,
19    modules::modules_routes,
20    users::users_routes,
21    test::test_routes,
22};
23use axum::{middleware::from_fn, routing::get, Router};
24use util::{config::AppConfig, state::AppState};
25use crate::routes::me::me_routes;
26
27pub mod auth;
28pub mod common;
29pub mod health;
30pub mod modules;
31pub mod users;
32pub mod test;
33pub mod me;
34
35/// Builds the complete application router for all HTTP endpoints.
36///
37/// The returned router has `AppState` as its state type and mounts
38/// all core API routes under their respective base paths.  
39///
40/// # Route Structure:
41/// - `/health` → Health check endpoint (no authentication required).
42/// - `/auth` → Authentication endpoints (login, refresh, etc.).
43/// - `/users` → User management (restricted to admins via `require_admin` middleware).
44/// - `/users/{user_id}/avatar` → Publicly accessible avatar retrieval.
45/// - `/modules` → Module CRUD, personnel management, and assignments (requires authentication).
46/// - `/me` → User-specific endpoints (announcements, tickets, assignments, etc.)
47/// - `/test` → Development/test-only routes (mounted only if `env != production`).
48///
49/// The `/test` route group is mounted **here** instead of in `main` to:
50/// 1. Keep `main` focused on server startup logic only.
51/// 2. Avoid changing the `Router` type after construction, which can cause trait bound issues.
52/// 3. Ensure that all route registration logic is centralized in one place.
53pub fn routes(app_state: AppState) -> Router<AppState> {
54    let mut router: Router<AppState> = Router::new()
55        .nest("/health", health_routes())
56        .nest("/auth", auth_routes())
57        .nest("/users", users_routes().route_layer(from_fn(require_admin)))
58        .route("/users/{user_id}/avatar", get(get_avatar))
59        .nest("/modules",modules_routes(app_state.clone()).route_layer(from_fn(require_authenticated)),)
60        .nest("/me", me_routes().route_layer(from_fn(require_authenticated)))
61        .with_state(app_state.clone());
62
63    // Conditionally mount the `/test` route group if *not* in production.
64    //
65    // This keeps development and test-only APIs out of the production environment,
66    // but still makes them available in `development` or `test` modes.
67    let env = AppConfig::global().env.to_lowercase();
68    if env != "production" {
69        router = router.nest("/test", test_routes(app_state.clone()));
70        tracing::info!("[dev/test] Mounted /test routes (env = {env})");
71    } else {
72        tracing::info!("[prod] Skipping /test routes");
73    }
74
75    router
76}