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}