73 lines
2.3 KiB
Rust
73 lines
2.3 KiB
Rust
//! Application state shared across request handlers
|
|
|
|
use sqlx::PgPool;
|
|
use std::sync::Arc;
|
|
use tokio::sync::{broadcast, RwLock};
|
|
|
|
use crate::auth::jwt::JwtConfig;
|
|
use attune_common::{config::Config, mq::Publisher};
|
|
|
|
/// Shared application state
|
|
#[derive(Clone)]
|
|
pub struct AppState {
|
|
/// Database connection pool
|
|
pub db: PgPool,
|
|
/// JWT configuration
|
|
pub jwt_config: Arc<JwtConfig>,
|
|
/// CORS allowed origins
|
|
pub cors_origins: Vec<String>,
|
|
/// Application configuration
|
|
pub config: Arc<Config>,
|
|
/// Optional message queue publisher (shared, swappable after reconnection)
|
|
pub publisher: Arc<RwLock<Option<Arc<Publisher>>>>,
|
|
/// Broadcast channel for SSE notifications
|
|
pub broadcast_tx: broadcast::Sender<String>,
|
|
}
|
|
|
|
impl AppState {
|
|
/// Create new application state
|
|
pub fn new(db: PgPool, config: Config) -> Self {
|
|
let jwt_secret = config.security.jwt_secret.clone().unwrap_or_else(|| {
|
|
tracing::warn!(
|
|
"JWT_SECRET not set in config, using default (INSECURE for production!)"
|
|
);
|
|
"insecure_default_secret_change_in_production".to_string()
|
|
});
|
|
|
|
let jwt_config = JwtConfig {
|
|
secret: jwt_secret,
|
|
access_token_expiration: config.security.jwt_access_expiration as i64,
|
|
refresh_token_expiration: config.security.jwt_refresh_expiration as i64,
|
|
};
|
|
|
|
let cors_origins = config.server.cors_origins.clone();
|
|
|
|
// Create broadcast channel for SSE notifications (capacity 1000)
|
|
let (broadcast_tx, _) = broadcast::channel(1000);
|
|
|
|
Self {
|
|
db,
|
|
jwt_config: Arc::new(jwt_config),
|
|
cors_origins,
|
|
config: Arc::new(config),
|
|
publisher: Arc::new(RwLock::new(None)),
|
|
broadcast_tx,
|
|
}
|
|
}
|
|
|
|
/// Set the message queue publisher (called once at startup or after reconnection)
|
|
pub async fn set_publisher(&self, publisher: Arc<Publisher>) {
|
|
let mut guard = self.publisher.write().await;
|
|
*guard = Some(publisher);
|
|
}
|
|
|
|
/// Get a clone of the current publisher, if available
|
|
pub async fn get_publisher(&self) -> Option<Arc<Publisher>> {
|
|
self.publisher.read().await.clone()
|
|
}
|
|
}
|
|
|
|
/// Type alias for Arc-wrapped application state
|
|
/// Used by Axum handlers
|
|
pub type SharedState = Arc<AppState>;
|