use axum::{Router, middleware};
use fraiseql_core::db::traits::DatabaseAdapter;
use tracing::info;
use super::super::Server;
use crate::middleware::admin_auth_middleware;
impl<A: DatabaseAdapter + Clone + Send + Sync + 'static> Server<A> {
#[cfg(feature = "observers")]
pub(super) fn add_observer_routes(&self, app: Router) -> Router {
use std::sync::Arc;
use crate::observers::{
ChangelogState, DlqState, ObserverRepository, ObserverState, RuntimeHealthState,
observer_changelog_routes, observer_dlq_routes, observer_routes,
observer_runtime_routes,
};
let Some(db_pool) = self.db_pool.clone() else {
tracing::error!(
"Observer management routes not mounted: \
the `observers` feature requires a PostgreSQL pool (`db_pool`). \
Pass `Some(sqlx::PgPool)` to Server::new() to enable observer endpoints."
);
return app;
};
let Some(ref validator) = self.oidc_validator else {
tracing::warn!(
"Observer admin API not mounted: \
the observer routes expose cluster-state mutations and bearer-token \
secrets (in actions[].headers) and so require an OIDC validator. \
Configure [auth] in fraiseql.toml to enable observer endpoints. \
The observer runtime itself (in-process triggers and dispatch) is \
unaffected — only the HTTP admin API is skipped."
);
return app;
};
let observer_state = ObserverState {
repository: ObserverRepository::new(db_pool.clone()),
};
let changelog_state = ChangelogState { pool: db_pool };
let auth_layer = || {
let auth_state = self.oidc_auth_state(Arc::clone(validator));
middleware::from_fn_with_state(auth_state, admin_auth_middleware)
};
let app = app
.nest("/api/observers", observer_routes(observer_state).route_layer(auth_layer()))
.nest(
"/api/observers",
observer_changelog_routes(changelog_state).route_layer(auth_layer()),
);
if let Some(ref runtime) = self.observer_runtime {
info!(
path = "/api/observers",
"Observer management, runtime health, and DLQ delivery status endpoints \
enabled (auth-gated)"
);
let runtime_state = RuntimeHealthState {
runtime: runtime.clone(),
};
let dlq_state = DlqState {
runtime: runtime.clone(),
};
mount_observer_runtime_routes(
app,
observer_runtime_routes(runtime_state).route_layer(auth_layer()),
observer_dlq_routes(dlq_state).route_layer(auth_layer()),
)
} else {
app
}
}
}
#[cfg(feature = "observers")]
pub(in crate::server) fn mount_observer_runtime_routes(
app: Router,
runtime_routes: Router,
dlq_routes: Router,
) -> Router {
app.nest("/api/observers", runtime_routes).nest("/api/observers", dlq_routes)
}