llm_registry_api/
routes.rs

1//! API route definitions
2//!
3//! This module defines all API routes and builds the router.
4
5use axum::{
6    middleware,
7    routing::{delete, get, patch, post},
8    Router,
9};
10
11use crate::{
12    auth::{optional_auth, require_auth, AuthState},
13    auth_handlers::{generate_api_key, login, logout, me, refresh_token, AuthHandlerState},
14    graphql::{build_schema, graphql_handler, graphql_playground},
15    handlers::{
16        delete_asset, get_asset, get_dependencies, get_dependents, health_check, list_assets,
17        metrics, register_asset, update_asset, version_info, AppState,
18    },
19};
20
21/// Build the API router with all routes
22pub fn build_router(state: AppState) -> Router {
23    Router::new()
24        // Health and info endpoints
25        .route("/health", get(health_check))
26        .route("/metrics", get(metrics))
27        .route("/version", get(version_info))
28        // API v1 routes
29        .nest("/v1", build_v1_routes())
30        .with_state(state)
31}
32
33/// Build the API router with authentication enabled
34///
35/// This function builds a complete router with authentication endpoints
36/// and middleware. Protected routes require JWT authentication.
37pub fn build_router_with_auth(
38    state: AppState,
39    auth_handler_state: AuthHandlerState,
40    auth_state: AuthState,
41) -> Router {
42    // Build public routes
43    let public_routes = Router::new()
44        .route("/health", get(health_check))
45        .route("/metrics", get(metrics))
46        .route("/version", get(version_info))
47        .with_state(state.clone());
48
49    // Build auth routes (public)
50    let auth_routes = Router::new()
51        .route("/login", post(login))
52        .route("/refresh", post(refresh_token))
53        .with_state(auth_handler_state.clone());
54
55    // Build protected auth routes
56    let protected_auth_routes = Router::new()
57        .route("/me", get(me))
58        .route("/logout", post(logout))
59        .route("/api-keys", post(generate_api_key))
60        .layer(middleware::from_fn_with_state(
61            auth_state.clone(),
62            require_auth,
63        ))
64        .with_state(auth_handler_state);
65
66    // Build v1 routes (with optional authentication on some endpoints)
67    let v1_routes = build_v1_routes().with_state(state);
68
69    // Combine all routes
70    Router::new()
71        .merge(public_routes)
72        .nest("/v1/auth", auth_routes)
73        .nest("/v1/auth", protected_auth_routes)
74        .nest("/v1", v1_routes)
75}
76
77/// Build the API router with GraphQL support
78///
79/// This function builds a complete router with REST API, GraphQL API,
80/// authentication, and GraphQL Playground.
81pub fn build_router_with_graphql(
82    state: AppState,
83    auth_handler_state: AuthHandlerState,
84    auth_state: AuthState,
85) -> Router {
86    // Build GraphQL schema
87    let schema = build_schema(state.services.clone());
88
89    // Build public routes
90    let public_routes = Router::new()
91        .route("/health", get(health_check))
92        .route("/metrics", get(metrics))
93        .route("/version", get(version_info))
94        .route("/graphql/playground", get(graphql_playground))
95        .with_state(state.clone());
96
97    // Build GraphQL route with optional authentication
98    let graphql_route = Router::new()
99        .route("/graphql", post(graphql_handler))
100        .layer(middleware::from_fn_with_state(
101            auth_state.clone(),
102            optional_auth,
103        ))
104        .with_state(schema);
105
106    // Build auth routes (public)
107    let auth_routes = Router::new()
108        .route("/login", post(login))
109        .route("/refresh", post(refresh_token))
110        .with_state(auth_handler_state.clone());
111
112    // Build protected auth routes
113    let protected_auth_routes = Router::new()
114        .route("/me", get(me))
115        .route("/logout", post(logout))
116        .route("/api-keys", post(generate_api_key))
117        .layer(middleware::from_fn_with_state(
118            auth_state.clone(),
119            require_auth,
120        ))
121        .with_state(auth_handler_state);
122
123    // Build v1 routes
124    let v1_routes = build_v1_routes().with_state(state);
125
126    // Combine all routes
127    Router::new()
128        .merge(public_routes)
129        .merge(graphql_route)
130        .nest("/v1/auth", auth_routes)
131        .nest("/v1/auth", protected_auth_routes)
132        .nest("/v1", v1_routes)
133}
134
135/// Build v1 API routes
136fn build_v1_routes() -> Router<AppState> {
137    Router::new()
138        // Asset management
139        .route("/assets", post(register_asset))
140        .route("/assets", get(list_assets))
141        .route("/assets/:id", get(get_asset))
142        .route("/assets/:id", patch(update_asset))
143        .route("/assets/:id", delete(delete_asset))
144        // Dependencies
145        .route("/assets/:id/dependencies", get(get_dependencies))
146        .route("/assets/:id/dependents", get(get_dependents))
147}
148
149/// Route configuration
150#[derive(Debug, Clone)]
151pub struct RouteConfig {
152    /// API base path
153    pub base_path: String,
154
155    /// API version
156    pub version: String,
157}
158
159impl Default for RouteConfig {
160    fn default() -> Self {
161        Self {
162            base_path: "/".to_string(),
163            version: "v1".to_string(),
164        }
165    }
166}
167
168impl RouteConfig {
169    /// Create a new route config
170    pub fn new() -> Self {
171        Self::default()
172    }
173
174    /// Set base path
175    pub fn with_base_path(mut self, path: impl Into<String>) -> Self {
176        self.base_path = path.into();
177        self
178    }
179
180    /// Set API version
181    pub fn with_version(mut self, version: impl Into<String>) -> Self {
182        self.version = version.into();
183        self
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    #[test]
192    fn test_route_config_default() {
193        let config = RouteConfig::default();
194        assert_eq!(config.base_path, "/");
195        assert_eq!(config.version, "v1");
196    }
197
198    #[test]
199    fn test_route_config_builder() {
200        let config = RouteConfig::new()
201            .with_base_path("/api")
202            .with_version("v2");
203
204        assert_eq!(config.base_path, "/api");
205        assert_eq!(config.version, "v2");
206    }
207}