ruvector_scipix/api/
state.rs1use moka::future::Cache;
2use sha2::{Sha256, Digest};
3use std::collections::HashMap;
4use std::sync::Arc;
5use std::time::Duration;
6
7use super::{jobs::JobQueue, middleware::{create_rate_limiter, AppRateLimiter}};
8
9#[derive(Clone)]
11pub struct AppState {
12 pub job_queue: Arc<JobQueue>,
14
15 pub cache: Cache<String, String>,
17
18 pub rate_limiter: AppRateLimiter,
20
21 pub auth_enabled: bool,
23
24 pub api_keys: Arc<HashMap<String, String>>,
27}
28
29impl AppState {
30 pub fn new() -> Self {
32 Self {
33 job_queue: Arc::new(JobQueue::new()),
34 cache: create_cache(),
35 rate_limiter: create_rate_limiter(),
36 auth_enabled: false,
37 api_keys: Arc::new(HashMap::new()),
38 }
39 }
40
41 pub fn with_config(max_jobs: usize, cache_size: u64) -> Self {
43 Self {
44 job_queue: Arc::new(JobQueue::with_capacity(max_jobs)),
45 cache: Cache::builder()
46 .max_capacity(cache_size)
47 .time_to_live(Duration::from_secs(3600))
48 .time_to_idle(Duration::from_secs(600))
49 .build(),
50 rate_limiter: create_rate_limiter(),
51 auth_enabled: false,
52 api_keys: Arc::new(HashMap::new()),
53 }
54 }
55
56 pub fn with_auth(api_keys: HashMap<String, String>) -> Self {
58 let hashed_keys: HashMap<String, String> = api_keys
60 .into_iter()
61 .map(|(app_id, key)| (app_id, hash_api_key(&key)))
62 .collect();
63
64 Self {
65 job_queue: Arc::new(JobQueue::new()),
66 cache: create_cache(),
67 rate_limiter: create_rate_limiter(),
68 auth_enabled: true,
69 api_keys: Arc::new(hashed_keys),
70 }
71 }
72
73 pub fn add_api_key(&mut self, app_id: String, api_key: &str) {
75 let hashed = hash_api_key(api_key);
76 Arc::make_mut(&mut self.api_keys).insert(app_id, hashed);
77 self.auth_enabled = true;
78 }
79
80 pub fn set_auth_enabled(&mut self, enabled: bool) {
82 self.auth_enabled = enabled;
83 }
84}
85
86impl Default for AppState {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92fn hash_api_key(key: &str) -> String {
94 let mut hasher = Sha256::new();
95 hasher.update(key.as_bytes());
96 format!("{:x}", hasher.finalize())
97}
98
99fn create_cache() -> Cache<String, String> {
101 Cache::builder()
102 .max_capacity(10_000)
104 .time_to_live(Duration::from_secs(3600))
106 .time_to_idle(Duration::from_secs(600))
108 .build()
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[tokio::test]
116 async fn test_state_creation() {
117 let state = AppState::new();
118 assert!(Arc::strong_count(&state.job_queue) >= 1);
119 }
120
121 #[tokio::test]
122 async fn test_state_with_config() {
123 let state = AppState::with_config(100, 5000);
124 assert!(Arc::strong_count(&state.job_queue) >= 1);
125 }
126
127 #[tokio::test]
128 async fn test_cache_operations() {
129 let state = AppState::new();
130
131 state.cache.insert("key1".to_string(), "value1".to_string()).await;
133
134 let value = state.cache.get(&"key1".to_string()).await;
136 assert_eq!(value, Some("value1".to_string()));
137
138 let missing = state.cache.get(&"missing".to_string()).await;
140 assert_eq!(missing, None);
141 }
142}