1use axum::extract::{Path, State, WebSocketUpgrade};
2use axum::http::Method;
3use axum::response::Response;
4use axum::routing::get;
5use axum::{Json, Router};
6use ezrtc::protocol::SessionId;
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9use tower_http::cors::{Any, CorsLayer};
10
11use crate::one_to_many;
12
13#[derive(Default, Clone)]
14pub struct ServerState {
15 one_to_many_connections: one_to_many::Connections,
16 one_to_many_sessions: one_to_many::Sessions,
17 one_to_many_pings: one_to_many::Pings,
18}
19
20#[derive(Serialize, Deserialize)]
21struct RootMessage {
22 status: u8,
23 build: String,
24}
25
26#[derive(Serialize, Deserialize)]
27struct StatusMessage {
28 online: bool,
29 metadata: Option<serde_json::Value>,
30 version: Option<String>,
31}
32
33const VERSION: &str = env!("CARGO_PKG_VERSION");
34
35async fn root() -> Json<RootMessage> {
36 Json(RootMessage {
37 status: 200,
38 build: VERSION.to_string(),
39 })
40}
41
42#[allow(clippy::unused_async)]
43async fn health_handler() -> &'static str {
44 "OK"
45}
46
47#[allow(clippy::unused_async)]
48async fn one_to_many_handler(State(state): State<ServerState>, ws: WebSocketUpgrade) -> Response {
49 ws.on_upgrade(move |socket| one_to_many::user_connected(socket, state.one_to_many_connections, state.one_to_many_sessions, state.one_to_many_pings))
50}
51
52async fn status_handler(Path(session_id): Path<String>, State(state): State<ServerState>) -> Json<StatusMessage> {
53 let pings = state.one_to_many_pings.lock().unwrap().clone();
54
55 let ping = pings
57 .iter()
58 .find_map(|(_k, v)| if v.session_id == Some(SessionId::new(session_id.clone())) { Some(v.clone()) } else { None });
59
60 match ping {
61 Some(ping) => {
62 let online = ping.last_seen.elapsed() <= Duration::from_secs(120);
63 Json(StatusMessage {
64 online,
65 version: ping.version.clone(),
66 metadata: ping.metadata.clone(),
67 })
68 }
69 None => Json(StatusMessage { online: false, metadata: None, version: None }),
70 }
71}
72
73pub fn create(server_state: ServerState) -> Router {
74 Router::new()
75 .route("/health", get(health_handler))
76 .route("/", get(root))
77 .route("/one-to-many", get(one_to_many_handler))
78 .route("/status/:id", get(status_handler))
79 .layer(CorsLayer::new().allow_methods([Method::GET]).allow_origin(Any))
80 .with_state(server_state)
81}