juicehost/
state.rs

1//! Shared state every handler can access.
2
3use std::path::PathBuf;
4use std::sync::Arc;
5use std::time::Duration;
6
7use dashmap::DashMap;
8
9/// A single entry in the in-memory file cache.
10#[derive(Clone)]
11pub struct CacheEntry {
12    /// Absolute path to the cached file on disk.
13    pub path: PathBuf,
14}
15
16/// Concurrent in-memory cache mapping file IDs to their disk paths.
17pub type FileCache = Arc<DashMap<String, CacheEntry>>;
18
19/// Shared state injected into every axum handler.
20#[derive(Clone)]
21pub struct AppState {
22    pub files_dir: PathBuf,
23    pub backend_url: String,
24    pub api_key: String,
25    pub allowed_origins: Vec<String>,
26    pub http: reqwest::Client,
27    pub files_cache: FileCache,
28    pub min_free_space_bytes: u64,
29}
30
31impl AppState {
32    pub fn new(files_dir: PathBuf, backend_url: String, api_key: String, allowed_origins: Vec<String>, min_free_space_bytes: u64) -> Self {
33        Self {
34            files_dir,
35            backend_url,
36            api_key,
37            allowed_origins,
38            min_free_space_bytes,
39            http: reqwest::Client::builder()
40                .timeout(Duration::from_secs(30))
41                .connect_timeout(Duration::from_secs(5))
42                .pool_idle_timeout(Duration::from_secs(30))
43                .build()
44                .expect("Failed to build reqwest client"),
45            files_cache: Arc::new(DashMap::new()),
46        }
47    }
48
49    pub async fn init_cache(&self) {
50        let mut entries = tokio::fs::read_dir(&self.files_dir).await;
51        if let Ok(ref mut entries) = entries {
52            while let Ok(Some(entry)) = entries.next_entry().await {
53                let name = entry.file_name().to_string_lossy().to_string();
54                if let Some(dot) = name.rfind('.') {
55                    let id = name[..dot].to_string();
56                    self.files_cache.insert(id, CacheEntry { path: entry.path() });
57                }
58            }
59        }
60        tracing::info!("file cache initialized with {} entries", self.files_cache.len());
61    }
62}