mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2024-11-24 09:10:16 -06:00
Loading/saving layouts to/from the server is functioning.
This commit is contained in:
parent
3ef76b13e0
commit
7117e0d5f1
1
.gitignore
vendored
1
.gitignore
vendored
@ -122,3 +122,4 @@ src/ShapedDevices.csv.good
|
|||||||
.gitignore
|
.gitignore
|
||||||
src/rust/lqosd/lts_keys.bin
|
src/rust/lqosd/lts_keys.bin
|
||||||
src/rust/lqosd/src/node_manager/js_build/out
|
src/rust/lqosd/src/node_manager/js_build/out
|
||||||
|
src/bin/dashboards
|
||||||
|
@ -2,5 +2,6 @@ mod run;
|
|||||||
mod static_pages;
|
mod static_pages;
|
||||||
mod template;
|
mod template;
|
||||||
mod ws;
|
mod ws;
|
||||||
|
mod localApi;
|
||||||
|
|
||||||
pub use run::spawn_webserver;
|
pub use run::spawn_webserver;
|
@ -157,9 +157,98 @@ export class Dashboard {
|
|||||||
col2.classList.add("col-6");
|
col2.classList.add("col-6");
|
||||||
col2.appendChild(this.#buildMenu());
|
col2.appendChild(this.#buildMenu());
|
||||||
|
|
||||||
|
// Themes from the server
|
||||||
|
col2.appendChild(document.createElement("hr"));
|
||||||
|
col2.appendChild(heading5Icon("cloud", "Saved Dashboard Layouts"))
|
||||||
|
let remoteList = document.createElement("select");
|
||||||
|
remoteList.id = "remoteThemes";
|
||||||
|
let remoteBtn = document.createElement("button");
|
||||||
|
remoteBtn.classList.add("btn", "btn-success");
|
||||||
|
remoteBtn.style.marginLeft = "5px";
|
||||||
|
remoteBtn.innerHTML = "<i class='fa fa-load'></i> Load Theme From Server";
|
||||||
|
remoteBtn.id = "remoteLoadBtn";
|
||||||
|
remoteBtn.onclick = () => {
|
||||||
|
let layoutName = {
|
||||||
|
theme: $('#remoteThemes').find(":selected").val().toString()
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/local-api/dashletGet",
|
||||||
|
data: JSON.stringify(layoutName),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: (data) => {
|
||||||
|
this.dashletIdentities = data;
|
||||||
|
this.#replaceDashletList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let delBtn = document.createElement("button");
|
||||||
|
delBtn.classList.add("btn", "btn-danger");
|
||||||
|
delBtn.innerHTML = "<i class='fa fa-trash'></i> Delete Remote Theme";
|
||||||
|
delBtn.style.marginLeft = "4px";
|
||||||
|
delBtn.onclick = () => {
|
||||||
|
let layoutName = $('#remoteThemes').find(":selected").val();
|
||||||
|
if (confirm("Are you sure you wish to delete " + layoutName)) {
|
||||||
|
let layoutNameObj = {
|
||||||
|
theme: layoutName.toString()
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/local-api/dashletDelete",
|
||||||
|
data: JSON.stringify(layoutNameObj),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: (data) => {
|
||||||
|
$.get("/local-api/dashletThemes", (data) => {
|
||||||
|
this.fillServerLayoutList(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
col2.appendChild(remoteList);
|
||||||
|
col2.appendChild(remoteBtn);
|
||||||
|
col2.appendChild(delBtn);
|
||||||
|
|
||||||
|
$.get("/local-api/dashletThemes", (data) => {
|
||||||
|
this.fillServerLayoutList(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save theme to the server
|
||||||
col2.appendChild(document.createElement("hr"));
|
col2.appendChild(document.createElement("hr"));
|
||||||
col2.appendChild(heading5Icon("save", "Save"));
|
col2.appendChild(heading5Icon("save", "Save"));
|
||||||
col2.appendChild(document.createElement("hr"));
|
let lbl = document.createElement("label");
|
||||||
|
lbl.htmlFor = "saveDashName";
|
||||||
|
let saveDashName = document.createElement("input");
|
||||||
|
saveDashName.id = "saveDashName";
|
||||||
|
saveDashName.type = "text";
|
||||||
|
let saveBtn = document.createElement("button");
|
||||||
|
saveBtn.type = "button";
|
||||||
|
saveBtn.classList.add("btn", "btn-success");
|
||||||
|
saveBtn.innerHTML = "<i class='fa fa-save'></i> Save to Server";
|
||||||
|
saveBtn.style.marginLeft = "4px";
|
||||||
|
saveBtn.onclick = () => {
|
||||||
|
let name = $("#saveDashName").val();
|
||||||
|
if (name.length < 1) return;
|
||||||
|
let request = {
|
||||||
|
name: name,
|
||||||
|
entries: this.dashletIdentities
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/local-api/dashletSave",
|
||||||
|
data: JSON.stringify(request),
|
||||||
|
contentType : 'application/json',
|
||||||
|
success: (data) => {
|
||||||
|
$.get("/local-api/dashletThemes", (data) => {
|
||||||
|
this.fillServerLayoutList(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
col2.appendChild(lbl);
|
||||||
|
col2.appendChild(saveDashName);
|
||||||
|
col2.appendChild(saveBtn);
|
||||||
|
|
||||||
row.appendChild(col1);
|
row.appendChild(col1);
|
||||||
row.appendChild(col2);
|
row.appendChild(col2);
|
||||||
@ -169,6 +258,28 @@ export class Dashboard {
|
|||||||
document.body.appendChild(darken);
|
document.body.appendChild(darken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fillServerLayoutList(data) {
|
||||||
|
let parent = document.getElementById("remoteThemes");
|
||||||
|
while (parent.children.length > 0) {
|
||||||
|
parent.removeChild(parent.lastChild);
|
||||||
|
}
|
||||||
|
for (let i=0; i<data.length; i++) {
|
||||||
|
let e = document.createElement("option");
|
||||||
|
e.innerText = data[i];
|
||||||
|
e.value = data[i];
|
||||||
|
parent.appendChild(e);
|
||||||
|
}
|
||||||
|
if (data.length === 0) {
|
||||||
|
let e = document.createElement("option");
|
||||||
|
e.innerText = "No Layouts Saved";
|
||||||
|
e.value = "No Layouts Saved";
|
||||||
|
parent.appendChild(e);
|
||||||
|
$("#remoteLoadBtn").prop('disabled', true);
|
||||||
|
} else {
|
||||||
|
$("#remoteLoadBtn").prop('disabled', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#clearRenderedDashboard() {
|
#clearRenderedDashboard() {
|
||||||
while (this.parentDiv.children.length > 1) {
|
while (this.parentDiv.children.length > 1) {
|
||||||
this.parentDiv.removeChild(this.parentDiv.lastChild);
|
this.parentDiv.removeChild(this.parentDiv.lastChild);
|
||||||
|
12
src/rust/lqosd/src/node_manager/localApi.rs
Normal file
12
src/rust/lqosd/src/node_manager/localApi.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
mod dashboard_themes;
|
||||||
|
|
||||||
|
use axum::Router;
|
||||||
|
use axum::routing::{get, post};
|
||||||
|
|
||||||
|
pub fn local_api() -> Router {
|
||||||
|
Router::new()
|
||||||
|
.route("/dashletThemes", get(dashboard_themes::list_themes))
|
||||||
|
.route("/dashletSave", post(dashboard_themes::save_theme))
|
||||||
|
.route("/dashletDelete", post(dashboard_themes::delete_theme))
|
||||||
|
.route("/dashletGet", post(dashboard_themes::get_theme))
|
||||||
|
}
|
84
src/rust/lqosd/src/node_manager/localApi/dashboard_themes.rs
Normal file
84
src/rust/lqosd/src/node_manager/localApi/dashboard_themes.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use axum::http::StatusCode;
|
||||||
|
use axum::Json;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use lqos_config::load_config;
|
||||||
|
|
||||||
|
pub async fn list_themes() -> Json<Vec<String>> {
|
||||||
|
if let Ok(config) = load_config() {
|
||||||
|
let base_path = std::path::Path::new(&config.lqos_directory).join("bin").join("dashboards");
|
||||||
|
if !base_path.exists() {
|
||||||
|
std::fs::create_dir(&base_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for f in std::fs::read_dir(&base_path).unwrap() {
|
||||||
|
if let Ok(f) = f {
|
||||||
|
let fs = f.file_name().to_string_lossy().to_string();
|
||||||
|
if fs.ends_with("json") {
|
||||||
|
result.push(fs.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Json(result);
|
||||||
|
}
|
||||||
|
Json(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct DashletSave {
|
||||||
|
name: String,
|
||||||
|
entries: Vec<DashletIdentity>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct DashletIdentity {
|
||||||
|
name: String,
|
||||||
|
tag: String,
|
||||||
|
size: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn save_theme(Json(data): Json<DashletSave>) -> StatusCode {
|
||||||
|
if let Ok(config) = load_config() {
|
||||||
|
let base_path = std::path::Path::new(&config.lqos_directory).join("bin").join("dashboards");
|
||||||
|
if !base_path.exists() {
|
||||||
|
std::fs::create_dir(&base_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = data.name.replace('/', "_");
|
||||||
|
let name = format!("{}.json", name);
|
||||||
|
let file_path = base_path.join(name);
|
||||||
|
let serialized = serde_json::to_string(&data).unwrap();
|
||||||
|
std::fs::write(&file_path, serialized.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusCode::OK
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ThemeSelector {
|
||||||
|
theme: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_theme(Json(f): Json<ThemeSelector>) -> StatusCode {
|
||||||
|
if let Ok(config) = load_config() {
|
||||||
|
let base_path = std::path::Path::new(&config.lqos_directory).join("bin").join("dashboards").join(&f.theme);
|
||||||
|
if base_path.exists() {
|
||||||
|
std::fs::remove_file(base_path).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusCode::OK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_theme(Json(f): Json<ThemeSelector>) -> Json<Vec<DashletIdentity>> {
|
||||||
|
if let Ok(config) = load_config() {
|
||||||
|
let base_path = std::path::Path::new(&config.lqos_directory).join("bin").join("dashboards").join(&f.theme);
|
||||||
|
if base_path.exists() {
|
||||||
|
let raw = std::fs::read_to_string(&base_path).unwrap();
|
||||||
|
let result: DashletSave = serde_json::from_str(&raw).unwrap();
|
||||||
|
return Json(result.entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Json(Vec::new())
|
||||||
|
}
|
@ -3,6 +3,7 @@ use log::info;
|
|||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crate::node_manager::{static_pages::{static_routes, vendor_route}, ws::websocket_router};
|
use crate::node_manager::{static_pages::{static_routes, vendor_route}, ws::websocket_router};
|
||||||
|
use crate::node_manager::localApi::local_api;
|
||||||
|
|
||||||
/// Launches the Axum webserver to take over node manager duties.
|
/// Launches the Axum webserver to take over node manager duties.
|
||||||
/// This is designed to be run as an independent Tokio future,
|
/// This is designed to be run as an independent Tokio future,
|
||||||
@ -15,7 +16,8 @@ pub async fn spawn_webserver() -> Result<()> {
|
|||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.nest("/", websocket_router())
|
.nest("/", websocket_router())
|
||||||
.nest("/vendor", vendor_route()?) // Serve /vendor as purely static
|
.nest("/vendor", vendor_route()?) // Serve /vendor as purely static
|
||||||
.nest("/", static_routes()?);
|
.nest("/", static_routes()?)
|
||||||
|
.nest("/local-api", local_api());
|
||||||
|
|
||||||
info!("Webserver listening on :: port 9223");
|
info!("Webserver listening on :: port 9223");
|
||||||
axum::serve(listener, router).await?;
|
axum::serve(listener, router).await?;
|
||||||
|
Loading…
Reference in New Issue
Block a user