tower_http_cache/admin/
mod.rs

1//! Admin API for cache introspection and management.
2//!
3//! This module provides HTTP endpoints for monitoring and managing the cache
4//! at runtime. Features include statistics, key inspection, hot key tracking,
5//! and tag-based invalidation.
6
7pub mod routes;
8pub mod stats;
9
10use crate::backend::CacheBackend;
11use std::sync::Arc;
12
13/// Configuration for the admin API.
14#[derive(Debug, Clone)]
15pub struct AdminConfig {
16    /// Whether admin API is enabled
17    pub enabled: bool,
18
19    /// Optional authentication token (Bearer token)
20    pub auth_token: Option<String>,
21
22    /// Whether authentication is required
23    pub require_auth: bool,
24
25    /// Mount path for admin endpoints (e.g., "/admin/cache")
26    pub mount_path: String,
27
28    /// Maximum number of keys to return in listing endpoints
29    pub max_keys_per_request: usize,
30
31    /// Maximum number of hot keys to track
32    pub max_hot_keys: usize,
33}
34
35impl Default for AdminConfig {
36    fn default() -> Self {
37        Self {
38            enabled: false,
39            auth_token: None,
40            require_auth: true,
41            mount_path: "/admin/cache".to_string(),
42            max_keys_per_request: 100,
43            max_hot_keys: 20,
44        }
45    }
46}
47
48impl AdminConfig {
49    /// Creates a new admin configuration with default settings.
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    /// Enables the admin API.
55    pub fn with_enabled(mut self, enabled: bool) -> Self {
56        self.enabled = enabled;
57        self
58    }
59
60    /// Sets the authentication token.
61    pub fn with_auth_token(mut self, token: impl Into<String>) -> Self {
62        self.auth_token = Some(token.into());
63        self
64    }
65
66    /// Sets whether authentication is required.
67    pub fn with_require_auth(mut self, require: bool) -> Self {
68        self.require_auth = require;
69        self
70    }
71
72    /// Sets the mount path.
73    pub fn with_mount_path(mut self, path: impl Into<String>) -> Self {
74        self.mount_path = path.into();
75        self
76    }
77
78    /// Validates the authentication token.
79    pub fn validate_token(&self, provided: Option<&str>) -> bool {
80        if !self.require_auth {
81            return true;
82        }
83
84        match (&self.auth_token, provided) {
85            (Some(expected), Some(provided)) => expected == provided,
86            (None, _) => !self.require_auth,
87            _ => false,
88        }
89    }
90}
91
92/// Shared state for admin API handlers.
93#[derive(Clone)]
94pub struct AdminState<B: CacheBackend> {
95    pub backend: B,
96    pub config: Arc<AdminConfig>,
97    pub stats: Arc<stats::GlobalStats>,
98}
99
100impl<B: CacheBackend> AdminState<B> {
101    /// Creates new admin state.
102    pub fn new(backend: B, config: AdminConfig) -> Self {
103        Self {
104            backend,
105            config: Arc::new(config),
106            stats: Arc::new(stats::GlobalStats::new()),
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn admin_config_default() {
117        let config = AdminConfig::default();
118        assert!(!config.enabled);
119        assert!(config.require_auth);
120        assert_eq!(config.mount_path, "/admin/cache");
121    }
122
123    #[test]
124    fn admin_config_builder() {
125        let config = AdminConfig::new()
126            .with_enabled(true)
127            .with_auth_token("secret")
128            .with_require_auth(false)
129            .with_mount_path("/cache/admin");
130
131        assert!(config.enabled);
132        assert_eq!(config.auth_token, Some("secret".to_string()));
133        assert!(!config.require_auth);
134        assert_eq!(config.mount_path, "/cache/admin");
135    }
136
137    #[test]
138    fn validate_token_success() {
139        let config = AdminConfig::new()
140            .with_auth_token("secret")
141            .with_require_auth(true);
142
143        assert!(config.validate_token(Some("secret")));
144    }
145
146    #[test]
147    fn validate_token_failure() {
148        let config = AdminConfig::new()
149            .with_auth_token("secret")
150            .with_require_auth(true);
151
152        assert!(!config.validate_token(Some("wrong")));
153        assert!(!config.validate_token(None));
154    }
155
156    #[test]
157    fn validate_token_no_auth_required() {
158        let config = AdminConfig::new().with_require_auth(false);
159
160        assert!(config.validate_token(None));
161        assert!(config.validate_token(Some("anything")));
162    }
163}