1use std::sync::Arc;
9use std::sync::atomic::{AtomicU64, Ordering};
10
11use arc_swap::ArcSwap;
12
13use crate::auth::JwtCache;
14use crate::auth::middleware::AuthState;
15use crate::backend::{DatabaseBackend, DbVersion, SqlDialect};
16use crate::config::AppConfig;
17use crate::error::Error;
18use crate::schema_cache::SchemaCache;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct PgVersion {
26 pub major: u32,
27 pub minor: u32,
28 pub patch: u32,
29}
30
31impl std::fmt::Display for PgVersion {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
34 }
35}
36
37impl From<&DbVersion> for PgVersion {
38 fn from(v: &DbVersion) -> Self {
39 Self {
40 major: v.major,
41 minor: v.minor,
42 patch: v.patch,
43 }
44 }
45}
46
47#[derive(Debug, Default)]
49pub struct Metrics {
50 pub requests_total: AtomicU64,
51 pub requests_success: AtomicU64,
52 pub requests_error: AtomicU64,
53 pub db_queries_total: AtomicU64,
54 pub schema_cache_reloads: AtomicU64,
55 pub jwt_cache_hits: AtomicU64,
56 pub jwt_cache_misses: AtomicU64,
57}
58
59#[derive(Clone)]
65pub struct AppState {
66 pub db: Arc<dyn DatabaseBackend>,
68 pub dialect: Arc<dyn SqlDialect>,
70 pub config: Arc<ArcSwap<AppConfig>>,
72 pub schema_cache: Arc<ArcSwap<Option<SchemaCache>>>,
74 pub auth: AuthState,
76 pub jwt_cache: JwtCache,
78 pub metrics: Arc<Metrics>,
80 pub db_version: DbVersion,
82 pub pg_version: PgVersion,
84}
85
86impl AppState {
87 pub fn new_with_backend(
92 db: Arc<dyn DatabaseBackend>,
93 dialect: Arc<dyn SqlDialect>,
94 config: AppConfig,
95 db_version: DbVersion,
96 ) -> Self {
97 let pg_version = PgVersion::from(&db_version);
98 let config_swap = Arc::new(ArcSwap::new(Arc::new(config)));
99 let auth = AuthState::with_shared_config(config_swap.clone());
100 let jwt_cache = auth.cache.clone();
101 Self {
102 db,
103 dialect,
104 config: config_swap,
105 schema_cache: Arc::new(ArcSwap::new(Arc::new(None))),
106 auth,
107 jwt_cache,
108 metrics: Arc::new(Metrics::default()),
109 db_version,
110 pg_version,
111 }
112 }
113
114 pub fn config(&self) -> arc_swap::Guard<Arc<AppConfig>> {
116 self.config.load()
117 }
118
119 pub fn schema_cache_guard(&self) -> arc_swap::Guard<Arc<Option<SchemaCache>>> {
121 self.schema_cache.load()
122 }
123
124 pub async fn reload_config(&self) -> Result<(), Error> {
126 let current = self.config.load();
127 let file_path = current.config_file_path.clone();
128 let new_config =
129 crate::config::load_config(file_path.as_deref(), std::collections::HashMap::new())
130 .await
131 .map_err(|e| Error::InvalidConfig {
132 message: e.to_string(),
133 })?;
134
135 self.config.store(Arc::new(new_config));
136
137 tracing::info!("Configuration reloaded successfully");
138 Ok(())
139 }
140
141 pub async fn reload_schema_cache(&self) -> Result<(), Error> {
143 let config = self.config.load();
144 let introspector = self.db.introspector();
145 let cache = SchemaCache::load(&*introspector, &config).await?;
146
147 self.metrics
148 .schema_cache_reloads
149 .fetch_add(1, Ordering::Relaxed);
150 self.schema_cache.store(Arc::new(Some(cache)));
151
152 tracing::info!("Schema cache reloaded successfully");
153 Ok(())
154 }
155}