veloren_server_cli/web/ui/
mod.rs

1use axum::{
2    Router,
3    extract::{ConnectInfo, State},
4    http::{HeaderMap, HeaderValue, header::SET_COOKIE},
5    response::{Html, IntoResponse},
6    routing::get,
7};
8use std::net::SocketAddr;
9
10pub mod api;
11
12/// Keep Size small, so we dont have to Clone much for each request.
13#[derive(Clone)]
14struct UiApiToken {
15    secret_token: String,
16}
17
18pub fn router(secret_token: String) -> Router {
19    let token = UiApiToken { secret_token };
20    Router::new().route("/", get(ui)).with_state(token)
21}
22
23async fn ui(
24    ConnectInfo(addr): ConnectInfo<SocketAddr>,
25    headers: HeaderMap,
26    State(token): State<UiApiToken>,
27) -> impl IntoResponse {
28    const X_FORWARDED_FOR: &'_ str = "X-Forwarded-For";
29    if !addr.ip().is_loopback()
30        || headers.contains_key(axum::http::header::FORWARDED)
31        || headers.contains_key(X_FORWARDED_FOR)
32    {
33        return Html(
34            r#"<!DOCTYPE html>
35<html>
36<body>
37Ui is only accessible from 127.0.0.1. Usage of proxies is forbidden.
38</body>
39</html>
40        "#
41            .to_string(),
42        )
43        .into_response();
44    }
45
46    let js = include_str!("./ui.js");
47    let css = include_str!("./ui.css");
48    let inner = include_str!("./ui.html");
49
50    let mut response = Html(format!(
51        r#"<!DOCTYPE html>
52<html>
53<head>
54<script type="text/javascript">
55{js}
56</script>
57<style>
58{css}
59</style>
60</head>
61<body>
62{inner}
63</body>
64</html>"#
65    ))
66    .into_response();
67
68    let cookie = format!("X-Secret-Token={}; SameSite=Strict", token.secret_token);
69
70    //Note: at this point we give a user our secret for the Api, this is only
71    // intended for local users, protect this route against the whole internet
72    response.headers_mut().insert(
73        SET_COOKIE,
74        HeaderValue::from_str(&cookie).expect("An invalid secret-token for ui was provided"),
75    );
76    response
77}