use fraiseql_core::db::traits::DatabaseAdapter;
use tracing::info;
use super::super::Server;
use crate::routes::graphql::AppState;
impl<A: DatabaseAdapter + Clone + Send + Sync + 'static> Server<A> {
pub(super) fn build_app_state(&self) -> AppState<A> {
let mut state = AppState::new(self.executor.clone())
.with_reload_config(self.config.schema_path.clone(), self.executor.adapter().clone());
#[cfg(feature = "secrets")]
if let Some(ref secrets_manager) = self.secrets_manager {
state = state.with_secrets_manager(secrets_manager.clone());
info!("SecretsManager attached to AppState");
let field_keys: std::collections::HashMap<String, String> = self
.executor
.schema()
.types
.iter()
.flat_map(|t| t.fields.iter())
.filter_map(|f| {
f.encryption.as_ref().map(|enc| (f.name.to_string(), enc.key_reference.clone()))
})
.collect();
if !field_keys.is_empty() {
use fraiseql_secrets::encryption::{
database_adapter::DatabaseFieldAdapter, middleware::FieldEncryptionService,
};
let adapter = std::sync::Arc::new(DatabaseFieldAdapter::new(
secrets_manager.clone(),
field_keys,
));
let svc = std::sync::Arc::new(FieldEncryptionService::from_schema(
self.executor.schema(),
adapter,
));
state = state.with_field_encryption(svc);
info!("Field encryption service wired from schema");
}
}
#[cfg(feature = "federation")]
if let Some(ref cb) = self.circuit_breaker {
state = state.with_circuit_breaker(cb.clone());
info!("Federation circuit breaker attached to AppState");
}
#[cfg(feature = "observers")]
if let Some(ref runtime) = self.observer_runtime {
state = state.with_observer_runtime(runtime.clone());
info!("Observer runtime attached to AppState for health probes");
}
state = state.with_adapter_cache_enabled(self.adapter_cache_enabled);
state = state.with_usage(self.usage.clone());
state = state.with_error_sanitizer(self.error_sanitizer.clone());
if self.error_sanitizer.is_enabled() {
info!(
"Error sanitizer enabled — internal error details will be stripped from responses"
);
}
if let Some(ref api_key_auth) = self.api_key_authenticator {
state = state.with_api_key_authenticator(api_key_auth.clone());
info!("API key authenticator attached to AppState");
}
#[cfg(feature = "auth")]
match &self.state_encryption {
Some(svc) => {
state = state.with_state_encryption(svc.clone());
info!("State encryption: enabled");
},
None => {
info!("State encryption: disabled (no key configured)");
},
}
let mut validator = crate::validation::RequestValidator::new();
let runtime_vc = self.config.validation.as_ref();
let compiled_vc = self.executor.schema().validation_config.as_ref();
let effective_depth = runtime_vc
.and_then(|v| v.max_query_depth)
.or_else(|| compiled_vc.and_then(|v| v.max_query_depth));
let effective_complexity = runtime_vc
.and_then(|v| v.max_query_complexity)
.or_else(|| compiled_vc.and_then(|v| v.max_query_complexity));
if let Some(depth) = effective_depth {
validator = validator.with_max_depth(depth as usize);
let source = if runtime_vc.and_then(|v| v.max_query_depth).is_some() {
"runtime toml"
} else {
"compiled schema"
};
info!(max_query_depth = depth, source, "Query depth limit configured");
}
if let Some(complexity) = effective_complexity {
validator = validator.with_max_complexity(complexity as usize);
let source = if runtime_vc.and_then(|v| v.max_query_complexity).is_some() {
"runtime toml"
} else {
"compiled schema"
};
info!(max_query_complexity = complexity, source, "Query complexity limit configured");
}
state = state.with_validator(validator);
if let Some(ref cfg) = self.pool_tuning_config {
if cfg.enabled {
let tuner = std::sync::Arc::new(crate::pool::PoolSizingAdvisor::new(cfg.clone()));
let _handle =
std::sync::Arc::clone(&tuner).start(self.executor.adapter().clone(), None);
state = state.with_pool_tuner(tuner);
info!(
tuning_interval_ms = cfg.tuning_interval_ms,
min = cfg.min_pool_size,
max = cfg.max_pool_size,
"Pool auto-tuner started (recommendation mode)"
);
}
}
state.debug_config.clone_from(&self.executor.schema().debug_config);
state.max_get_query_bytes = self.config.max_get_query_bytes;
if let Some(ref store) = self.apq_store {
state = state.with_apq_store(store.clone());
}
if let Some(ref store) = self.trusted_docs {
state = state.with_trusted_docs(store.clone());
}
if self.config.tenancy.runtime.enabled {
use crate::{
routes::graphql::tenant_registry::TenantExecutorRegistry,
tenancy::audit::InMemoryAuditLog,
};
let registry = TenantExecutorRegistry::new(state.executor.clone());
state = state
.with_tenant_registry(std::sync::Arc::new(registry))
.with_tenant_audit_log(std::sync::Arc::new(InMemoryAuditLog::new()));
if let Some(ref factory) = self.tenant_executor_factory {
state = state.with_tenant_executor_factory(factory.clone());
}
info!(
provisioning = self.tenant_executor_factory.is_some(),
"Multi-tenant runtime enabled: tenant registry, dispatch, and \
/api/v1/admin/tenants/* mounted"
);
}
state
}
}