1use std::sync::Arc;
9
10use arc_swap::ArcSwap;
11
12use crate::auth::JwtCache;
13use crate::auth::middleware::AuthState;
14use crate::backend::{DatabaseBackend, DbVersion, SqlDialect};
15use crate::config::AppConfig;
16use crate::error::Error;
17use crate::schema_cache::SchemaCache;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct PgVersion {
25 pub major: u32,
26 pub minor: u32,
27 pub patch: u32,
28}
29
30impl std::fmt::Display for PgVersion {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
33 }
34}
35
36impl From<&DbVersion> for PgVersion {
37 fn from(v: &DbVersion) -> Self {
38 Self {
39 major: v.major,
40 minor: v.minor,
41 patch: v.patch,
42 }
43 }
44}
45
46#[derive(Clone)]
52pub struct AppState {
53 pub db: Arc<dyn DatabaseBackend>,
55 pub dialect: Arc<dyn SqlDialect>,
57 pub config: Arc<ArcSwap<AppConfig>>,
59 pub schema_cache: Arc<ArcSwap<Option<SchemaCache>>>,
61 pub auth: AuthState,
63 pub jwt_cache: JwtCache,
65 pub db_version: DbVersion,
67 pub pg_version: PgVersion,
69}
70
71impl AppState {
72 pub fn new_with_backend(
77 db: Arc<dyn DatabaseBackend>,
78 dialect: Arc<dyn SqlDialect>,
79 config: AppConfig,
80 db_version: DbVersion,
81 ) -> Self {
82 let pg_version = PgVersion::from(&db_version);
83 let config_swap = Arc::new(ArcSwap::new(Arc::new(config)));
84 let auth = AuthState::with_shared_config(config_swap.clone());
85 let jwt_cache = auth.cache.clone();
86 Self {
87 db,
88 dialect,
89 config: config_swap,
90 schema_cache: Arc::new(ArcSwap::new(Arc::new(None))),
91 auth,
92 jwt_cache,
93 db_version,
94 pg_version,
95 }
96 }
97
98 pub fn config(&self) -> arc_swap::Guard<Arc<AppConfig>> {
100 self.config.load()
101 }
102
103 pub fn schema_cache_guard(&self) -> arc_swap::Guard<Arc<Option<SchemaCache>>> {
105 self.schema_cache.load()
106 }
107
108 #[tracing::instrument(name = "reload_config", skip(self))]
110 pub async fn reload_config(&self) -> Result<(), Error> {
111 let current = self.config.load();
112 let file_path = current.config_file_path.clone();
113 let new_config =
114 crate::config::load_config(file_path.as_deref(), std::collections::HashMap::new())
115 .await
116 .map_err(|e| Error::InvalidConfig {
117 message: e.to_string(),
118 })?;
119
120 self.config.store(Arc::new(new_config));
121
122 tracing::info!("Configuration reloaded successfully");
123 Ok(())
124 }
125
126 #[tracing::instrument(name = "reload_schema_cache", skip(self))]
128 pub async fn reload_schema_cache(&self) -> Result<(), Error> {
129 let config = self.config.load();
130 let introspector = self.db.introspector();
131 let cache = SchemaCache::load(&*introspector, &config).await?;
132
133 metrics::counter!("schema_cache.reload.total").increment(1);
134 self.schema_cache.store(Arc::new(Some(cache)));
135
136 tracing::info!("Schema cache reloaded successfully");
137 Ok(())
138 }
139}