Skip to main content

o_sfu/runtime/http_server/operator/
diagnostics.rs

1//! HTTP response handlers for runtime diagnostics.
2
3use std::sync::Arc;
4
5use axum::{
6    extract::{FromRef, Path, State},
7    http::StatusCode,
8    response::{IntoResponse, Response},
9};
10
11use crate::runtime::{MediaTransport, RuntimeState, diagnostics, room::RoomManager};
12
13#[derive(Debug, Clone)]
14pub(super) struct DiagnosticsServices {
15    room_manager: Arc<RoomManager>,
16    media_transport: MediaTransport,
17}
18
19impl FromRef<RuntimeState> for DiagnosticsServices {
20    fn from_ref(state: &RuntimeState) -> Self {
21        Self {
22            room_manager: Arc::clone(&state.room_manager),
23            media_transport: state.media_transport.clone(),
24        }
25    }
26}
27
28/// diagnostics overview for room, user and publication totals
29pub(super) async fn summary(State(services): State<DiagnosticsServices>) -> Response {
30    axum::Json(
31        diagnostics::summary_response(&services.room_manager, &services.media_transport).await,
32    )
33    .into_response()
34}
35
36/// diagnostics inventory for active rooms
37pub(super) async fn rooms(State(services): State<DiagnosticsServices>) -> Response {
38    axum::Json(diagnostics::rooms_response(&services.room_manager, &services.media_transport).await)
39        .into_response()
40}
41
42/// diagnostics inventory for media workers and load pressure
43pub(super) async fn workers(State(services): State<DiagnosticsServices>) -> Response {
44    axum::Json(
45        diagnostics::workers_response(&services.room_manager, &services.media_transport).await,
46    )
47    .into_response()
48}
49
50/// Room diagnostics with users and sources.
51///
52/// Returns HTTP 404 when the room is missing and HTTP 503 when a required
53/// worker source observation is unavailable.
54pub(super) async fn room_detail(
55    State(services): State<DiagnosticsServices>,
56    Path(room_id): Path<String>,
57) -> Response {
58    let payload = diagnostics::room_detail_response(
59        &services.room_manager,
60        &services.media_transport,
61        &room_id,
62    )
63    .await;
64    payload.map_or_else(
65        |_error| StatusCode::SERVICE_UNAVAILABLE.into_response(),
66        optional_response,
67    )
68}
69
70/// user rows for one room
71pub(super) async fn room_users(
72    State(services): State<DiagnosticsServices>,
73    Path(room_id): Path<String>,
74) -> Response {
75    let payload = diagnostics::room_users_response(
76        &services.room_manager,
77        &services.media_transport,
78        &room_id,
79    )
80    .await;
81    optional_response(payload)
82}
83
84/// diagnostics for one user in one room
85pub(super) async fn user_detail(
86    State(services): State<DiagnosticsServices>,
87    Path((room_id, user_key)): Path<(String, String)>,
88) -> Response {
89    optional_response(
90        diagnostics::user_detail_response(
91            &services.room_manager,
92            &services.media_transport,
93            &room_id,
94            &user_key,
95        )
96        .await,
97    )
98}
99
100fn optional_response<T>(payload: Option<T>) -> Response
101where
102    axum::Json<T>: IntoResponse,
103{
104    payload.map_or_else(
105        || StatusCode::NOT_FOUND.into_response(),
106        |payload| axum::Json(payload).into_response(),
107    )
108}