systemprompt_runtime/context/
mod.rs1use std::sync::{Arc, OnceLock};
8
9use tokio::task::JoinHandle;
10
11use systemprompt_analytics::{AnalyticsService, FingerprintRepository, GeoIpReader};
12use systemprompt_database::DbPool;
13use systemprompt_extension::ExtensionRegistry;
14use systemprompt_marketplace::MarketplaceFilter;
15use systemprompt_mcp::services::registry::RegistryService;
16use systemprompt_models::services::SystemAdmin;
17use systemprompt_models::{AppPaths, Config, ContentConfigRaw, ContentRouting, RouteClassifier};
18use systemprompt_security::authz::SharedAuthzHook;
19use systemprompt_users::UserService;
20
21mod context_loaders;
22
23use crate::builder::AppContextBuilder;
24use crate::error::RuntimeResult;
25use crate::registry::ModuleApiRegistry;
26
27#[derive(Clone)]
42pub struct AppContext {
43 pub(crate) config: Arc<Config>,
44 pub(crate) database: DbPool,
45 pub(crate) api_registry: Arc<ModuleApiRegistry>,
46 pub(crate) extension_registry: Arc<ExtensionRegistry>,
47 pub(crate) geoip_reader: Option<GeoIpReader>,
48 pub(crate) content_config: Option<Arc<ContentConfigRaw>>,
49 pub(crate) route_classifier: Arc<RouteClassifier>,
50 pub(crate) analytics_service: Arc<AnalyticsService>,
51 pub(crate) fingerprint_repo: Option<Arc<FingerprintRepository>>,
52 pub(crate) user_service: Option<Arc<UserService>>,
53 pub(crate) app_paths: Arc<AppPaths>,
54 pub(crate) marketplace_filter: Arc<dyn MarketplaceFilter>,
55 pub(crate) event_bridge: Arc<OnceLock<JoinHandle<()>>>,
56 pub(crate) system_admin: Arc<SystemAdmin>,
57 pub(crate) mcp_registry: RegistryService,
58 pub(crate) authz_hook: SharedAuthzHook,
59}
60
61impl std::fmt::Debug for AppContext {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("AppContext")
64 .field("config", &"Config")
65 .field("database", &"DbPool")
66 .field("api_registry", &"ModuleApiRegistry")
67 .field("extension_registry", &self.extension_registry)
68 .field("geoip_reader", &self.geoip_reader.is_some())
69 .field("content_config", &self.content_config.is_some())
70 .field("route_classifier", &"RouteClassifier")
71 .field("analytics_service", &"AnalyticsService")
72 .field("fingerprint_repo", &self.fingerprint_repo.is_some())
73 .field("user_service", &self.user_service.is_some())
74 .field("app_paths", &"AppPaths")
75 .field("marketplace_filter", &self.marketplace_filter)
76 .field("event_bridge", &self.event_bridge.get().is_some())
77 .field("system_admin", &self.system_admin.username())
78 .field("mcp_registry", &"RegistryService")
79 .field("authz_hook", &"SharedAuthzHook")
80 .finish()
81 }
82}
83
84#[derive(Debug)]
90pub struct AppContextParts {
91 pub config: Arc<Config>,
92 pub database: DbPool,
93 pub api_registry: Arc<ModuleApiRegistry>,
94 pub extension_registry: Arc<ExtensionRegistry>,
95 pub geoip_reader: Option<GeoIpReader>,
96 pub content_config: Option<Arc<ContentConfigRaw>>,
97 pub route_classifier: Arc<RouteClassifier>,
98 pub analytics_service: Arc<AnalyticsService>,
99 pub fingerprint_repo: Option<Arc<FingerprintRepository>>,
100 pub user_service: Option<Arc<UserService>>,
101 pub app_paths: Arc<AppPaths>,
102 pub marketplace_filter: Arc<dyn MarketplaceFilter>,
103 pub event_bridge: Arc<OnceLock<JoinHandle<()>>>,
104 pub system_admin: Arc<SystemAdmin>,
105 pub mcp_registry: RegistryService,
106 pub authz_hook: SharedAuthzHook,
107}
108
109impl AppContext {
110 pub async fn new() -> RuntimeResult<Self> {
114 Self::builder().build().await
115 }
116
117 #[must_use]
118 pub fn builder() -> AppContextBuilder {
119 AppContextBuilder::new()
120 }
121
122 pub fn from_parts(parts: AppContextParts) -> Self {
126 Self {
127 config: parts.config,
128 database: parts.database,
129 api_registry: parts.api_registry,
130 extension_registry: parts.extension_registry,
131 geoip_reader: parts.geoip_reader,
132 content_config: parts.content_config,
133 route_classifier: parts.route_classifier,
134 analytics_service: parts.analytics_service,
135 fingerprint_repo: parts.fingerprint_repo,
136 user_service: parts.user_service,
137 app_paths: parts.app_paths,
138 marketplace_filter: parts.marketplace_filter,
139 event_bridge: parts.event_bridge,
140 system_admin: parts.system_admin,
141 mcp_registry: parts.mcp_registry,
142 authz_hook: parts.authz_hook,
143 }
144 }
145
146 pub fn load_geoip_database(config: &Config, show_warnings: bool) -> Option<GeoIpReader> {
147 context_loaders::load_geoip_database(config, show_warnings)
148 }
149
150 pub fn load_content_config(
151 config: &Config,
152 app_paths: &AppPaths,
153 ) -> Option<Arc<ContentConfigRaw>> {
154 context_loaders::load_content_config(config, app_paths)
155 }
156
157 pub fn config(&self) -> &Config {
158 &self.config
159 }
160
161 pub fn content_config(&self) -> Option<&ContentConfigRaw> {
162 self.content_config.as_ref().map(AsRef::as_ref)
163 }
164
165 pub fn content_routing(&self) -> Option<Arc<dyn ContentRouting>> {
166 let concrete = Arc::clone(self.content_config.as_ref()?);
167 let routing: Arc<dyn ContentRouting> = concrete;
168 Some(routing)
169 }
170
171 pub const fn db_pool(&self) -> &DbPool {
172 &self.database
173 }
174
175 pub const fn database(&self) -> &DbPool {
176 &self.database
177 }
178
179 pub fn api_registry(&self) -> &ModuleApiRegistry {
180 &self.api_registry
181 }
182
183 pub fn extension_registry(&self) -> &ExtensionRegistry {
184 &self.extension_registry
185 }
186
187 pub fn server_address(&self) -> String {
188 format!("{}:{}", self.config.host, self.config.port)
189 }
190
191 pub const fn geoip_reader(&self) -> Option<&GeoIpReader> {
192 self.geoip_reader.as_ref()
193 }
194
195 pub const fn analytics_service(&self) -> &Arc<AnalyticsService> {
196 &self.analytics_service
197 }
198
199 pub const fn route_classifier(&self) -> &Arc<RouteClassifier> {
200 &self.route_classifier
201 }
202
203 pub fn app_paths(&self) -> &AppPaths {
204 &self.app_paths
205 }
206
207 pub const fn app_paths_arc(&self) -> &Arc<AppPaths> {
208 &self.app_paths
209 }
210
211 pub fn marketplace_filter(&self) -> &Arc<dyn MarketplaceFilter> {
212 &self.marketplace_filter
213 }
214
215 pub const fn event_bridge(&self) -> &Arc<OnceLock<JoinHandle<()>>> {
216 &self.event_bridge
217 }
218
219 pub fn system_admin(&self) -> &SystemAdmin {
220 &self.system_admin
221 }
222
223 pub const fn mcp_registry(&self) -> &RegistryService {
224 &self.mcp_registry
225 }
226
227 pub const fn authz_hook(&self) -> &SharedAuthzHook {
228 &self.authz_hook
229 }
230}