From 13fe329ef090fb509f79767c1fab3f41a88cc6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Wed, 13 Mar 2024 10:43:53 +0100 Subject: [PATCH] extract javascript, css and html in seperate files --- server-cli/src/web/mod.rs | 5 +- server-cli/src/web/ui.rs | 325 -------------------- server-cli/src/web/{ui_api.rs => ui/api.rs} | 0 server-cli/src/web/ui/mod.rs | 70 +++++ server-cli/src/web/ui/ui.css | 49 +++ server-cli/src/web/ui/ui.html | 93 ++++++ server-cli/src/web/ui/ui.js | 99 ++++++ 7 files changed, 313 insertions(+), 328 deletions(-) delete mode 100644 server-cli/src/web/ui.rs rename server-cli/src/web/{ui_api.rs => ui/api.rs} (100%) create mode 100644 server-cli/src/web/ui/mod.rs create mode 100644 server-cli/src/web/ui/ui.css create mode 100644 server-cli/src/web/ui/ui.html create mode 100644 server-cli/src/web/ui/ui.js diff --git a/server-cli/src/web/mod.rs b/server-cli/src/web/mod.rs index 4dde6a8b4e..de1a4bd7bb 100644 --- a/server-cli/src/web/mod.rs +++ b/server-cli/src/web/mod.rs @@ -1,4 +1,4 @@ -use crate::web::ui_api::UiRequestSender; +use crate::web::ui::api::UiRequestSender; use axum::{extract::State, response::IntoResponse, routing::get, Router}; use core::{future::Future, ops::Deref}; use hyper::{body::Body, header, http, StatusCode}; @@ -8,7 +8,6 @@ use std::net::SocketAddr; mod chat; mod ui; -mod ui_api; pub async fn run( registry: R, @@ -32,7 +31,7 @@ where .nest("/chat/v1", chat::router(cache, chat_secret)) .nest( "/ui_api/v1", - ui_api::router(web_ui_request_s, ui_secret.clone()), + ui::api::router(web_ui_request_s, ui_secret.clone()), ) .nest("/ui", ui::router(ui_secret)) .nest("/metrics", metrics) diff --git a/server-cli/src/web/ui.rs b/server-cli/src/web/ui.rs deleted file mode 100644 index fb5206dfa7..0000000000 --- a/server-cli/src/web/ui.rs +++ /dev/null @@ -1,325 +0,0 @@ -use axum::{ - extract::{ConnectInfo, State}, - http::{header::SET_COOKIE, HeaderValue}, - response::{Html, IntoResponse}, - routing::get, - Router, -}; -use std::net::SocketAddr; - -/// Keep Size small, so we dont have to Clone much for each request. -#[derive(Clone)] -struct UiApiToken { - secret_token: String, -} - -pub fn router(secret_token: String) -> Router { - let token = UiApiToken { secret_token }; - Router::new().route("/", get(ui)).with_state(token) -} - -async fn ui( - ConnectInfo(addr): ConnectInfo, - State(token): State, -) -> impl IntoResponse { - if !addr.ip().is_loopback() { - return Html( - r#" - - -Ui is only accissable from 127.0.0.1 - - - "# - .to_string(), - ) - .into_response(); - } - - let mut response = Html(format!( - r#" - - - - - - - -{} - - -"#, - javascript(), - css(), - inner() - )) - .into_response(); - - let cookie = format!("X-Secret-Token={}; SameSite=Strict", token.secret_token); - - response.headers_mut().insert( - SET_COOKIE, - HeaderValue::from_str(&cookie).expect("An invalid secret-token for ui was provided"), - ); - response -} - -fn inner() -> &'static str { - r##" -
- - - - -
- -
-
-
-

Server Name:

-
-
-

-
-
- -
-
-

Server Ip/Port:

-
-
-

-
-
- -
-
-

Require Auth:

-
-
-

Enabled

-
-
- -
-
-

Player Limit:

-
-
-

20

-

-
-
- -
-
-

Max View Distance:

-
-
-

30

-

-
-
- -
-
-

Global PvP:

-
-
-

Enable

-
-
- -
-
-

Experimental Terrain Persistence:

-
-
-

Enable

-
-
-
- -
-

Server Logs

-
-
- -
- - - -

Players

-
    -
-
- -
-

Whitelist

-

Banlist

-

Admin

-
- "## -} - -fn javascript() -> &'static str { - r#" -function openTab(evt, cityName) { - // Declare all variables - var i, tabcontent, tablinks; - - // Get all elements with class="tabcontent" and hide them - tabcontent = document.getElementsByClassName("tabcontent"); - for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = "none"; - } - - // Get all elements with class="tablinks" and remove the class "active" - tablinks = document.getElementsByClassName("tablinks"); - for (i = 0; i < tablinks.length; i++) { - tablinks[i].className = tablinks[i].className.replace(" active", ""); - } - - // Show the current tab, and add an "active" class to the button that opened the tab - document.getElementById(cityName).style.display = "block"; - evt.currentTarget.className += " active"; -} - -function changeSlider(evt, sliderId, showId) { - var slider = document.getElementById(sliderId); - var sliderNo = document.getElementById(showId); - sliderNo.innerHTML = slider.value; -} - -async function sendGlobalMsg() { - var world_msg = document.getElementById("world_msg"); - const msg_text = world_msg.value; - - const msg_response = await fetch("/ui_api/v1/send_world_msg", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - msg: msg_text - }) - }); - - if (msg_response.status == 200) { - world_msg.value = ''; - } -} - -async function update_players() { - const players_response = await fetch("/ui_api/v1/players"); - const players = await players_response.json(); - - // remove no longer existing childs - var players_list = document.getElementById("players_list"); - for (var i = players_list.children.length-1; i >= 0; i--) { - if (!players.includes(players_list.children[i].innerText)) { - console.log("remove player: " + players_list.children[i].innerText); - players_list.removeChild(players_list.children[i]); - i--; - } - } - - // add non-existing elements - addloop: for (const player of players) { - for (var i = 0; i < players_list.children.length; i++) { - if (players_list.children[i].innerText == player) { - continue addloop; - } - } - - var li = document.createElement("li"); - li.appendChild(document.createTextNode(player)); - players_list.appendChild(li); - - console.log("added player: " + player); - } -} - -async function update_logs() { - const logs_response = await fetch("/ui_api/v1/logs"); - const logs = await logs_response.json(); - - // remove no longer existing childs - var logs_list = document.getElementById("logs_list"); - while (logs_list.lastElementChild) { - logs_list.removeChild(logs_list.lastElementChild); - } - - for (const log of logs) { - var p = document.createElement("p"); - p.appendChild(document.createTextNode(log)); - logs_list.appendChild(p); - } -} - -async function loop() { - await update_players(); - await update_logs(); -} - -var loopId = window.setInterval(loop, 1000); - "# -} - -fn css() -> &'static str { - r#" - /* Style the tab */ -.tab { - overflow: hidden; - border: 1px solid #ccc; - background-color: #f1f1f1; -} - -/* Style the buttons that are used to open the tab content */ -.tab button { - background-color: inherit; - float: left; - border: none; - outline: none; - cursor: pointer; - padding: 14px 16px; - transition: 0.3s; -} - -/* Change background color of buttons on hover */ -.tab button:hover { - background-color: #ddd; -} - -/* Create an active/current tablink class */ -.tab button.active { - background-color: #ccc; -} - -/* Style the tab content */ -.tabcontent { - display: none; - padding: 6px 12px; - border: 1px solid #ccc; - border-top: none; -} -div#settings.tabcontent { - display: block; -} - -.flex-container { - display: flex; - margin: 4px; - padding: 4px; - background-color: #f1f1f1; -} - -.flex-container .first { - width: 300px -} - "# -} diff --git a/server-cli/src/web/ui_api.rs b/server-cli/src/web/ui/api.rs similarity index 100% rename from server-cli/src/web/ui_api.rs rename to server-cli/src/web/ui/api.rs diff --git a/server-cli/src/web/ui/mod.rs b/server-cli/src/web/ui/mod.rs new file mode 100644 index 0000000000..e93e9cbb76 --- /dev/null +++ b/server-cli/src/web/ui/mod.rs @@ -0,0 +1,70 @@ +use axum::{ + extract::{ConnectInfo, State}, + http::{header::SET_COOKIE, HeaderValue}, + response::{Html, IntoResponse}, + routing::get, + Router, +}; +use std::net::SocketAddr; + +pub mod api; + +/// Keep Size small, so we dont have to Clone much for each request. +#[derive(Clone)] +struct UiApiToken { + secret_token: String, +} + +pub fn router(secret_token: String) -> Router { + let token = UiApiToken { secret_token }; + Router::new().route("/", get(ui)).with_state(token) +} + +async fn ui( + ConnectInfo(addr): ConnectInfo, + State(token): State, +) -> impl IntoResponse { + if !addr.ip().is_loopback() { + return Html( + r#" + + +Ui is only accissable from 127.0.0.1 + + + "# + .to_string(), + ) + .into_response(); + } + + let js = include_str!("./ui.js"); + let css = include_str!("./ui.css"); + let inner = include_str!("./ui.html"); + + let mut response = Html(format!( + r#" + + + + + + +{inner} + +"# + )) + .into_response(); + + let cookie = format!("X-Secret-Token={}; SameSite=Strict", token.secret_token); + + response.headers_mut().insert( + SET_COOKIE, + HeaderValue::from_str(&cookie).expect("An invalid secret-token for ui was provided"), + ); + response +} diff --git a/server-cli/src/web/ui/ui.css b/server-cli/src/web/ui/ui.css new file mode 100644 index 0000000000..e6af189c72 --- /dev/null +++ b/server-cli/src/web/ui/ui.css @@ -0,0 +1,49 @@ +/* Style the tab */ +.tab { + overflow: hidden; + border: 1px solid #ccc; + background-color: #f1f1f1; +} + +/* Style the buttons that are used to open the tab content */ +.tab button { + background-color: inherit; + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 14px 16px; + transition: 0.3s; +} + +/* Change background color of buttons on hover */ +.tab button:hover { + background-color: #ddd; +} + +/* Create an active/current tablink class */ +.tab button.active { + background-color: #ccc; +} + +/* Style the tab content */ +.tabcontent { + display: none; + padding: 6px 12px; + border: 1px solid #ccc; + border-top: none; +} +div#settings.tabcontent { + display: block; +} + +.flex-container { + display: flex; + margin: 4px; + padding: 4px; + background-color: #f1f1f1; +} + +.flex-container .first { + width: 300px +} \ No newline at end of file diff --git a/server-cli/src/web/ui/ui.html b/server-cli/src/web/ui/ui.html new file mode 100644 index 0000000000..4fa97e091a --- /dev/null +++ b/server-cli/src/web/ui/ui.html @@ -0,0 +1,93 @@ +
+ + + + +
+ +
+
+
+

Server Name:

+
+
+

+
+
+ +
+
+

Server Ip/Port:

+
+
+

+
+
+ +
+
+

Require Auth:

+
+
+

Enabled

+
+
+ +
+
+

Player Limit:

+
+
+

20

+

+
+
+ +
+
+

Max View Distance:

+
+
+

30

+

+
+
+ +
+
+

Global PvP:

+
+
+

Enable

+
+
+ +
+
+

Experimental Terrain Persistence:

+
+
+

Enable

+
+
+
+ +
+

Server Logs

+
+
+ +
+ + + +

Players

+
    +
+
+ +
+

Whitelist

+

Banlist

+

Admin

+
\ No newline at end of file diff --git a/server-cli/src/web/ui/ui.js b/server-cli/src/web/ui/ui.js new file mode 100644 index 0000000000..263f3d83dd --- /dev/null +++ b/server-cli/src/web/ui/ui.js @@ -0,0 +1,99 @@ +function openTab(evt, cityName) { + // Declare all variables + var i, tabcontent, tablinks; + + // Get all elements with class="tabcontent" and hide them + tabcontent = document.getElementsByClassName("tabcontent"); + for (i = 0; i < tabcontent.length; i++) { + tabcontent[i].style.display = "none"; + } + + // Get all elements with class="tablinks" and remove the class "active" + tablinks = document.getElementsByClassName("tablinks"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].className = tablinks[i].className.replace(" active", ""); + } + + // Show the current tab, and add an "active" class to the button that opened the tab + document.getElementById(cityName).style.display = "block"; + evt.currentTarget.className += " active"; +} + +function changeSlider(evt, sliderId, showId) { + var slider = document.getElementById(sliderId); + var sliderNo = document.getElementById(showId); + sliderNo.innerHTML = slider.value; +} + +async function sendGlobalMsg() { + var world_msg = document.getElementById("world_msg"); + const msg_text = world_msg.value; + + const msg_response = await fetch("/ui_api/v1/send_world_msg", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + msg: msg_text + }) + }); + + if (msg_response.status == 200) { + world_msg.value = ''; + } +} + +async function update_players() { + const players_response = await fetch("/ui_api/v1/players"); + const players = await players_response.json(); + + // remove no longer existing childs + var players_list = document.getElementById("players_list"); + for (var i = players_list.children.length-1; i >= 0; i--) { + if (!players.includes(players_list.children[i].innerText)) { + console.log("remove player: " + players_list.children[i].innerText); + players_list.removeChild(players_list.children[i]); + i--; + } + } + + // add non-existing elements + addloop: for (const player of players) { + for (var i = 0; i < players_list.children.length; i++) { + if (players_list.children[i].innerText == player) { + continue addloop; + } + } + + var li = document.createElement("li"); + li.appendChild(document.createTextNode(player)); + players_list.appendChild(li); + + console.log("added player: " + player); + } +} + +async function update_logs() { + const logs_response = await fetch("/ui_api/v1/logs"); + const logs = await logs_response.json(); + + // remove no longer existing childs + var logs_list = document.getElementById("logs_list"); + while (logs_list.lastElementChild) { + logs_list.removeChild(logs_list.lastElementChild); + } + + for (const log of logs) { + var p = document.createElement("p"); + p.appendChild(document.createTextNode(log)); + logs_list.appendChild(p); + } +} + +async function loop() { + await update_players(); + await update_logs(); +} + +var loopId = window.setInterval(loop, 1000); \ No newline at end of file