Skip to main content

bezant_server/
state.rs

1//! Shared application state.
2
3use std::sync::Arc;
4
5use crate::events::EventsHandle;
6
7/// State shared across all axum handlers.
8#[derive(Clone)]
9pub struct AppState {
10    inner: Arc<Inner>,
11}
12
13struct Inner {
14    client: bezant::Client,
15    /// Optional token guarding the `/debug/*` endpoints. When `None`,
16    /// debug endpoints return 404. When `Some`, callers must present a
17    /// matching token via `?token=…` query string or
18    /// `X-Bezant-Debug-Token` header.
19    debug_token: Option<String>,
20    /// Handle to the optional events-capture connector. `None` disables
21    /// the `/events/*` routes (they return 503).
22    events: Option<EventsHandle>,
23}
24
25impl AppState {
26    /// Build app state from a configured [`bezant::Client`].
27    ///
28    /// Debug endpoints are disabled by default. Use
29    /// [`AppState::with_debug_token`] to enable them with token gating.
30    /// Events are disabled by default. Use [`AppState::with_events`] to
31    /// attach a connector handle.
32    #[must_use]
33    pub fn new(client: bezant::Client) -> Self {
34        Self {
35            inner: Arc::new(Inner {
36                client,
37                debug_token: None,
38                events: None,
39            }),
40        }
41    }
42
43    /// Enable the `/debug/*` endpoints, requiring the given token on
44    /// every request (via `?token=…` or `X-Bezant-Debug-Token` header).
45    /// Without this, all `/debug/*` routes 404.
46    ///
47    /// **Security:** the cookie jar holds live IBKR session cookies
48    /// — anyone who can read it can resume the IBKR session and
49    /// trade the account. Pick a long, random token (>=32 bytes
50    /// from `/dev/urandom`) and treat it like a credential.
51    #[must_use]
52    pub fn with_debug_token(client: bezant::Client, token: impl Into<String>) -> Self {
53        Self {
54            inner: Arc::new(Inner {
55                client,
56                debug_token: Some(token.into()),
57                events: None,
58            }),
59        }
60    }
61
62    /// Return a new state with the given events handle attached. The
63    /// handle is what powers `/events/*` reads. Without it, those routes
64    /// return 503.
65    #[must_use]
66    pub fn with_events(self, events: EventsHandle) -> Self {
67        let inner = Inner {
68            client: self.inner.client.clone(),
69            debug_token: self.inner.debug_token.clone(),
70            events: Some(events),
71        };
72        Self {
73            inner: Arc::new(inner),
74        }
75    }
76
77    /// Borrow the underlying Bezant client.
78    #[must_use]
79    pub fn client(&self) -> &bezant::Client {
80        &self.inner.client
81    }
82
83    /// Borrow the configured debug token, if any.
84    #[must_use]
85    pub fn debug_token(&self) -> Option<&str> {
86        self.inner.debug_token.as_deref()
87    }
88
89    /// Borrow the events handle, if attached.
90    #[must_use]
91    pub fn events(&self) -> Option<&EventsHandle> {
92        self.inner.events.as_ref()
93    }
94}