veloren_server_cli/web/
mod.rs

1use crate::web::ui::api::UiRequestSender;
2use axum::{Router, body::Bytes, extract::State, response::IntoResponse, routing::get};
3use core::{future::Future, ops::Deref};
4use http_body_util::Full;
5use hyper::{StatusCode, header, http};
6use prometheus::{Registry, TextEncoder};
7use server::chat::ChatCache;
8use std::{future::IntoFuture, net::SocketAddr};
9
10mod chat;
11mod ui;
12
13pub async fn run<S, F, R>(
14    registry: R,
15    cache: ChatCache,
16    chat_secret: Option<String>,
17    ui_secret: String,
18    web_ui_request_s: UiRequestSender,
19    addr: S,
20    shutdown: F,
21) -> Result<(), hyper::Error>
22where
23    S: Into<SocketAddr>,
24    F: Future<Output = ()> + Send,
25    R: Deref<Target = Registry> + Clone + Send + Sync + 'static,
26{
27    let metrics = Router::new()
28        .route("/", get(metrics))
29        .with_state(registry.deref().clone());
30
31    let app = Router::new()
32        .nest("/chat/v1", chat::router(cache, chat_secret))
33        .nest(
34            "/ui_api/v1",
35            ui::api::router(web_ui_request_s, ui_secret.clone()),
36        )
37        .nest("/ui", ui::router(ui_secret))
38        .nest("/metrics", metrics)
39        .route("/health", get(|| async {}));
40
41    // run it
42    let addr = addr.into();
43    let listener = tokio::net::TcpListener::bind(addr)
44        .await
45        .expect("can't bind to web-port.");
46    tracing::info!("listening on {}", addr);
47    let server = axum::serve(
48        listener,
49        app.into_make_service_with_connect_info::<SocketAddr>(),
50    )
51    .into_future();
52    let res = tokio::select! {
53        res = server => res,
54        _ = shutdown => Ok(()),
55    };
56    match res {
57        Ok(_) => tracing::debug!("webserver shutdown successful"),
58        Err(e) => tracing::error!(?e, "webserver shutdown error"),
59    }
60
61    Ok(())
62}
63
64async fn metrics(State(registry): State<Registry>) -> Result<impl IntoResponse, StatusCode> {
65    use prometheus::Encoder;
66    let mf = registry.gather();
67    let mut buffer = Vec::with_capacity(1024);
68
69    let encoder = TextEncoder::new();
70    encoder
71        .encode(&mf, &mut buffer)
72        .expect("write to vec cannot fail");
73
74    let bytes: Bytes = buffer.into();
75
76    match http::Response::builder()
77        .status(StatusCode::OK)
78        .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
79        .body(Full::new(bytes))
80    {
81        Err(e) => {
82            tracing::warn!(?e, "could not export metrics to HTTP format");
83            Err(StatusCode::INTERNAL_SERVER_ERROR)
84        },
85        Ok(r) => Ok(r),
86    }
87}