Skip to main content

cja/
app_state.rs

1use crate::server::cookies::CookieKey;
2
3/// A trait representing the application state that must be implemented by users of the cja framework.
4///
5/// This trait provides access to core components like the database pool and cookie encryption key.
6/// The `AppState` is cloned for each request, so it should be cheap to clone (usually by wrapping
7/// expensive resources in Arc).
8///
9/// # Example
10///
11/// ```rust
12/// use cja::app_state::AppState;
13/// use cja::server::cookies::CookieKey;
14/// use sqlx::PgPool;
15///
16/// #[derive(Clone)]
17/// struct MyAppState {
18///     db: PgPool,
19///     cookie_key: CookieKey,
20/// }
21///
22/// impl AppState for MyAppState {
23///     fn version(&self) -> &str {
24///         "1.0.0"
25///     }
26///     
27///     fn db(&self) -> &PgPool {
28///         &self.db
29///     }
30///     
31///     fn cookie_key(&self) -> &CookieKey {
32///         &self.cookie_key
33///     }
34/// }
35/// ```
36///
37/// # Using with Axum
38///
39/// ```rust
40/// use cja::app_state::AppState;
41/// use cja::server::cookies::CookieKey;
42/// use sqlx::postgres::PgPoolOptions;
43/// use axum::{Router, routing::get, extract::State};
44///
45/// #[derive(Clone)]
46/// struct MyAppState {
47///     db: sqlx::PgPool,
48///     cookie_key: CookieKey,
49/// }
50///
51/// impl AppState for MyAppState {
52///     fn version(&self) -> &str { "1.0.0" }
53///     fn db(&self) -> &sqlx::PgPool { &self.db }
54///     fn cookie_key(&self) -> &CookieKey { &self.cookie_key }
55/// }
56///
57/// async fn handler(State(state): State<MyAppState>) -> String {
58///     format!("App version: {}", state.version())
59/// }
60///
61///  async fn example() -> Result<(), Box<dyn std::error::Error>> {
62///  use sqlx::postgres::PgPoolOptions;
63/// let db = PgPoolOptions::new()
64///      .connect(&std::env::var("DATABASE_URL").unwrap_or_else(|_| "postgres://test".to_string()))
65///      .await
66///      .map_err(|_| "Database connection failed")?;
67///     
68/// let state = MyAppState {
69///     db,
70///     cookie_key: CookieKey::from_env_or_generate()?,
71/// };
72///
73/// let app: Router = Router::new()
74///     .route("/", get(handler))
75///     .with_state(state);
76///
77/// // Run the server
78/// # Ok(())
79/// # }
80/// ```
81pub trait AppState: Clone + Send + Sync + 'static {
82    /// Returns the version string for the application.
83    /// This is useful for health checks and debugging.
84    fn version(&self) -> &str;
85
86    /// Returns a reference to the database connection pool.
87    /// The pool is typically shared across all requests.
88    fn db(&self) -> &sqlx::PgPool;
89
90    /// Called after a successful autocommit job enqueue. An override may wake
91    /// a process-local idle poll gate; this is not a transaction-commit hook.
92    fn job_enqueued(&self) {}
93
94    /// Returns the cookie encryption key used for secure cookies.
95    /// This key should be consistent across application restarts
96    /// to maintain session continuity.
97    fn cookie_key(&self) -> &CookieKey;
98
99    /// Returns the application's public HTTP(S) origin or base URL for Eyes monitors.
100    ///
101    /// Root-relative monitor targets use URL join semantics and therefore discard
102    /// any path component here (`https://example.com/app` plus `/health` becomes
103    /// `https://example.com/health`). Path-prefixed apps should declare absolute
104    /// monitor targets instead.
105    fn eyes_base_url(&self) -> Option<&str> {
106        None
107    }
108
109    /// Returns the app-level HTTP monitors reported to Eyes.
110    ///
111    /// Monitor IDs must be stable identifiers. Applications can use the cja
112    /// re-export without depending on `eyes-subscriber` directly:
113    ///
114    /// ```
115    /// use cja::eyes_subscriber::{HttpMethod, HttpMonitor};
116    /// let monitors = vec![
117    ///     HttpMonitor::new("health", "/health"),
118    ///     HttpMonitor::new("headers", "https://example.com/").method(HttpMethod::Head),
119    /// ];
120    /// assert_eq!(monitors.len(), 2);
121    /// ```
122    fn eyes_http_monitors(&self) -> Vec<crate::eyes_subscriber::HttpMonitor> {
123        Vec::new()
124    }
125}