| 505 |
505 |
|
|
| 506 |
506 |
|
/// Build the app router with all routes and middleware (minus tracing/TCP).
|
| 507 |
507 |
|
pub fn build_app(state: AppState, session_layer: SessionManagerLayer<PostgresStore>) -> Router {
|
| 508 |
|
- |
let metrics_handle = state.metrics_handle.clone();
|
| 509 |
508 |
|
// All mutation-bearing sub-routers register through `CsrfRouter`, whose
|
| 510 |
509 |
|
// `route` method only accepts `PostureMethodRouter` values produced by
|
| 511 |
510 |
|
// the `csrf::*_csrf*` helpers. Finalising the merged tree drops the
|
| 530 |
529 |
|
.merge(ota_routes())
|
| 531 |
530 |
|
.merge(build_routes())
|
| 532 |
531 |
|
.finalize();
|
| 533 |
|
- |
let mut app = Router::new()
|
|
532 |
+ |
let app = Router::new()
|
| 534 |
533 |
|
.merge(page_routes())
|
| 535 |
534 |
|
.merge(sso_routes())
|
| 536 |
535 |
|
.merge(csrf_routes)
|
| 567 |
566 |
|
.fallback(routes::custom_domain::custom_domain_fallback)
|
| 568 |
567 |
|
.with_state(state.clone());
|
| 569 |
568 |
|
|
| 570 |
|
- |
// /metrics endpoint (Prometheus scrape target). Only available when the
|
| 571 |
|
- |
// recorder is installed (i.e. in the real server, not in integration tests).
|
| 572 |
|
- |
// Protected by Bearer token matching cli_service_token.
|
| 573 |
|
- |
if let Some(handle) = metrics_handle {
|
| 574 |
|
- |
let metrics_state = state.clone();
|
| 575 |
|
- |
app = app.merge(
|
| 576 |
|
- |
Router::new()
|
| 577 |
|
- |
.route(
|
| 578 |
|
- |
"/metrics",
|
| 579 |
|
- |
axum::routing::get(
|
| 580 |
|
- |
move |axum::extract::State(prom_handle): axum::extract::State<
|
| 581 |
|
- |
metrics_exporter_prometheus::PrometheusHandle,
|
| 582 |
|
- |
>,
|
| 583 |
|
- |
headers: axum::http::HeaderMap| async move {
|
| 584 |
|
- |
use axum::response::IntoResponse;
|
| 585 |
|
- |
let token = headers
|
| 586 |
|
- |
.get("authorization")
|
| 587 |
|
- |
.and_then(|v| v.to_str().ok())
|
| 588 |
|
- |
.and_then(|v| v.strip_prefix("Bearer "));
|
| 589 |
|
- |
match (
|
| 590 |
|
- |
token,
|
| 591 |
|
- |
metrics_state
|
| 592 |
|
- |
.config
|
| 593 |
|
- |
.integrations
|
| 594 |
|
- |
.cli_service_token
|
| 595 |
|
- |
.as_deref(),
|
| 596 |
|
- |
) {
|
| 597 |
|
- |
(Some(t), Some(expected))
|
| 598 |
|
- |
if crate::helpers::constant_time_compare(t, expected) =>
|
| 599 |
|
- |
{
|
| 600 |
|
- |
prom_handle.render().into_response()
|
| 601 |
|
- |
}
|
| 602 |
|
- |
_ => axum::http::StatusCode::UNAUTHORIZED.into_response(),
|
| 603 |
|
- |
}
|
| 604 |
|
- |
},
|
| 605 |
|
- |
),
|
| 606 |
|
- |
)
|
| 607 |
|
- |
.with_state(handle),
|
| 608 |
|
- |
);
|
| 609 |
|
- |
}
|
|
569 |
+ |
// There is no /metrics scrape endpoint. Prometheus and Grafana were retired
|
|
570 |
+ |
// on 2026-07-21 and PoM is the monitoring story, so the endpoint had no
|
|
571 |
+ |
// consumer left. The recorder itself stays: the admin metrics dashboard
|
|
572 |
+ |
// renders from the same handle in-process.
|
| 610 |
573 |
|
|
| 611 |
574 |
|
// All rate limiters were registered as they were built above; start the
|
| 612 |
575 |
|
// periodic GC that sweeps their bucket maps so they don't grow unbounded for
|