veloren_server_cli/web/
mod.rs1use 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 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}