diff options
| author | tv <tv@krebsco.de> | 2026-10-28 17:40:07 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-28 17:40:07 +0100 |
| commit | 5c79174b36f7ce077b3d1b9feafe88008696fc31 (patch) | |
| tree | f0b23629719d6a0f458f1b4d3aa5e675a8f9fa55 /src/api | |
| parent | d7959009790a422ebb17d4c4ab1d9be3d48dc511 (diff) | |
introduce relay only mode
Diffstat (limited to 'src/api')
| -rw-r--r-- | src/api/mod.rs | 23 | ||||
| -rw-r--r-- | src/api/stats.rs | 46 | ||||
| -rw-r--r-- | src/api/stats/prometheus.rs | 19 |
3 files changed, 88 insertions, 0 deletions
diff --git a/src/api/mod.rs b/src/api/mod.rs index 45eaec4..2cb0415 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -76,6 +76,29 @@ pub(crate) fn router(state: AppState, auth_state: AuthState, debug_api: bool) -> .with_state(state) } +/// The HTTP router of a node that only relays (`--relay-only`): its figures and the log +/// filter (authenticated), and `/health`. +#[cfg_attr(not(feature = "libp2p"), allow(dead_code))] +pub(crate) fn relay_only_router( + transport: std::sync::Arc<dyn crate::transport::Transport>, + auth_state: AuthState, +) -> Router { + let relay = stats::RelayOnly { + transport, + started: tokio::time::Instant::now(), + }; + Router::new() + .route( + "/admin/log", + get(log::get_log_filter).put(log::put_log_filter), + ) + .route("/admin/stats", get(stats::get_relay_only_stats)) + .layer(middleware::from_fn_with_state(auth_state, basic_auth)) + .route("/health", get(health::health)) + .layer(middleware::from_fn(json_errors)) + .with_state(relay) +} + /// An error answer: `{"error": message}` with `details`' fields added (`docs/openapi.yaml`, /// `Error`). pub(crate) fn error_response( diff --git a/src/api/stats.rs b/src/api/stats.rs index 07521ab..889b675 100644 --- a/src/api/stats.rs +++ b/src/api/stats.rs @@ -189,6 +189,52 @@ struct StabilityStats { observed_secs: u64, } +/// What `/admin/stats` of a node that only relays (`--relay-only`) says. +#[derive(Clone)] +pub(crate) struct RelayOnly { + pub(crate) transport: std::sync::Arc<dyn crate::transport::Transport>, + pub(crate) started: tokio::time::Instant, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct RelayOnlyStats { + /// The relay's libp2p peer ID. + peer_id: String, + /// Where nodes reach it, as addresses to list (`_continuum/relays`). + addresses: Vec<String>, + uptime_secs: u64, + transports: TransportStats, +} + +/// `GET /admin/stats` of a node that only relays. +#[cfg_attr(not(feature = "libp2p"), allow(dead_code))] +pub(crate) async fn get_relay_only_stats( + State(relay): State<RelayOnly>, + headers: HeaderMap, +) -> Response { + let stats = RelayOnlyStats { + peer_id: relay.transport.local_id().to_owned(), + addresses: relay + .transport + .local_addresses() + .into_iter() + .map(|address| format!("libp2p:{address}")) + .collect(), + uptime_secs: relay.started.elapsed().as_secs(), + transports: relay.transport.stats(), + }; + let accept = headers + .get(header::ACCEPT) + .and_then(|value| value.to_str().ok()); + if prometheus::wanted(accept) { + let body = prometheus::render_relay_only(&stats); + ([(header::CONTENT_TYPE, prometheus::CONTENT_TYPE)], body).into_response() + } else { + Json(stats).into_response() + } +} + /// `GET /admin/stats`. pub(crate) async fn get_stats(State(state): State<AppState>, headers: HeaderMap) -> Response { let stats = collect(&state).await; diff --git a/src/api/stats/prometheus.rs b/src/api/stats/prometheus.rs index 8b37864..690effa 100644 --- a/src/api/stats/prometheus.rs +++ b/src/api/stats/prometheus.rs @@ -363,6 +363,25 @@ fn transports(out: &mut Out, stats: &TransportStats) { } } +/// `/admin/stats` of a node that only relays. +pub(super) fn render_relay_only(stats: &super::RelayOnlyStats) -> String { + let mut out = Out::default(); + out.family( + "relay_info", + "gauge", + "This relay's libp2p peer ID (always 1); it is no node.", + ); + out.sample("relay_info", &[("peer_id", &stats.peer_id)], 1.0); + out.single( + "uptime_seconds", + "gauge", + "Seconds since this relay started.", + stats.uptime_secs as f64, + ); + transports(&mut out, &stats.transports); + out.0 +} + /// A per-peer incident counter: its name, its help and how to read it. type IncidentSeries = (&'static str, &'static str, fn(&Incidents) -> f64); |
