WASM-based websocket system, including compression and binary encoding.

This commit is contained in:
Herbert Wolverson 2023-05-20 15:36:04 +00:00
parent 423be6f1ed
commit f41e9d7261
65 changed files with 2251 additions and 893 deletions

1
.gitignore vendored
View File

@ -62,6 +62,7 @@ src/rust/long_term_stats/pgdb/.env
src/rust/long_term_stats/site_build/node_modules
src/rust/long_term_stats/site_build/output
src/rust/long_term_stats/site_build/package-lock.json
src/rust/long_term_stats/wasm_pipe/staging
# Ignore Rust build artifacts
src/rust/target

43
src/rust/Cargo.lock generated
View File

@ -2137,10 +2137,12 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"tokio-util",
"tower",
"tower-http",
"tracing",
"tracing-subscriber 0.3.17",
"wasm_pipe_types",
]
[[package]]
@ -4306,9 +4308,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.85"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4"
checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -4316,9 +4318,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.85"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822"
checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
dependencies = [
"bumpalo",
"log",
@ -4343,9 +4345,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.85"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434"
checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -4353,9 +4355,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.85"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869"
checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
dependencies = [
"proc-macro2",
"quote",
@ -4366,9 +4368,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.85"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb"
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
[[package]]
name = "wasm-streams"
@ -4383,6 +4385,27 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wasm_pipe"
version = "0.1.0"
dependencies = [
"js-sys",
"miniz_oxide",
"serde_cbor",
"serde_json",
"wasm-bindgen",
"wasm_pipe_types",
"web-sys",
]
[[package]]
name = "wasm_pipe_types"
version = "0.1.0"
dependencies = [
"chrono",
"serde",
]
[[package]]
name = "web-sys"
version = "0.3.62"

View File

@ -33,5 +33,7 @@ members = [
"long_term_stats/pgdb", # PostgreSQL interface for the LTS system
"long_term_stats/licman", # A CLI tool for managing the licensing server
"long_term_stats/lts_client", # Shared data and client-side code for long-term stats
"long_term_stats/wasm_pipe", # Provides a WebAssembly tight/compressed data pipeline
"long_term_stats/wasm_pipe_types", # Common types between the WASM conduit and the WASM server
"lqos_map_perf", # A CLI tool for testing eBPF map performance
]

View File

@ -26,3 +26,5 @@ tower = { version = "0.4", features = ["util"] }
tower-http = { version = "0.4.0", features = ["fs", "trace"] }
chrono = "0"
miniz_oxide = "0.7.1"
tokio-util = { version = "0.7.8", features = ["io"] }
wasm_pipe_types = { path = "../wasm_pipe_types" }

View File

@ -1,9 +1,13 @@
#!/bin/bash
pushd ../wasm_pipe
./build.sh
popd
pushd ../site_build
./esbuild.mjs
popd
pushd web
cp ../../site_build/output/* .
cp ../../site_build/src/main.html .
cp ../../site_build/wasm/wasm_pipe_bg.wasm .
popd
RUST_LOG=warn RUST_BACKTRACE=1 cargo run
RUST_LOG=info RUST_BACKTRACE=1 cargo run --release

View File

@ -3,9 +3,13 @@
//! should provide HTTPS.
mod wss;
use crate::web::wss::ws_handler;
use axum::body::StreamBody;
use axum::http::{header, HeaderMap};
use axum::response::IntoResponse;
use axum::{response::Html, routing::get, Router};
use pgdb::sqlx::Pool;
use pgdb::sqlx::Postgres;
use tokio_util::io::ReaderStream;
use tower_http::trace::TraceLayer;
use tower_http::trace::DefaultMakeSpan;
@ -14,6 +18,7 @@ const JS_MAP: &str = include_str!("../../web/app.js.map");
const CSS: &str = include_str!("../../web/style.css");
const CSS_MAP: &str = include_str!("../../web/style.css.map");
const HTML_MAIN: &str = include_str!("../../web/main.html");
const WASM_BODY: &[u8] = include_bytes!("../../web/wasm_pipe_bg.wasm");
pub async fn webserver(cnn: Pool<Postgres>) {
let app = Router::new()
@ -23,6 +28,7 @@ pub async fn webserver(cnn: Pool<Postgres>) {
.route("/style.css", get(css))
.route("/style.css.map", get(css_map))
.route("/ws", get(ws_handler))
.route("/wasm_pipe_bg.wasm", get(wasm_file))
.with_state(cnn)
.layer(
TraceLayer::new_for_http()
@ -67,3 +73,20 @@ async fn css_map() -> axum::response::Response<String> {
.body(CSS_MAP.to_string())
.unwrap()
}
async fn wasm_file() -> impl IntoResponse {
let mut headers = HeaderMap::new();
headers.insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/wasm"),
);
headers.insert(
header::CONTENT_DISPOSITION,
header::HeaderValue::from_static("attachment; filename=wasm_pipe_bg.wasm"),
);
axum::response::Response::builder()
.header(header::CONTENT_TYPE, header::HeaderValue::from_static("application/wasm"))
.header(header::CONTENT_DISPOSITION, header::HeaderValue::from_static("attachment; filename=wasm_pipe_bg.wasm"))
.body(StreamBody::new(ReaderStream::new(WASM_BODY)))
.unwrap()
}

View File

@ -1,7 +1,9 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
use serde_json::Value;
use wasm_pipe_types::WasmResponse;
use super::send_response;
#[derive(Debug, Serialize)]
pub struct LoginResult {
@ -11,71 +13,45 @@ pub struct LoginResult {
pub license_key: String,
}
pub async fn on_login(json: &Value, socket: &mut WebSocket, cnn: Pool<Postgres>) -> Option<LoginResult> {
if let (
Some(Value::String(license)),
Some(Value::String(username)),
Some(Value::String(password)),
) = (
json.get("license"),
json.get("username"),
json.get("password"),
) {
let login = pgdb::try_login(cnn, license, username, password).await;
if let Ok(login) = login {
let lr = LoginResult {
msg: "loginOk".to_string(),
token: login.token,
name: login.name,
license_key: license.to_string(),
};
if let Ok(login) = serde_json::to_string(&lr) {
let msg = Message::Text(login);
socket.send(msg).await.unwrap();
return Some(lr);
}
} else {
let lr = LoginResult {
msg: "loginFail".to_string(),
token: String::new(),
name: String::new(),
license_key: license.to_string(),
};
if let Ok(login) = serde_json::to_string(&lr) {
let msg = Message::Text(login);
socket.send(msg).await.unwrap();
}
}
pub async fn on_login(license: &str, username: &str, password: &str, socket: &mut WebSocket, cnn: Pool<Postgres>) -> Option<LoginResult> {
let login = pgdb::try_login(cnn, license, username, password).await;
if let Ok(login) = login {
let lr = WasmResponse::LoginOk {
token: login.token.clone(),
name: login.name.clone(),
license_key: license.to_string(),
};
send_response(socket, lr).await;
return Some(LoginResult {
msg: "Login Ok".to_string(),
token: login.token.to_string(),
name: login.name.to_string(),
license_key: license.to_string(),
});
} else {
let lr = WasmResponse::LoginFail;
send_response(socket, lr).await;
}
None
None
}
pub async fn on_token_auth(json: &Value, socket: &mut WebSocket, cnn: Pool<Postgres>) -> Option<LoginResult> {
let token_id = json.get("token").unwrap().as_str().unwrap();
pub async fn on_token_auth(token_id: &str, socket: &mut WebSocket, cnn: Pool<Postgres>) -> Option<LoginResult> {
let login = pgdb::token_to_credentials(cnn, token_id).await;
if let Ok(login) = login {
let lr = LoginResult {
msg: "authOk".to_string(),
token: login.token,
name: login.name,
license_key: login.license,
let lr = WasmResponse::AuthOk {
token: login.token.clone(),
name: login.name.clone(),
license_key: login.license.clone(),
};
if let Ok(login) = serde_json::to_string(&lr) {
let msg = Message::Text(login);
socket.send(msg).await.unwrap();
return Some(lr);
}
send_response(socket, lr).await;
return Some(LoginResult {
msg: "Login Ok".to_string(),
token: login.token.to_string(),
name: login.name.to_string(),
license_key: login.license.to_string(),
});
} else {
let lr = LoginResult {
msg: "authFail".to_string(),
token: String::new(),
name: String::new(),
license_key: String::new(),
};
if let Ok(login) = serde_json::to_string(&lr) {
let msg = Message::Text(login);
socket.send(msg).await.unwrap();
}
send_response(socket, WasmResponse::AuthFail).await;
}
None
}

View File

@ -3,17 +3,17 @@ use crate::web::wss::queries::{
send_packets_for_node, send_perf_for_node, send_rtt_for_all_nodes, send_rtt_for_all_nodes_site,
send_rtt_for_node, send_site_info, send_site_parents, send_throughput_for_all_nodes,
send_throughput_for_all_nodes_by_site, send_throughput_for_node, site_heat_map,
site_tree::send_site_tree, send_throughput_for_all_nodes_by_circuit, send_rtt_for_all_nodes_circuit, send_site_stack_map,
site_tree::send_site_tree, send_throughput_for_all_nodes_by_circuit, send_rtt_for_all_nodes_circuit, send_site_stack_map, time_period::InfluxTimePeriod,
};
use axum::{
extract::{
ws::{WebSocket, WebSocketUpgrade},
ws::{WebSocket, WebSocketUpgrade, Message},
State,
},
response::IntoResponse,
};
use pgdb::sqlx::{Pool, Postgres};
use serde_json::Value;
use wasm_pipe_types::{WasmRequest, WasmResponse};
mod login;
mod nodes;
mod queries;
@ -31,8 +31,308 @@ async fn handle_socket(mut socket: WebSocket, cnn: Pool<Postgres>) {
while let Some(msg) = socket.recv().await {
let cnn = cnn.clone();
let msg = msg.unwrap();
// Get the binary message and decompress it
log::info!("Received a message: {:?}", msg);
if let Ok(text) = msg.into_text() {
let raw = msg.into_data();
let uncompressed = miniz_oxide::inflate::decompress_to_vec(&raw).unwrap();
let msg = lts_client::cbor::from_slice::<WasmRequest>(&uncompressed).unwrap();
log::info!("{msg:?}");
// Update the token credentials (if there are any)
if let Some(credentials) = &credentials {
let _ = pgdb::refresh_token(cnn.clone(), &credentials.token).await;
}
// Handle the message by type
match msg {
// Handle login with just a token
WasmRequest::Auth { token } => {
let result = login::on_token_auth(&token, &mut socket, cnn).await;
if let Some(result) = result {
credentials = Some(result);
}
}
// Handle login with a username and password
WasmRequest::Login { license, username, password } => {
let result = login::on_login(&license, &username, &password, &mut socket, cnn).await;
if let Some(result) = result {
credentials = Some(result);
}
}
// Node status for dashboard
WasmRequest::GetNodeStatus => {
if let Some(credentials) = &credentials {
nodes::node_status(
cnn.clone(),
&mut socket,
&credentials.license_key,
)
.await;
} else {
log::info!("Node status requested but no credentials provided");
}
}
// Packet chart for dashboard
WasmRequest::PacketChart { period } => {
if let Some(credentials) = &credentials {
let _ = send_packets_for_all_nodes(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
// Packet chart for individual node
WasmRequest::PacketChartSingle { period, node_id, node_name } => {
if let Some(credentials) = &credentials {
let _ = send_packets_for_node(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
node_id,
node_name,
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
// Throughput chart for the dashboard
WasmRequest::ThroughputChart { period } => {
if let Some(credentials) = &credentials {
let _ = send_throughput_for_all_nodes(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
// Throughput chart for a single shaper node
WasmRequest::ThroughputChartSingle { period, node_id, node_name } => {
if let Some(credentials) = &credentials {
let _ = send_throughput_for_node(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
node_id,
node_name,
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
},
WasmRequest::ThroughputChartSite { period, site_id } => {
if let Some(credentials) = &credentials {
let _ = send_throughput_for_all_nodes_by_site(
cnn.clone(),
&mut socket,
&credentials.license_key,
site_id,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
WasmRequest::ThroughputChartCircuit { period, circuit_id } => {
if let Some(credentials) = &credentials {
let _ = send_throughput_for_all_nodes_by_circuit(
cnn.clone(),
&mut socket,
&credentials.license_key,
circuit_id,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
// Rtt Chart
WasmRequest::RttChart { period } => {
if let Some(credentials) = &credentials {
let _ = send_rtt_for_all_nodes(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
WasmRequest::RttChartSite { period, site_id } => {
if let Some(credentials) = &credentials {
let _ = send_rtt_for_all_nodes_site(
cnn.clone(),
&mut socket,
&credentials.license_key,
site_id,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
WasmRequest::RttChartSingle { period, node_id, node_name } => {
if let Some(credentials) = &credentials {
let _ = send_rtt_for_node(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
node_id,
node_name,
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
WasmRequest::RttChartCircuit { period, circuit_id } => {
if let Some(credentials) = &credentials {
let _ = send_rtt_for_all_nodes_circuit(
cnn.clone(),
&mut socket,
&credentials.license_key,
circuit_id,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
// Site Stack
WasmRequest::SiteStack { period, site_id } => {
if let Some(credentials) = &credentials {
let _ = send_site_stack_map(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
site_id,
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
},
WasmRequest::RootHeat { period } => {
if let Some(credentials) = &credentials {
let _ = root_heat_map(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
},
WasmRequest::SiteHeat { period, site_id } => {
if let Some(credentials) = &credentials {
let _ = site_heat_map(
cnn.clone(),
&mut socket,
&credentials.license_key,
&site_id,
InfluxTimePeriod::new(&period),
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
}
WasmRequest::NodePerfChart { period, node_id, node_name } => {
if let Some(credentials) = &credentials {
let _ = send_perf_for_node(
cnn.clone(),
&mut socket,
&credentials.license_key,
InfluxTimePeriod::new(&period),
node_id,
node_name,
)
.await;
} else {
log::info!("Throughput requested but no credentials provided");
}
},
WasmRequest::Tree { parent } => {
if let Some(credentials) = &credentials {
send_site_tree(
cnn.clone(),
&mut socket,
&credentials.license_key,
&parent,
)
.await;
}
},
WasmRequest::SiteInfo { site_id } => {
if let Some(credentials) = &credentials {
send_site_info(
cnn.clone(),
&mut socket,
&credentials.license_key,
&site_id,
)
.await;
}
}
WasmRequest::SiteParents { site_id } => {
if let Some(credentials) = &credentials {
send_site_parents(
cnn.clone(),
&mut socket,
&credentials.license_key,
&site_id,
)
.await;
}
}
WasmRequest::Search { term } => {
if let Some(credentials) = &credentials {
let _ = omnisearch(
cnn.clone(),
&mut socket,
&credentials.license_key,
&term,
)
.await;
}
}
WasmRequest::CircuitInfo { circuit_id } => {
if let Some(credentials) = &credentials {
send_circuit_info(
cnn.clone(),
&mut socket,
&credentials.license_key,
&circuit_id,
)
.await;
}
}
}
/*if let Ok(text) = msg.into_text() {
let json = serde_json::from_str::<Value>(&text);
if json.is_err() {
log::warn!("Unable to parse JSON: {}", json.err().unwrap());
@ -331,6 +631,16 @@ async fn handle_socket(mut socket: WebSocket, cnn: Pool<Postgres>) {
}
}
}
}
}*/
}
}
fn serialize_resposne(response: WasmResponse) -> Vec<u8> {
let cbor = lts_client::cbor::to_vec(&response).unwrap();
miniz_oxide::deflate::compress_to_vec(&cbor, 8)
}
pub async fn send_response(socket: &mut WebSocket, response: WasmResponse) {
let serialized = serialize_resposne(response);
socket.send(Message::Binary(serialized)).await.unwrap();
}

View File

@ -1,27 +1,14 @@
use axum::extract::ws::WebSocket;
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
use wasm_pipe_types::Node;
#[derive(Serialize)]
struct NodeStatus {
msg: String,
nodes: Vec<Node>,
}
use crate::web::wss::send_response;
#[derive(Serialize)]
struct Node {
node_id: String,
node_name: String,
last_seen: i32,
}
impl From<pgdb::NodeStatus> for Node {
fn from(ns: pgdb::NodeStatus) -> Self {
Self {
node_id: ns.node_id,
node_name: ns.node_name,
last_seen: ns.last_seen,
}
fn convert(ns: pgdb::NodeStatus) -> Node {
Node {
node_id: ns.node_id,
node_name: ns.node_name,
last_seen: ns.last_seen,
}
}
@ -30,13 +17,8 @@ pub async fn node_status(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str)
let nodes = pgdb::node_status(cnn, key).await;
match nodes {
Ok(nodes) => {
let nodes: Vec<Node> = nodes.into_iter().map(|n| n.into()).collect();
let status = NodeStatus {
msg: "nodeStatus".to_string(),
nodes};
let reply = serde_json::to_string(&status).unwrap();
let msg = axum::extract::ws::Message::Text(reply);
socket.send(msg).await.unwrap();
let nodes: Vec<Node> = nodes.into_iter().map(convert).collect();
send_response(socket, wasm_pipe_types::WasmResponse::NodeStatus { nodes }).await;
},
Err(e) => {
log::error!("Unable to obtain node status: {}", e);

View File

@ -1,58 +1,29 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
use wasm_pipe_types::CircuitList;
#[derive(Serialize)]
struct CircuitInfoMessage {
msg: String,
data: Vec<CircuitList>,
}
use crate::web::wss::send_response;
#[derive(Serialize)]
pub struct CircuitList {
pub circuit_name: String,
pub device_id: String,
pub device_name: String,
pub parent_node: String,
pub mac: String,
pub download_min_mbps: i32,
pub download_max_mbps: i32,
pub upload_min_mbps: i32,
pub upload_max_mbps: i32,
pub comment: String,
pub ip_range: String,
pub subnet: i32,
}
impl From<pgdb::CircuitInfo> for CircuitList {
fn from(circuit: pgdb::CircuitInfo) -> Self {
Self {
circuit_name: circuit.circuit_name,
device_id: circuit.device_id,
device_name: circuit.device_name,
parent_node: circuit.parent_node,
mac: circuit.mac,
download_min_mbps: circuit.download_min_mbps,
download_max_mbps: circuit.download_max_mbps,
upload_min_mbps: circuit.upload_min_mbps,
upload_max_mbps: circuit.upload_max_mbps,
comment: circuit.comment,
ip_range: circuit.ip_range,
subnet: circuit.subnet,
}
fn from(circuit: pgdb::CircuitInfo) -> CircuitList {
CircuitList {
circuit_name: circuit.circuit_name,
device_id: circuit.device_id,
device_name: circuit.device_name,
parent_node: circuit.parent_node,
mac: circuit.mac,
download_min_mbps: circuit.download_min_mbps,
download_max_mbps: circuit.download_max_mbps,
upload_min_mbps: circuit.upload_min_mbps,
upload_max_mbps: circuit.upload_max_mbps,
comment: circuit.comment,
ip_range: circuit.ip_range,
subnet: circuit.subnet,
}
}
pub async fn send_circuit_info(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, circuit_id: &str) {
if let Ok(hosts) = pgdb::get_circuit_info(cnn, key, circuit_id).await {
let hosts = hosts.into_iter().map(CircuitList::from).collect::<Vec<_>>();
let msg = CircuitInfoMessage {
msg: "circuit_info".to_string(),
data: hosts,
};
let json = serde_json::to_string(&msg).unwrap();
if let Err(e) = socket.send(Message::Text(json)).await {
tracing::error!("Error sending message: {}", e);
}
let hosts = hosts.into_iter().map(from).collect::<Vec<_>>();
send_response(socket, wasm_pipe_types::WasmResponse::CircuitInfo { data: hosts }).await;
}
}

View File

@ -1,9 +1,9 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use chrono::{DateTime, FixedOffset, Utc};
use influxdb2::{Client, FromDataPoint, models::Query};
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
use crate::submissions::get_org_details;
use wasm_pipe_types::{PerfHost, Perf};
use crate::{submissions::get_org_details, web::wss::send_response};
use super::time_period::InfluxTimePeriod;
#[derive(Debug, FromDataPoint)]
@ -27,27 +27,6 @@ impl Default for PerfRow {
}
}
#[derive(Serialize, Debug)]
pub struct PerfHost {
pub node_id: String,
pub node_name: String,
pub stats: Vec<Perf>,
}
#[derive(Serialize, Debug)]
pub struct Perf {
pub date: String,
pub cpu: f64,
pub cpu_max: f64,
pub ram: f64,
}
#[derive(Serialize, Debug)]
pub struct PacketChart {
pub msg: String,
pub nodes: Vec<PerfHost>,
}
pub async fn send_perf_for_node(
cnn: Pool<Postgres>,
socket: &mut WebSocket,
@ -57,13 +36,7 @@ pub async fn send_perf_for_node(
node_name: String,
) -> anyhow::Result<()> {
let node = get_perf_for_node(cnn, key, node_id, node_name, period).await?;
let chart = PacketChart {
msg: "nodePerfChart".to_string(),
nodes: vec![node],
};
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::NodePerfChart { nodes: vec![node] }).await;
Ok(())
}

View File

@ -1,29 +1,24 @@
//! Packet-per-second data queries
mod packet_host;
mod packet_row;
use self::{packet_host::{Packets, PacketHost, PacketChart}, packet_row::PacketRow};
use crate::submissions::get_org_details;
use axum::extract::ws::{WebSocket, Message};
use self::packet_row::PacketRow;
use crate::{submissions::get_org_details, web::wss::send_response};
use axum::extract::ws::WebSocket;
use futures::future::join_all;
use influxdb2::{models::Query, Client};
use pgdb::sqlx::{Pool, Postgres};
use wasm_pipe_types::{PacketHost, Packets};
use super::time_period::InfluxTimePeriod;
pub async fn send_packets_for_all_nodes(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, period: InfluxTimePeriod) -> anyhow::Result<()> {
let nodes = get_packets_for_all_nodes(cnn, key, period).await?;
let chart = PacketChart { msg: "packetChart".to_string(), nodes };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::PacketChart { nodes }).await;
Ok(())
}
pub async fn send_packets_for_node(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, period: InfluxTimePeriod, node_id: String, node_name: String) -> anyhow::Result<()> {
let node = get_packets_for_node(cnn, key, node_id, node_name, period).await?;
let chart = PacketChart { msg: "packetChart".to_string(), nodes: vec![node] };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::PacketChart { nodes: vec![node] }).await;
Ok(())
}

View File

@ -1,23 +0,0 @@
use serde::Serialize;
#[derive(Serialize, Debug)]
pub struct PacketHost {
pub node_id: String,
pub node_name: String,
pub down: Vec<Packets>,
pub up: Vec<Packets>,
}
#[derive(Serialize, Debug)]
pub struct Packets {
pub value: f64,
pub date: String,
pub l: f64,
pub u: f64,
}
#[derive(Serialize, Debug)]
pub struct PacketChart {
pub msg: String,
pub nodes: Vec<PacketHost>,
}

View File

@ -1,13 +1,13 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use futures::future::join_all;
use influxdb2::{Client, models::Query};
use pgdb::sqlx::{Pool, Postgres};
use crate::{submissions::get_org_details, web::wss::queries::rtt::rtt_row::RttCircuitRow};
use self::{rtt_row::{RttRow, RttSiteRow}, rtt_host::{Rtt, RttHost, RttChart}};
use wasm_pipe_types::{RttHost, Rtt};
use crate::{submissions::get_org_details, web::wss::{queries::rtt::rtt_row::RttCircuitRow, send_response}};
use self::rtt_row::{RttRow, RttSiteRow};
use super::time_period::InfluxTimePeriod;
mod rtt_row;
mod rtt_host;
pub async fn send_rtt_for_all_nodes(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, period: InfluxTimePeriod) -> anyhow::Result<()> {
let nodes = get_rtt_for_all_nodes(cnn, key, period).await?;
@ -19,10 +19,8 @@ pub async fn send_rtt_for_all_nodes(cnn: Pool<Postgres>, socket: &mut WebSocket,
histogram[bucket] += 1;
}
}
send_response(socket, wasm_pipe_types::WasmResponse::RttChart { nodes, histogram }).await;
let chart = RttChart { msg: "rttChart".to_string(), nodes, histogram };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
Ok(())
}
@ -37,9 +35,7 @@ pub async fn send_rtt_for_all_nodes_site(cnn: Pool<Postgres>, socket: &mut WebSo
}
}
let chart = RttChart { msg: "rttChartSite".to_string(), nodes, histogram };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::RttChartSite { nodes, histogram }).await;
Ok(())
}
@ -54,9 +50,7 @@ pub async fn send_rtt_for_all_nodes_circuit(cnn: Pool<Postgres>, socket: &mut We
}
}
let chart = RttChart { msg: "rttChartCircuit".to_string(), nodes, histogram };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::RttChartCircuit { nodes, histogram }).await;
Ok(())
}
@ -72,9 +66,7 @@ pub async fn send_rtt_for_node(cnn: Pool<Postgres>, socket: &mut WebSocket, key:
}
}
let chart = RttChart { msg: "rttChart".to_string(), nodes, histogram };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::RttChart { nodes, histogram }).await;
Ok(())
}

View File

@ -1,23 +0,0 @@
use serde::Serialize;
#[derive(Serialize, Debug)]
pub struct Rtt {
pub value: f64,
pub date: String,
pub l: f64,
pub u: f64,
}
#[derive(Serialize, Debug)]
pub struct RttHost {
pub node_id: String,
pub node_name: String,
pub rtt: Vec<Rtt>,
}
#[derive(Serialize, Debug)]
pub struct RttChart {
pub msg: String,
pub nodes: Vec<RttHost>,
pub histogram: Vec<u32>,
}

View File

@ -1,6 +1,8 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
use wasm_pipe_types::SearchResult;
use crate::web::wss::send_response;
pub async fn omnisearch(
cnn: Pool<Postgres>,
@ -23,31 +25,11 @@ pub async fn omnisearch(
hits.dedup_by(|a,b| a.name == b.name && a.url == b.url);
hits.sort_by(|a,b| a.score.partial_cmp(&b.score).unwrap());
let msg = SearchMessage {
msg: "search".to_string(),
hits
};
log::warn!("{msg:?}");
let json = serde_json::to_string(&msg).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::SearchResult { hits }).await;
Ok(())
}
#[derive(Serialize, Debug)]
struct SearchMessage {
msg: String,
hits: Vec<SearchResult>,
}
#[derive(Serialize, Debug)]
struct SearchResult {
name: String,
url: String,
score: f64,
icon: String,
}
async fn search_devices(
cnn: Pool<Postgres>,
key: &str,

View File

@ -1,12 +1,14 @@
use super::time_period::InfluxTimePeriod;
use crate::submissions::get_org_details;
use axum::extract::ws::{Message, WebSocket};
use crate::web::wss::send_response;
use axum::extract::ws::WebSocket;
use chrono::{DateTime, FixedOffset, Utc};
use influxdb2::Client;
use influxdb2::{models::Query, FromDataPoint};
use pgdb::OrganizationDetails;
use pgdb::sqlx::{query, Pool, Postgres, Row};
use serde::Serialize;
use wasm_pipe_types::WasmResponse;
use std::collections::HashMap;
pub async fn root_heat_map(
@ -69,12 +71,7 @@ pub async fn root_heat_map(
sorter.insert(row.node_name.clone(), vec![(row.time, row.rtt_avg)]);
}
}
let msg = HeatMessage {
msg: "rootHeat".to_string(),
data: sorter,
};
let json = serde_json::to_string(&msg).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, WasmResponse::RootHeat { data: sorter}).await;
}
}
}
@ -232,13 +229,7 @@ pub async fn site_heat_map(
}
site_circuits_heat_map(cnn, key, site_name, period, &mut sorter, client, &org).await?;
let msg = HeatMessage {
msg: "siteHeat".to_string(),
data: sorter,
};
let json = serde_json::to_string(&msg).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, WasmResponse::SiteHeat { data: sorter }).await;
}
}
}

View File

@ -1,7 +1,9 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
use super::site_tree::SiteTree;
use wasm_pipe_types::{SiteTree, WasmResponse};
use crate::web::wss::send_response;
use super::site_tree::tree_to_host;
#[derive(Serialize)]
struct SiteInfoMessage {
@ -12,14 +14,7 @@ struct SiteInfoMessage {
pub async fn send_site_info(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, site_id: &str) {
if let Ok(host) = pgdb::get_site_info(cnn, key, site_id).await {
let host = SiteTree::from(host);
let msg = SiteInfoMessage {
msg: "site_info".to_string(),
data: host,
};
let json = serde_json::to_string(&msg).unwrap();
if let Err(e) = socket.send(Message::Text(json)).await {
tracing::error!("Error sending message: {}", e);
}
let host = tree_to_host(host);
send_response(socket, WasmResponse::SiteInfo { data: host }).await;
}
}

View File

@ -1,6 +1,7 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use pgdb::sqlx::{Pool, Postgres};
use serde::Serialize;
use crate::web::wss::send_response;
pub async fn send_site_parents(
cnn: Pool<Postgres>,
@ -9,39 +10,13 @@ pub async fn send_site_parents(
site_name: &str,
) {
if let Ok(parents) = pgdb::get_parent_list(cnn.clone(), key, site_name).await {
let msg = TreeMessage {
msg: "site_parents".to_string(),
data: parents,
};
let json = serde_json::to_string(&msg).unwrap();
if let Err(e) = socket.send(Message::Text(json)).await {
tracing::error!("Error sending message: {}", e);
}
send_response(socket, wasm_pipe_types::WasmResponse::SiteParents { data: parents }).await;
}
let child_result = pgdb::get_child_list(cnn, key, site_name).await;
if let Ok(children) = child_result {
let msg = TreeChildMessage {
msg: "site_children".to_string(),
data: children,
};
let json = serde_json::to_string(&msg).unwrap();
if let Err(e) = socket.send(Message::Text(json)).await {
tracing::error!("Error sending message: {}", e);
}
send_response(socket, wasm_pipe_types::WasmResponse::SiteChildren { data: children }).await;
} else {
log::error!("Error getting children: {:?}", child_result);
}
}
#[derive(Serialize)]
struct TreeMessage {
msg: String,
data: Vec<(String, String)>,
}
#[derive(Serialize)]
struct TreeChildMessage {
msg: String,
data: Vec<(String, String, String)>,
}

View File

@ -1,57 +1,31 @@
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use pgdb::{
sqlx::{Pool, Postgres},
TreeNode,
};
use serde::Serialize;
use wasm_pipe_types::SiteTree;
use crate::web::wss::send_response;
pub async fn send_site_tree(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, parent: &str) {
let tree = pgdb::get_site_tree(cnn, key, parent).await.unwrap();
let tree = tree
.into_iter()
.map(|row| row.into())
.map(tree_to_host)
.collect::<Vec<SiteTree>>();
let msg = TreeMessage {
msg: "site_tree".to_string(),
data: tree,
};
let json = serde_json::to_string(&msg).unwrap();
if let Err(e) = socket.send(Message::Text(json)).await {
tracing::error!("Error sending message: {}", e);
send_response(socket, wasm_pipe_types::WasmResponse::SiteTree { data: tree }).await;
}
pub(crate) fn tree_to_host(row: TreeNode) -> SiteTree {
SiteTree {
index: row.index,
site_name: row.site_name,
site_type: row.site_type,
parent: row.parent,
max_down: row.max_down,
max_up: row.max_up,
current_down: row.current_down,
current_up: row.current_up,
current_rtt: row.current_rtt,
}
}
#[derive(Serialize)]
pub struct SiteTree {
pub index: i32,
pub site_name: String,
pub site_type: String,
pub parent: i32,
pub max_down: i32,
pub max_up: i32,
pub current_down: i32,
pub current_up: i32,
pub current_rtt: i32,
}
impl From<TreeNode> for SiteTree {
fn from(row: TreeNode) -> Self {
SiteTree {
index: row.index,
site_name: row.site_name,
site_type: row.site_type,
parent: row.parent,
max_down: row.max_down,
max_up: row.max_up,
current_down: row.current_down,
current_up: row.current_up,
current_rtt: row.current_rtt,
}
}
}
#[derive(Serialize)]
struct TreeMessage {
msg: String,
data: Vec<SiteTree>,
}

View File

@ -1,49 +1,38 @@
use std::collections::HashMap;
mod site_stack;
use axum::extract::ws::{WebSocket, Message};
use axum::extract::ws::WebSocket;
use futures::future::join_all;
use influxdb2::{Client, models::Query};
use pgdb::sqlx::{Pool, Postgres};
use crate::submissions::get_org_details;
use self::{throughput_host::{ThroughputHost, Throughput, ThroughputChart}, throughput_row::{ThroughputRow, ThroughputRowBySite, ThroughputRowByCircuit}};
use wasm_pipe_types::{ThroughputHost, Throughput};
use crate::{submissions::get_org_details, web::wss::send_response};
use self::throughput_row::{ThroughputRow, ThroughputRowBySite, ThroughputRowByCircuit};
use super::time_period::InfluxTimePeriod;
mod throughput_host;
mod throughput_row;
pub use site_stack::send_site_stack_map;
pub async fn send_throughput_for_all_nodes(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, period: InfluxTimePeriod) -> anyhow::Result<()> {
let nodes = get_throughput_for_all_nodes(cnn, key, period).await?;
let chart = ThroughputChart { msg: "bitsChart".to_string(), nodes };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::BitsChart { nodes }).await;
Ok(())
}
pub async fn send_throughput_for_all_nodes_by_site(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, site_name: String, period: InfluxTimePeriod) -> anyhow::Result<()> {
let nodes = get_throughput_for_all_nodes_by_site(cnn, key, period, &site_name).await?;
let chart = ThroughputChart { msg: "bitsChartSite".to_string(), nodes };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::BitsChart { nodes }).await;
Ok(())
}
pub async fn send_throughput_for_all_nodes_by_circuit(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, circuit_id: String, period: InfluxTimePeriod) -> anyhow::Result<()> {
let nodes = get_throughput_for_all_nodes_by_circuit(cnn, key, period, &circuit_id).await?;
let chart = ThroughputChart { msg: "bitsChartCircuit".to_string(), nodes };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::BitsChart { nodes }).await;
Ok(())
}
pub async fn send_throughput_for_node(cnn: Pool<Postgres>, socket: &mut WebSocket, key: &str, period: InfluxTimePeriod, node_id: String, node_name: String) -> anyhow::Result<()> {
let node = get_throughput_for_node(cnn, key, node_id, node_name, period).await?;
let chart = ThroughputChart { msg: "bitsChart".to_string(), nodes: vec![node] };
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::BitsChart { nodes: vec![node] }).await;
Ok(())
}

View File

@ -1,7 +1,7 @@
use crate::web::wss::queries::{
throughput::throughput_host::ThroughputChart, time_period::InfluxTimePeriod,
};
use axum::extract::ws::{Message, WebSocket};
use crate::web::wss::{queries::{
time_period::InfluxTimePeriod,
}, send_response};
use axum::extract::ws::WebSocket;
use pgdb::sqlx::{Pool, Postgres, Row};
use super::{get_throughput_for_all_nodes_by_circuit, get_throughput_for_all_nodes_by_site};
@ -58,12 +58,7 @@ pub async fn send_site_stack_map(
}
//println!("{result:?}");
let chart = ThroughputChart {
msg: "siteStack".to_string(),
nodes: result,
};
let json = serde_json::to_string(&chart).unwrap();
socket.send(Message::Text(json)).await.unwrap();
send_response(socket, wasm_pipe_types::WasmResponse::SiteStack { nodes: result }).await;
Ok(())
}

View File

@ -1,23 +0,0 @@
use serde::Serialize;
#[derive(Serialize, Debug, Clone)]
pub struct ThroughputHost {
pub node_id: String,
pub node_name: String,
pub down: Vec<Throughput>,
pub up: Vec<Throughput>,
}
#[derive(Serialize, Debug, Clone)]
pub struct Throughput {
pub value: f64,
pub date: String,
pub l: f64,
pub u: f64,
}
#[derive(Serialize, Debug)]
pub struct ThroughputChart {
pub msg: String,
pub nodes: Vec<ThroughputHost>,
}

View File

@ -1,5 +1,3 @@
use serde_json::Value;
#[derive(Clone)]
pub struct InfluxTimePeriod {
start: String,
@ -7,41 +5,34 @@ pub struct InfluxTimePeriod {
}
impl InfluxTimePeriod {
pub fn new(period: Option<Value>) -> Self {
if let Some(period) = period {
let start = match period.as_str() {
Some("5m") => "-5m",
Some("15m") => "-15m",
Some("1h") => "-60m",
Some("6h") => "-360m",
Some("12h") => "-720m",
Some("24h") => "-1440m",
Some("7d") => "-10080m",
Some("28d") => "-40320m",
_ => "-5m",
};
pub fn new(period: &str) -> Self {
let start = match period {
"5m" => "-5m",
"15m" => "-15m",
"1h" => "-60m",
"6h" => "-360m",
"12h" => "-720m",
"24h" => "-1440m",
"7d" => "-10080m",
"28d" => "-40320m",
_ => "-5m",
};
let aggregate = match period.as_str() {
Some("5m") => "10s",
Some("15m") => "10s",
Some("1h") => "10s",
Some("6h") => "1m",
Some("12h") => "2m",
Some("24h") => "4m",
Some("7d") => "30m",
Some("28d") => "1h",
_ => "10s"
};
let aggregate = match period {
"5m" => "10s",
"15m" => "10s",
"1h" => "10s",
"6h" => "1m",
"12h" => "2m",
"24h" => "4m",
"7d" => "30m",
"28d" => "1h",
_ => "10s",
};
Self {
start: start.to_string(),
aggregate: aggregate.to_string(),
}
} else {
Self {
start: "-5m".to_string(),
aggregate: "10s".to_string(),
}
Self {
start: start.to_string(),
aggregate: aggregate.to_string(),
}
}
@ -50,6 +41,9 @@ impl InfluxTimePeriod {
}
pub fn aggregate_window(&self) -> String {
format!("aggregateWindow(every: {}, fn: mean, createEmpty: false)", self.aggregate)
format!(
"aggregateWindow(every: {}, fn: mean, createEmpty: false)",
self.aggregate
)
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,5 +8,6 @@ await esbuild.build({
sourcemap: true,
// target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
outdir: 'output/',
loader: { '.html': 'text'}
loader: { '.html': 'text'},
format: 'esm',
})

View File

@ -1,9 +1,14 @@
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/js/bootstrap.js';
import { SiteRouter } from './router';
import { Bus } from './bus';
import { Bus, onAuthFail, onAuthOk, onMessage } from './bus';
import { Auth } from './auth';
import init from '../wasm/wasm_pipe.js';
await init();
console.log("WASM loaded");
declare global {
interface Window {
router: SiteRouter;
@ -14,6 +19,9 @@ declare global {
changeGraphPeriod: any;
}
}
(window as any).onAuthFail = onAuthFail;
(window as any).onAuthOk = onAuthOk;
(window as any).onMessage = onMessage;
window.auth = new Auth;
window.bus = new Bus();
@ -29,6 +37,7 @@ window.graphPeriod = graphPeriod;
window.changeGraphPeriod = (period: string) => changeGraphPeriod(period);
window.setInterval(() => {
console.log("tick");
window.router.ontick();
window.bus.updateConnected();
}, 1000);
@ -36,4 +45,4 @@ window.setInterval(() => {
function changeGraphPeriod(period: string) {
window.graphPeriod = period;
localStorage.setItem('graphPeriod', period);
}
}

View File

@ -1,3 +1,4 @@
import { connect_wasm_pipe, is_wasm_connected } from "../wasm/wasm_pipe";
import { Auth } from "./auth";
import { SiteRouter } from "./router";
@ -6,12 +7,15 @@ export class Bus {
connected: boolean;
constructor() {
const currentUrlWithoutAnchors = window.location.href.split('#')[0].replace("https://", "").replace("http://", "");
const url = "ws://" + currentUrlWithoutAnchors + "ws";
this.connected = false;
}
updateConnected() {
//console.log("Connection via WASM: " + is_wasm_connected());
let indicator = document.getElementById("connStatus");
if (indicator && this.connected) {
if (indicator && is_wasm_connected()) {
indicator.style.color = "green";
} else if (indicator) {
indicator.style.color = "red";
@ -22,77 +26,17 @@ export class Bus {
connect() {
const currentUrlWithoutAnchors = window.location.href.split('#')[0].replace("https://", "").replace("http://", "");
const url = "ws://" + currentUrlWithoutAnchors + "ws";
this.ws = new WebSocket(url);
this.ws.onopen = () => {
this.connected = true;
this.sendToken();
};
this.ws.onclose = (e) => {
this.connected = false;
console.log("close", e)
};
this.ws.onerror = (e) => {
console.log("error", e)
this.connected = false;
};
this.ws.onmessage = (e) => {
//console.log("message", e.data)
let json = JSON.parse(e.data);
if (json.msg && json.msg == "authOk") {
window.auth.hasCredentials = true;
window.login = json;
window.auth.token = json.token;
} else if (json.msg && json.msg == "authFail") {
window.auth.hasCredentials = false;
window.login = null;
window.auth.token = null;
localStorage.removeItem("token");
window.router.goto("login");
}
window.router.onMessage(json);
};
connect_wasm_pipe(url);
}
sendToken() {
getToken(): string {
if (window.auth.hasCredentials && window.auth.token) {
this.ws.send(formatToken(window.auth.token));
return window.auth.token;
} else {
return "";
}
}
requestNodeStatus() {
this.ws.send("{ \"msg\": \"nodeStatus\" }");
}
requestPacketChart() {
this.ws.send("{ \"msg\": \"packetChart\", \"period\": \"" + window.graphPeriod + "\" }");
}
requestPacketChartSingle(node_id: string, node_name: string) {
let request = {
msg: "packetChartSingle",
period: window.graphPeriod,
node_id: node_id,
node_name: node_name,
};
let json = JSON.stringify(request);
this.ws.send(json);
}
requestThroughputChart() {
this.ws.send("{ \"msg\": \"throughputChart\", \"period\": \"" + window.graphPeriod + "\" }");
}
requestThroughputChartSingle(node_id: string, node_name: string) {
let request = {
msg: "throughputChartSingle",
period: window.graphPeriod,
node_id: node_id,
node_name: node_name,
};
let json = JSON.stringify(request);
this.ws.send(json);
}
requestThroughputChartCircuit(circuit_id: string) {
let request = {
msg: "throughputChartCircuit",
@ -113,31 +57,6 @@ export class Bus {
this.ws.send(json);
}
requestThroughputStackSite(site_id: string) {
let request = {
msg: "throughputStackSite",
period: window.graphPeriod,
site_id: decodeURI(site_id),
};
let json = JSON.stringify(request);
this.ws.send(json);
}
requestRttChart() {
this.ws.send("{ \"msg\": \"rttChart\", \"period\": \"" + window.graphPeriod + "\" }");
}
requestRttChartSingle(node_id: string, node_name: string) {
let request = {
msg: "rttChartSingle",
period: window.graphPeriod,
node_id: node_id,
node_name: node_name,
};
let json = JSON.stringify(request);
this.ws.send(json);
}
requestRttChartSite(site_id: string) {
let request = {
msg: "rttChartSite",
@ -158,21 +77,6 @@ export class Bus {
this.ws.send(json);
}
requestNodePerfChart(node_id: string, node_name: string) {
let request = {
msg: "nodePerf",
period: window.graphPeriod,
node_id: node_id,
node_name: node_name,
};
let json = JSON.stringify(request);
this.ws.send(json);
}
requestSiteRootHeat() {
this.ws.send("{ \"msg\": \"siteRootHeat\", \"period\": \"" + window.graphPeriod + "\" }");
}
requestSiteHeat(site_id: string) {
let request = {
msg: "siteHeat",
@ -192,10 +96,6 @@ export class Bus {
this.ws.send(json);
}
requestTree(parent: string) {
this.ws.send("{ \"msg\": \"siteTree\", \"parent\": \"" + parent + "\" }");
}
requestSiteInfo(site_id: string) {
let request = {
msg: "siteInfo",
@ -224,12 +124,33 @@ export class Bus {
}
}
function formatToken(token: string) {
return "{ \"msg\": \"auth\", \"token\": \"" + token + "\" }";
}
function retryConnect() {
if (!window.bus.connected) {
window.bus.connect();
}
}
// WASM callback
export function onAuthFail() {
window.auth.hasCredentials = false;
window.login = null;
window.auth.token = null;
localStorage.removeItem("token");
window.router.goto("login");
}
// WASM callback
export function onAuthOk(token: string, name: string, license_key: string) {
window.auth.hasCredentials = true;
window.login = { msg: "authOk", token: token, name: name, license_key: license_key };
window.auth.token = token;
}
// WASM Callback
export function onMessage(rawJson: string) {
let json = JSON.parse(rawJson);
//console.log(json);
//console.log(Object.keys(json));
json.msg = Object.keys(json)[0];
window.router.onMessage(json);
}

View File

@ -1,45 +1,46 @@
import { scaleNumber } from "../helpers";
import { mbps_to_bps } from "../site_tree/site_tree";
import { Component } from "./component";
import { request_circuit_info } from "../../wasm/wasm_pipe";
export class CircuitInfo implements Component {
circuitId: string;
count: number = 0;
constructor(siteId: string) {
this.circuitId = siteId;
this.circuitId = decodeURI(siteId);
}
wireup(): void {
window.bus.requestCircuitInfo(this.circuitId);
request_circuit_info(this.circuitId);
}
ontick(): void {
this.count++;
if (this.count % 10 == 0) {
window.bus.requestCircuitInfo(this.circuitId);
request_circuit_info(this.circuitId);
}
}
onmessage(event: any): void {
if (event.msg == "circuit_info") {
//console.log(event.data);
if (event.msg == "CircuitInfo") {
//console.log(event.CircuitInfo.data);
let div = document.getElementById("circuitInfo") as HTMLDivElement;
let html = "";
html += "<table class='table table-striped'>";
html += "<tr><td>Circuit Name:</td><td>" + event.data[0].circuit_name + "</td></tr>";
html += "<tr><td>Min (CIR) Limits:</td><td>" + event.data[0].download_min_mbps + " / " + event.data[0].upload_min_mbps + " Mbps</td></tr>";
html += "<tr><td>Max (Ceiling) Limits:</td><td>" + event.data[0].download_max_mbps + " / " + event.data[0].upload_max_mbps + " Mbps</td></tr>";
html += "<tr><td>Circuit Name:</td><td>" + event.CircuitInfo.data[0].circuit_name + "</td></tr>";
html += "<tr><td>Min (CIR) Limits:</td><td>" + event.CircuitInfo.data[0].download_min_mbps + " / " + event.CircuitInfo.data[0].upload_min_mbps + " Mbps</td></tr>";
html += "<tr><td>Max (Ceiling) Limits:</td><td>" + event.CircuitInfo.data[0].download_max_mbps + " / " + event.CircuitInfo.data[0].upload_max_mbps + " Mbps</td></tr>";
html += "</table>";
div.innerHTML = html;
div = document.getElementById("circuitDevices") as HTMLDivElement;
html = "";
html += "<table class='table table-striped'>";
for (let i=0; i<event.data.length; i++) {
for (let i=0; i<event.CircuitInfo.data.length; i++) {
html += "<tr>";
html += "<td>Device:</td><td>" + event.data[i].device_name + "</td>";
html += "<td>IP:</td><td>" + event.data[i].ip_range + "/" + event.data[i].subnet + "</td>";
html += "<td>Device:</td><td>" + event.CircuitInfo.data[i].device_name + "</td>";
html += "<td>IP:</td><td>" + event.CircuitInfo.data[i].ip_range + "/" + event.CircuitInfo.data[i].subnet + "</td>";
html += "</tr>";
}
html += "</table>";

View File

@ -25,7 +25,7 @@ export class NodeCpuChart implements Component {
}
onmessage(event: any): void {
if (event.msg == "nodePerfChart") {
if (event.msg == "NodePerfChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -33,8 +33,8 @@ export class NodeCpuChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.NodePerfChart.nodes.length; i++) {
let node = event.NodePerfChart.nodes[i];
legend.push(node.node_name + " CPU %");
legend.push(node.node_name + " Single Core Peak");
//console.log(node);

View File

@ -1,4 +1,5 @@
import { Component } from "./component";
import { request_node_status } from "../../wasm/wasm_pipe";
export class NodeList implements Component {
wireup(): void {
@ -6,11 +7,11 @@ export class NodeList implements Component {
}
ontick(): void {
window.bus.requestNodeStatus();
request_node_status();
}
onmessage(event: any): void {
if (event.msg == "nodeStatus") {
if (event.msg == "NodeStatus") {
let status = document.getElementById("nodeList");
let html = "";
if (status) {
@ -18,8 +19,8 @@ export class NodeList implements Component {
html += "<thead>";
html += "<th>Node ID</th><th>Node Name</th><th>Last Seen</th>";
html += "</thead><tbody>";
for (let i = 0; i < event.nodes.length; i++) {
let node = event.nodes[i];
for (let i = 0; i < event.NodeStatus.nodes.length; i++) {
let node = event.NodeStatus.nodes[i];
let url = "\"shaperNode:" + node.node_id + ":" + node.node_name.replace(':', '_') + "\"";
let oc = "onclick='window.router.goto(" + url + ")'";
html += "<tr>";

View File

@ -1,3 +1,4 @@
import { request_node_perf_chart } from "../../wasm/wasm_pipe";
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -21,11 +22,11 @@ export class NodeRamChart implements Component {
}
ontick(): void {
window.bus.requestNodePerfChart(this.node_id, this.node_name);
request_node_perf_chart(window.graphPeriod, this.node_id, this.node_name);
}
onmessage(event: any): void {
if (event.msg == "nodePerfChart") {
if (event.msg == "NodePerfChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -33,8 +34,8 @@ export class NodeRamChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.NodePerfChart.nodes.length; i++) {
let node = event.NodePerfChart.nodes[i];
legend.push(node.node_name);
//console.log(node);

View File

@ -1,3 +1,4 @@
import { request_node_status } from "../../wasm/wasm_pipe";
import { Component } from "./component";
export class NodeStatus implements Component {
@ -6,16 +7,16 @@ export class NodeStatus implements Component {
}
ontick(): void {
window.bus.requestNodeStatus();
request_node_status();
}
onmessage(event: any): void {
if (event.msg == "nodeStatus") {
if (event.msg == "NodeStatus") {
let status = document.getElementById("nodeStatus");
let html = "";
if (status) {
for (let i = 0; i < event.nodes.length; i++) {
let node = event.nodes[i];
for (let i = 0; i < event.NodeStatus.nodes.length; i++) {
let node = event.NodeStatus.nodes[i];
let color = "danger";
if (node.last_seen > 86400) {
color = "secondary";

View File

@ -1,3 +1,4 @@
import { request_packet_chart } from "../../wasm/wasm_pipe";
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -17,11 +18,11 @@ export class PacketsChart implements Component {
}
ontick(): void {
window.bus.requestPacketChart();
request_packet_chart(window.graphPeriod);
}
onmessage(event: any): void {
if (event.msg == "packetChart") {
if (event.msg == "PacketChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -29,8 +30,8 @@ export class PacketsChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.PacketChart.nodes.length; i++) {
let node = event.PacketChart.nodes[i];
legend.push(node.node_name);
//legend.push(node.node_name + " UL");
//console.log(node);

View File

@ -1,3 +1,4 @@
import { request_packet_chart_for_node } from "../../wasm/wasm_pipe";
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -21,11 +22,11 @@ export class PacketsChartSingle implements Component {
}
ontick(): void {
window.bus.requestPacketChartSingle(this.node_id, this.node_name);
request_packet_chart_for_node(window.graphPeriod, this.node_id, this.node_name);
}
onmessage(event: any): void {
if (event.msg == "packetChart") {
if (event.msg == "PacketChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -33,8 +34,8 @@ export class PacketsChartSingle implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.PacketChart.nodes.length; i++) {
let node = event.PacketChart.nodes[i];
legend.push(node.node_name);
//legend.push(node.node_name + " UL");
//console.log(node);

View File

@ -1,3 +1,4 @@
import { request_root_heat } from "../../wasm/wasm_pipe";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -13,17 +14,17 @@ export class RootHeat implements Component {
}
wireup(): void {
window.bus.requestSiteRootHeat();
request_root_heat(window.graphPeriod);
}
ontick(): void {
this.counter++;
if (this.counter % 10 == 0)
window.bus.requestSiteRootHeat();
request_root_heat(window.graphPeriod);
}
onmessage(event: any): void {
if (event.msg == "rootHeat") {
if (event.msg == "RootHeat") {
this.myChart.hideLoading();
let categories: string[] = [];
@ -32,7 +33,7 @@ export class RootHeat implements Component {
let count = 0;
let data: any[] = [];
let keys: string[] = [];
for (const key in event.data) {
for (const key in event.RootHeat.data) {
keys.push(key);
}
keys = keys.sort().reverse();
@ -45,14 +46,14 @@ export class RootHeat implements Component {
// Push the X axis values
if (first) {
first = false;
for (let i=0; i<event.data[key].length; i++) {
x.push(event.data[key][i][0]);
for (let i=0; i<event.RootHeat.data[key].length; i++) {
x.push(event.RootHeat.data[key][i][0]);
}
}
// Create all the series entries for this category
for (let i=0; i<event.data[key].length; i++) {
data.push([i, count, event.data[key][i][1].toFixed(1)]);
for (let i=0; i<event.RootHeat.data[key].length; i++) {
data.push([i, count, event.RootHeat.data[key][i][1].toFixed(1)]);
}
count++;

View File

@ -1,6 +1,7 @@
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
import { request_rtt_chart_for_circuit } from "../../wasm/wasm_pipe";
export class RttChartCircuit implements Component {
div: HTMLElement;
@ -9,7 +10,7 @@ export class RttChartCircuit implements Component {
circuitId: string;
constructor(circuitId: string) {
this.circuitId = circuitId;
this.circuitId = decodeURI(circuitId);
this.div = document.getElementById("rttChart") as HTMLElement;
this.myChart = echarts.init(this.div);
this.myChart.showLoading();
@ -19,11 +20,11 @@ export class RttChartCircuit implements Component {
}
ontick(): void {
window.bus.requestRttChartCircuit(this.circuitId);
request_rtt_chart_for_circuit(window.graphPeriod, this.circuitId);
}
onmessage(event: any): void {
if (event.msg == "rttChartCircuit") {
if (event.msg == "RttChartCircuit") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -31,8 +32,8 @@ export class RttChartCircuit implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.RttChartCircuit.nodes.length; i++) {
let node = event.RttChartCircuit.nodes[i];
legend.push(node.node_name);
//console.log(node);

View File

@ -1,6 +1,7 @@
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
import { request_rtt_chart } from "../../wasm/wasm_pipe";
export class RttChart implements Component {
div: HTMLElement;
@ -17,11 +18,11 @@ export class RttChart implements Component {
}
ontick(): void {
window.bus.requestRttChart();
request_rtt_chart(window.graphPeriod);
}
onmessage(event: any): void {
if (event.msg == "rttChart") {
if (event.msg == "RttChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -29,8 +30,8 @@ export class RttChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.RttChart.nodes.length; i++) {
let node = event.RttChart.nodes[i];
legend.push(node.node_name);
//console.log(node);

View File

@ -1,3 +1,4 @@
import { request_rtt_chart_for_node } from "../../wasm/wasm_pipe";
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -21,11 +22,11 @@ export class RttChartSingle implements Component {
}
ontick(): void {
window.bus.requestRttChartSingle(this.node_id, this.node_name);
request_rtt_chart_for_node(window.graphPeriod, this.node_id, this.node_name);
}
onmessage(event: any): void {
if (event.msg == "rttChart") {
if (event.msg == "RttChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -33,8 +34,8 @@ export class RttChartSingle implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.RttChart.nodes.length; i++) {
let node = event.RttChart.nodes[i];
legend.push(node.node_name);
//console.log(node);

View File

@ -22,12 +22,12 @@ export class RttHisto implements Component {
}
onmessage(event: any): void {
if (event.msg == "rttChart") {
if (event.msg == "RttChart") {
//console.log(event);
this.download = [];
this.x = [];
for (let i = 0; i < event.histogram.length; i++) {
this.download.push(event.histogram[i]);
for (let i = 0; i < event.RttChart.histogram.length; i++) {
this.download.push(event.RttChart.histogram[i]);
this.x.push(i * 10);
}

View File

@ -22,12 +22,12 @@ export class RttHistoSite implements Component {
}
onmessage(event: any): void {
if (event.msg == "rttChartSite") {
if (event.msg == "RttChartSite") {
//console.log(event);
this.download = [];
this.x = [];
for (let i = 0; i < event.histogram.length; i++) {
this.download.push(event.histogram[i]);
for (let i = 0; i < event.RttChartSite.histogram.length; i++) {
this.download.push(event.RttChartSite.histogram[i]);
this.x.push(i * 10);
}

View File

@ -1,6 +1,7 @@
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
import { request_rtt_chart_for_site } from "../../wasm/wasm_pipe";
export class RttChartSite implements Component {
div: HTMLElement;
@ -19,11 +20,11 @@ export class RttChartSite implements Component {
}
ontick(): void {
window.bus.requestRttChartSite(this.siteId);
request_rtt_chart_for_site(window.graphPeriod, this.siteId);
}
onmessage(event: any): void {
if (event.msg == "rttChartSite") {
if (event.msg == "RttChartSite") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -31,8 +32,8 @@ export class RttChartSite implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.RttChartSite.nodes.length; i++) {
let node = event.RttChartSite.nodes[i];
legend.push(node.node_name);
//console.log(node);

View File

@ -1,3 +1,4 @@
import { request_site_parents } from "../../wasm/wasm_pipe";
import { makeUrl } from "../helpers";
import { Component } from "./component";
@ -9,18 +10,18 @@ export class SiteBreadcrumbs implements Component {
}
wireup(): void {
window.bus.requestSiteParents(this.siteId);
request_site_parents(this.siteId);
}
ontick(): void {
}
onmessage(event: any): void {
if (event.msg == "site_parents") {
if (event.msg == "SiteParents") {
//console.log(event.data);
let div = document.getElementById("siteName") as HTMLDivElement;
let html = "";
let crumbs = event.data.reverse();
let crumbs = event.SiteParents.data.reverse();
for (let i = 0; i < crumbs.length-1; i++) {
let url = makeUrl(crumbs[i][0], crumbs[i][1]);
html += "<a href='#" + url + "' onclick='window.router.goto(\"" + url + "\")'>" + crumbs[i][1] + "</a> | ";
@ -28,11 +29,11 @@ export class SiteBreadcrumbs implements Component {
html += crumbs[crumbs.length-1][1] + " | ";
html += "<select id='siteChildren'></select>";
div.innerHTML = html;
} else if (event.msg == "site_children") {
} else if (event.msg == "SiteChildren") {
//console.log(event.data);
let html = "<option value=''>-- Children --</option>";
for (let i=0; i<event.data.length; i++) {
html += "<option value='" + makeUrl(event.data[i][0], event.data[i][1]) + "'>" + event.data[i][2] + "</option>";
for (let i=0; i<event.SiteChildren.data.length; i++) {
html += "<option value='" + makeUrl(event.SiteChildren.data[i][0], event.SiteChildren.data[i][1]) + "'>" + event.SiteChildren.data[i][2] + "</option>";
}
let select = document.getElementById("siteChildren") as HTMLSelectElement;
select.innerHTML = html;

View File

@ -1,5 +1,6 @@
import { Component } from "./component";
import * as echarts from 'echarts';
import { request_site_heat } from "../../wasm/wasm_pipe";
export class SiteHeat implements Component {
div: HTMLElement;
@ -8,24 +9,26 @@ export class SiteHeat implements Component {
siteId: string;
constructor(siteId: string) {
this.siteId = siteId;
this.siteId = decodeURI(siteId);
this.div = document.getElementById("rootHeat") as HTMLElement;
this.myChart = echarts.init(this.div);
this.myChart.showLoading();
}
wireup(): void {
window.bus.requestSiteHeat(this.siteId);
console.log("SiteHeat wireup");
request_site_heat(window.graphPeriod, this.siteId);
}
ontick(): void {
console.log("SiteHeat ontick");
this.counter++;
if (this.counter % 10 == 0)
window.bus.requestSiteHeat(this.siteId);
request_site_heat(window.graphPeriod, this.siteId);
}
onmessage(event: any): void {
if (event.msg == "siteHeat") {
if (event.msg == "SiteHeat") {
this.myChart.hideLoading();
let categories: string[] = [];
@ -34,7 +37,7 @@ export class SiteHeat implements Component {
let count = 0;
let data: any[] = [];
let keys: string[] = [];
for (const key in event.data) {
for (const key in event.SiteHeat.data) {
keys.push(key);
}
keys = keys.sort().reverse();
@ -47,14 +50,14 @@ export class SiteHeat implements Component {
// Push the X axis values
if (first) {
first = false;
for (let i=0; i<event.data[key].length; i++) {
x.push(event.data[key][i][0]);
for (let i=0; i<event.SiteHeat.data[key].length; i++) {
x.push(event.SiteHeat.data[key][i][0]);
}
}
// Create all the series entries for this category
for (let i=0; i<event.data[key].length; i++) {
data.push([i, count, event.data[key][i][1].toFixed(1)]);
for (let i=0; i<event.SiteHeat.data[key].length; i++) {
data.push([i, count, event.SiteHeat.data[key][i][1].toFixed(1)]);
}
count++;

View File

@ -1,6 +1,7 @@
import { scaleNumber } from "../helpers";
import { mbps_to_bps } from "../site_tree/site_tree";
import { Component } from "./component";
import { request_site_info } from "../../wasm/wasm_pipe";
export class SiteInfo implements Component {
siteId: string;
@ -11,25 +12,25 @@ export class SiteInfo implements Component {
}
wireup(): void {
window.bus.requestSiteInfo(this.siteId);
request_site_info(decodeURI(this.siteId));
}
ontick(): void {
this.count++;
if (this.count % 10 == 0) {
window.bus.requestSiteInfo(this.siteId);
request_site_info(decodeURI(this.siteId));
}
}
onmessage(event: any): void {
if (event.msg == "site_info") {
if (event.msg == "SiteInfo") {
//console.log(event.data);
let div = document.getElementById("siteInfo") as HTMLDivElement;
let html = "";
html += "<table class='table table-striped'>";
html += "<tr><td>Max:</td><td>" + scaleNumber(event.data.max_down * mbps_to_bps) + " / " + scaleNumber(event.data.max_up * mbps_to_bps) + "</td></tr>";
html += "<tr><td>Current:</td><td>" + scaleNumber(event.data.current_down) + " / " + scaleNumber(event.data.current_up) + "</td></tr>";
html += "<tr><td>Current RTT:</td><td>" + event.data.current_rtt / 100.0 + " ms</td></tr>";
html += "<tr><td>Max:</td><td>" + scaleNumber(event.SiteInfo.data.max_down * mbps_to_bps) + " / " + scaleNumber(event.SiteInfo.data.max_up * mbps_to_bps) + "</td></tr>";
html += "<tr><td>Current:</td><td>" + scaleNumber(event.SiteInfo.data.current_down) + " / " + scaleNumber(event.SiteInfo.data.current_up) + "</td></tr>";
html += "<tr><td>Current RTT:</td><td>" + event.SiteInfo.data.current_rtt / 100.0 + " ms</td></tr>";
html += "</table>";
div.innerHTML = html;
}

View File

@ -1,3 +1,4 @@
import { request_site_stack } from "../../wasm/wasm_pipe";
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -7,6 +8,7 @@ export class SiteStackChart implements Component {
myChart: echarts.ECharts;
chartMade: boolean = false;
siteId: string;
counter: number = 0;
constructor(siteId: string) {
this.siteId = siteId;
@ -19,11 +21,14 @@ export class SiteStackChart implements Component {
}
ontick(): void {
window.bus.requestThroughputStackSite(this.siteId);
this.counter++;
if (this.counter % 10 == 0 || this.counter == 0) {
request_site_stack(window.graphPeriod, this.siteId);
}
}
onmessage(event: any): void {
if (event.msg == "siteStack") {
if (event.msg == "SiteStack") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -31,8 +36,8 @@ export class SiteStackChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i = 0; i < event.nodes.length; i++) {
let node = event.nodes[i];
for (let i = 0; i < event.SiteStack.nodes.length; i++) {
let node = event.SiteStack.nodes[i];
if (node.node_name != "Root") {
legend.push(node.node_name);
//legend.push(node.node_name + " UL");

View File

@ -1,3 +1,4 @@
import { request_throughput_chart } from "../../wasm/wasm_pipe";
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -17,11 +18,11 @@ export class ThroughputChart implements Component {
}
ontick(): void {
window.bus.requestThroughputChart();
request_throughput_chart(window.graphPeriod);
}
onmessage(event: any): void {
if (event.msg == "bitsChart") {
if (event.msg == "BitsChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -29,8 +30,8 @@ export class ThroughputChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.BitsChart.nodes.length; i++) {
let node = event.BitsChart.nodes[i];
legend.push(node.node_name);
//legend.push(node.node_name + " UL");
//console.log(node);

View File

@ -1,6 +1,7 @@
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
import { request_throughput_chart_for_circuit } from "../../wasm/wasm_pipe";
export class ThroughputCircuitChart implements Component {
div: HTMLElement;
@ -19,11 +20,11 @@ export class ThroughputCircuitChart implements Component {
}
ontick(): void {
window.bus.requestThroughputChartCircuit(this.circuitId);
request_throughput_chart_for_circuit(window.graphPeriod, this.circuitId);
}
onmessage(event: any): void {
if (event.msg == "bitsChartCircuit") {
if (event.msg == "BitsChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -31,8 +32,8 @@ export class ThroughputCircuitChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.BitsChart.nodes.length; i++) {
let node = event.BitsChart.nodes[i];
legend.push(node.node_name);
//legend.push(node.node_name + " UL");
//console.log(node);

View File

@ -1,3 +1,4 @@
import { request_throughput_chart_for_node } from "../../wasm/wasm_pipe";
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
@ -21,11 +22,11 @@ export class ThroughputChartSingle implements Component {
}
ontick(): void {
window.bus.requestThroughputChartSingle(this.node_id, this.node_name);
request_throughput_chart_for_node(window.graphPeriod, this.node_id, this.node_name);
}
onmessage(event: any): void {
if (event.msg == "bitsChart") {
if (event.msg == "BitsChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -33,8 +34,8 @@ export class ThroughputChartSingle implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.BitsChart.nodes.length; i++) {
let node = event.BitsChart.nodes[i];
legend.push(node.node_name);
//legend.push(node.node_name + " UL");
//console.log(node);

View File

@ -1,6 +1,7 @@
import { scaleNumber } from "../helpers";
import { Component } from "./component";
import * as echarts from 'echarts';
import { request_throughput_chart_for_site } from "../../wasm/wasm_pipe";
export class ThroughputSiteChart implements Component {
div: HTMLElement;
@ -19,11 +20,11 @@ export class ThroughputSiteChart implements Component {
}
ontick(): void {
window.bus.requestThroughputChartSite(this.siteId);
request_throughput_chart_for_site(window.graphPeriod, this.siteId);
}
onmessage(event: any): void {
if (event.msg == "bitsChartSite") {
if (event.msg == "BitsChart") {
let series: echarts.SeriesOption[] = [];
// Iterate all provides nodes and create a set of series for each,
@ -31,8 +32,8 @@ export class ThroughputSiteChart implements Component {
let x: any[] = [];
let first = true;
let legend: string[] = [];
for (let i=0; i<event.nodes.length; i++) {
let node = event.nodes[i];
for (let i=0; i<event.BitsChart.nodes.length; i++) {
let node = event.BitsChart.nodes[i];
legend.push(node.node_name);
//legend.push(node.node_name + " UL");
//console.log(node);

View File

@ -1,6 +1,7 @@
import html from './template.html';
import { Page } from '../page'
import { getValueFromForm } from '../helpers';
import { send_login } from '../../wasm/wasm_pipe';
export class LoginPage implements Page {
constructor() {
@ -61,26 +62,19 @@ export class LoginPage implements Page {
btn.innerHTML = "<i class=\"fa-solid fa-spinner fa-spin\"></i>";
}
let data = {
msg: "login",
license: license,
username: username,
password: password,
};
let json: string = JSON.stringify(data);
window.bus.ws.send(json);
send_login(license, username, password);
}
onmessage(event: any) {
if (event.msg) {
if (event.msg == "loginOk") {
if (event.msg == "LoginOk") {
// TODO: Store the credentials globally
window.login = event;
window.login = event.LoginOk;
window.auth.hasCredentials = true;
window.auth.token = event.token;
localStorage.setItem("token", event.token);
window.auth.token = event.LoginOk.token;
localStorage.setItem("token", event.LoginOk.token);
window.router.goto("dashboard");
} else if (event.msg = "loginFail") {
} else if (event.msg = "LoginFail") {
alert("Login failed");
let btn = document.getElementById('btnLogin');
if (btn) {

View File

@ -1,6 +1,7 @@
import html from './template.html';
import { Page } from '../page'
import { siteIcon } from '../helpers';
import { request_search } from "../../wasm/wasm_pipe";
export class MenuPage implements Page {
activePanel: string;
@ -56,7 +57,7 @@ export class MenuPage implements Page {
if (r) {
r.style.display = "none";
}
window.bus.sendSearch(term);
request_search(term);
}
onmessage(event: any) {
@ -72,8 +73,8 @@ export class MenuPage implements Page {
}
}
} break;
case "search": {
this.searchResult(event.hits);
case "SearchResult": {
this.searchResult(event.SearchResult.hits);
} break;
}
}

View File

@ -37,7 +37,6 @@ export class SitePage implements Page {
this.components.forEach(component => {
component.wireup();
});
window.bus.requestThroughputStackSite(this.siteId);
}
ontick(): void {

View File

@ -4,6 +4,7 @@ import { MenuPage } from '../menu/menu';
import { Component } from '../components/component';
import mermaid from 'mermaid';
import { makeUrl, rttColor, scaleNumber, siteIcon, usageColor } from '../helpers';
import { request_node_status, request_tree } from '../../wasm/wasm_pipe';
export class SiteTreePage implements Page {
menu: MenuPage;
@ -26,7 +27,7 @@ export class SiteTreePage implements Page {
this.components.forEach(component => {
component.wireup();
});
window.bus.requestNodeStatus();
request_node_status();
}
ontick(): void {
@ -48,20 +49,20 @@ export class SiteTreePage implements Page {
component.onmessage(event);
});
if (event.msg == "nodeStatus") {
if (event.msg == "NodeStatus") {
let drop_down = document.getElementById("shaper_node_select") as HTMLSelectElement;
if (drop_down) {
let items = "";
for (let i = 0; i < event.nodes.length; i++) {
for (let i = 0; i < event.NodeStatus.nodes.length; i++) {
let isSelected = "";
if (i ==0 || this.selectedNode == event.nodes[i].node_id) {
if (i ==0 || this.selectedNode == event.NodeStatus.nodes[i].node_id) {
isSelected = "selected";
if (i == 0) {
this.selectedNode = event.nodes[i].node_id;
this.selectedNode = event.NodeStatus.nodes[i].node_id;
fetchTree(this.selectedNode);
}
}
items += "<option " + isSelected + " value='" + event.nodes[i].node_id + "'>" + event.nodes[i].node_name + "</option>";
items += "<option " + isSelected + " value='" + event.NodeStatus.nodes[i].node_id + "'>" + event.NodeStatus.nodes[i].node_name + "</option>";
}
drop_down.innerHTML = items;
drop_down.onchange = () => {
@ -72,15 +73,15 @@ export class SiteTreePage implements Page {
}
}
if (event.msg == "site_tree") {
buildTree(event.data);
if (event.msg == "SiteTree") {
buildTree(event.SiteTree.data);
}
}
}
}
function fetchTree(parent: string) {
window.bus.requestTree(parent);
request_tree(parent);
}
class TreeItem {

View File

@ -0,0 +1,170 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {string} url
*/
export function connect_wasm_pipe(url: string): void;
/**
* @returns {boolean}
*/
export function is_wasm_connected(): boolean;
/**
* @param {string} token
*/
export function send_token(token: string): void;
/**
* @param {string} license
* @param {string} username
* @param {string} password
*/
export function send_login(license: string, username: string, password: string): void;
/**
*/
export function request_node_status(): void;
/**
* @param {string} period
*/
export function request_packet_chart(period: string): void;
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_packet_chart_for_node(period: string, node_id: string, node_name: string): void;
/**
* @param {string} period
*/
export function request_throughput_chart(period: string): void;
/**
* @param {string} period
* @param {string} site_id
*/
export function request_throughput_chart_for_site(period: string, site_id: string): void;
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_throughput_chart_for_node(period: string, node_id: string, node_name: string): void;
/**
* @param {string} period
* @param {string} circuit_id
*/
export function request_throughput_chart_for_circuit(period: string, circuit_id: string): void;
/**
* @param {string} period
* @param {string} site_id
*/
export function request_site_stack(period: string, site_id: string): void;
/**
* @param {string} period
*/
export function request_rtt_chart(period: string): void;
/**
* @param {string} period
* @param {string} site_id
*/
export function request_rtt_chart_for_site(period: string, site_id: string): void;
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_rtt_chart_for_node(period: string, node_id: string, node_name: string): void;
/**
* @param {string} period
* @param {string} circuit_id
*/
export function request_rtt_chart_for_circuit(period: string, circuit_id: string): void;
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_node_perf_chart(period: string, node_id: string, node_name: string): void;
/**
* @param {string} period
*/
export function request_root_heat(period: string): void;
/**
* @param {string} period
* @param {string} site_id
*/
export function request_site_heat(period: string, site_id: string): void;
/**
* @param {string} parent
*/
export function request_tree(parent: string): void;
/**
* @param {string} site_id
*/
export function request_site_info(site_id: string): void;
/**
* @param {string} site_id
*/
export function request_site_parents(site_id: string): void;
/**
* @param {string} term
*/
export function request_search(term: string): void;
/**
* @param {string} circuit_id
*/
export function request_circuit_info(circuit_id: string): void;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly connect_wasm_pipe: (a: number, b: number) => void;
readonly is_wasm_connected: () => number;
readonly send_token: (a: number, b: number) => void;
readonly send_login: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly request_node_status: () => void;
readonly request_packet_chart: (a: number, b: number) => void;
readonly request_packet_chart_for_node: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly request_throughput_chart: (a: number, b: number) => void;
readonly request_throughput_chart_for_site: (a: number, b: number, c: number, d: number) => void;
readonly request_throughput_chart_for_node: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly request_throughput_chart_for_circuit: (a: number, b: number, c: number, d: number) => void;
readonly request_site_stack: (a: number, b: number, c: number, d: number) => void;
readonly request_rtt_chart: (a: number, b: number) => void;
readonly request_rtt_chart_for_site: (a: number, b: number, c: number, d: number) => void;
readonly request_rtt_chart_for_node: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly request_rtt_chart_for_circuit: (a: number, b: number, c: number, d: number) => void;
readonly request_node_perf_chart: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly request_root_heat: (a: number, b: number) => void;
readonly request_site_heat: (a: number, b: number, c: number, d: number) => void;
readonly request_tree: (a: number, b: number) => void;
readonly request_site_info: (a: number, b: number) => void;
readonly request_site_parents: (a: number, b: number) => void;
readonly request_search: (a: number, b: number) => void;
readonly request_circuit_info: (a: number, b: number) => void;
readonly __wbindgen_malloc: (a: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
readonly __wbindgen_export_2: WebAssembly.Table;
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h66d1b1d022c5cd85: (a: number, b: number, c: number) => void;
readonly __wbindgen_free: (a: number, b: number) => void;
readonly __wbindgen_exn_store: (a: number) => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {SyncInitInput} module
*
* @returns {InitOutput}
*/
export function initSync(module: SyncInitInput): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

View File

@ -0,0 +1,692 @@
let wasm;
const heap = new Array(128).fill(undefined);
heap.push(undefined, null, true, false);
function getObject(idx) { return heap[idx]; }
function debugString(val) {
// primitive types
const type = typeof val;
if (type == 'number' || type == 'boolean' || val == null) {
return `${val}`;
}
if (type == 'string') {
return `"${val}"`;
}
if (type == 'symbol') {
const description = val.description;
if (description == null) {
return 'Symbol';
} else {
return `Symbol(${description})`;
}
}
if (type == 'function') {
const name = val.name;
if (typeof name == 'string' && name.length > 0) {
return `Function(${name})`;
} else {
return 'Function';
}
}
// objects
if (Array.isArray(val)) {
const length = val.length;
let debug = '[';
if (length > 0) {
debug += debugString(val[0]);
}
for(let i = 1; i < length; i++) {
debug += ', ' + debugString(val[i]);
}
debug += ']';
return debug;
}
// Test for built-in
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
let className;
if (builtInMatches.length > 1) {
className = builtInMatches[1];
} else {
// Failed to match the standard '[object ClassName]'
return toString.call(val);
}
if (className == 'Object') {
// we're a user defined class or Object
// JSON.stringify avoids problems with cycles, and is generally much
// easier than looping through ownProperties of `val`.
try {
return 'Object(' + JSON.stringify(val) + ')';
} catch (_) {
return 'Object';
}
}
// errors
if (val instanceof Error) {
return `${val.name}: ${val.message}\n${val.stack}`;
}
// TODO we could test for more things here, like `Set`s and `Map`s.
return className;
}
let WASM_VECTOR_LEN = 0;
let cachedUint8Memory0 = null;
function getUint8Memory0() {
if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8Memory0;
}
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
? function (arg, view) {
return cachedTextEncoder.encodeInto(arg, view);
}
: function (arg, view) {
const buf = cachedTextEncoder.encode(arg);
view.set(buf);
return {
read: arg.length,
written: buf.length
};
});
function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length) >>> 0;
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}
let len = arg.length;
let ptr = malloc(len) >>> 0;
const mem = getUint8Memory0();
let offset = 0;
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3) >>> 0;
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
offset += ret.written;
}
WASM_VECTOR_LEN = offset;
return ptr;
}
let cachedInt32Memory0 = null;
function getInt32Memory0() {
if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) {
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
}
return cachedInt32Memory0;
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let heap_next = heap.length;
function dropObject(idx) {
if (idx < 132) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
}
function makeMutClosure(arg0, arg1, dtor, f) {
const state = { a: arg0, b: arg1, cnt: 1, dtor };
const real = (...args) => {
// First up with a closure we increment the internal reference
// count. This ensures that the Rust closure environment won't
// be deallocated while we're invoking it.
state.cnt++;
const a = state.a;
state.a = 0;
try {
return f(a, state.b, ...args);
} finally {
if (--state.cnt === 0) {
wasm.__wbindgen_export_2.get(state.dtor)(a, state.b);
} else {
state.a = a;
}
}
};
real.original = state;
return real;
}
function __wbg_adapter_12(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h66d1b1d022c5cd85(arg0, arg1, addHeapObject(arg2));
}
function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }
/**
* @param {string} url
*/
export function connect_wasm_pipe(url) {
const ptr0 = passStringToWasm0(url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.connect_wasm_pipe(ptr0, len0);
}
/**
* @returns {boolean}
*/
export function is_wasm_connected() {
const ret = wasm.is_wasm_connected();
return ret !== 0;
}
/**
* @param {string} token
*/
export function send_token(token) {
const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.send_token(ptr0, len0);
}
/**
* @param {string} license
* @param {string} username
* @param {string} password
*/
export function send_login(license, username, password) {
const ptr0 = passStringToWasm0(license, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(username, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ptr2 = passStringToWasm0(password, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len2 = WASM_VECTOR_LEN;
wasm.send_login(ptr0, len0, ptr1, len1, ptr2, len2);
}
/**
*/
export function request_node_status() {
wasm.request_node_status();
}
/**
* @param {string} period
*/
export function request_packet_chart(period) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_packet_chart(ptr0, len0);
}
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_packet_chart_for_node(period, node_id, node_name) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(node_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ptr2 = passStringToWasm0(node_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len2 = WASM_VECTOR_LEN;
wasm.request_packet_chart_for_node(ptr0, len0, ptr1, len1, ptr2, len2);
}
/**
* @param {string} period
*/
export function request_throughput_chart(period) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_throughput_chart(ptr0, len0);
}
/**
* @param {string} period
* @param {string} site_id
*/
export function request_throughput_chart_for_site(period, site_id) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(site_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.request_throughput_chart_for_site(ptr0, len0, ptr1, len1);
}
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_throughput_chart_for_node(period, node_id, node_name) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(node_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ptr2 = passStringToWasm0(node_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len2 = WASM_VECTOR_LEN;
wasm.request_throughput_chart_for_node(ptr0, len0, ptr1, len1, ptr2, len2);
}
/**
* @param {string} period
* @param {string} circuit_id
*/
export function request_throughput_chart_for_circuit(period, circuit_id) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(circuit_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.request_throughput_chart_for_circuit(ptr0, len0, ptr1, len1);
}
/**
* @param {string} period
* @param {string} site_id
*/
export function request_site_stack(period, site_id) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(site_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.request_site_stack(ptr0, len0, ptr1, len1);
}
/**
* @param {string} period
*/
export function request_rtt_chart(period) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_rtt_chart(ptr0, len0);
}
/**
* @param {string} period
* @param {string} site_id
*/
export function request_rtt_chart_for_site(period, site_id) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(site_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.request_rtt_chart_for_site(ptr0, len0, ptr1, len1);
}
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_rtt_chart_for_node(period, node_id, node_name) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(node_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ptr2 = passStringToWasm0(node_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len2 = WASM_VECTOR_LEN;
wasm.request_rtt_chart_for_node(ptr0, len0, ptr1, len1, ptr2, len2);
}
/**
* @param {string} period
* @param {string} circuit_id
*/
export function request_rtt_chart_for_circuit(period, circuit_id) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(circuit_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.request_rtt_chart_for_circuit(ptr0, len0, ptr1, len1);
}
/**
* @param {string} period
* @param {string} node_id
* @param {string} node_name
*/
export function request_node_perf_chart(period, node_id, node_name) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(node_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
const ptr2 = passStringToWasm0(node_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len2 = WASM_VECTOR_LEN;
wasm.request_node_perf_chart(ptr0, len0, ptr1, len1, ptr2, len2);
}
/**
* @param {string} period
*/
export function request_root_heat(period) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_root_heat(ptr0, len0);
}
/**
* @param {string} period
* @param {string} site_id
*/
export function request_site_heat(period, site_id) {
const ptr0 = passStringToWasm0(period, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(site_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
wasm.request_site_heat(ptr0, len0, ptr1, len1);
}
/**
* @param {string} parent
*/
export function request_tree(parent) {
const ptr0 = passStringToWasm0(parent, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_tree(ptr0, len0);
}
/**
* @param {string} site_id
*/
export function request_site_info(site_id) {
const ptr0 = passStringToWasm0(site_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_site_info(ptr0, len0);
}
/**
* @param {string} site_id
*/
export function request_site_parents(site_id) {
const ptr0 = passStringToWasm0(site_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_site_parents(ptr0, len0);
}
/**
* @param {string} term
*/
export function request_search(term) {
const ptr0 = passStringToWasm0(term, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_search(ptr0, len0);
}
/**
* @param {string} circuit_id
*/
export function request_circuit_info(circuit_id) {
const ptr0 = passStringToWasm0(circuit_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.request_circuit_info(ptr0, len0);
}
function handleError(f, args) {
try {
return f.apply(this, args);
} catch (e) {
wasm.__wbindgen_exn_store(addHeapObject(e));
}
}
function getArrayU8FromWasm0(ptr, len) {
ptr = ptr >>> 0;
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
}
async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
function __wbg_get_imports() {
const imports = {};
imports.wbg = {};
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
const ret = debugString(getObject(arg1));
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
imports.wbg.__wbg_log_cd48b3599daf93ee = function(arg0, arg1) {
console.log(getStringFromWasm0(arg0, arg1));
};
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
takeObject(arg0);
};
imports.wbg.__wbg_windowbusgetToken_eab6ac8f06d69af2 = function(arg0) {
const ret = window.bus.getToken();
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_send_737fddb36434277e = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).send(getArrayU8FromWasm0(arg1, arg2));
}, arguments) };
imports.wbg.__wbg_data_ef47af9c565d228b = function(arg0) {
const ret = getObject(arg0).data;
return addHeapObject(ret);
};
imports.wbg.__wbg_instanceof_ArrayBuffer_de688b806c28ff28 = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof ArrayBuffer;
} catch {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_new_bc5d9aad3f9ac80e = function(arg0) {
const ret = new Uint8Array(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_length_d9c4ded7e708c6a1 = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
imports.wbg.__wbindgen_memory = function() {
const ret = wasm.memory;
return addHeapObject(ret);
};
imports.wbg.__wbg_buffer_fcbfb6d88b2732e9 = function(arg0) {
const ret = getObject(arg0).buffer;
return addHeapObject(ret);
};
imports.wbg.__wbg_set_4b3aa8445ac1e91c = function(arg0, arg1, arg2) {
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
};
imports.wbg.__wbg_windowonMessage_5c5b80d5376153dc = function(arg0, arg1) {
let deferred0_0;
let deferred0_1;
try {
deferred0_0 = arg0;
deferred0_1 = arg1;
window.onMessage(getStringFromWasm0(arg0, arg1));
} finally {
wasm.__wbindgen_free(deferred0_0, deferred0_1);
}
};
imports.wbg.__wbg_windowonAuthFail_ddfdfcd594ff15b8 = typeof window.onAuthFail == 'function' ? window.onAuthFail : notDefined('window.onAuthFail');
imports.wbg.__wbg_windowonAuthOk_9cd9fb8f74884ca4 = function(arg0, arg1, arg2, arg3, arg4, arg5) {
let deferred0_0;
let deferred0_1;
let deferred1_0;
let deferred1_1;
let deferred2_0;
let deferred2_1;
try {
deferred0_0 = arg0;
deferred0_1 = arg1;
deferred1_0 = arg2;
deferred1_1 = arg3;
deferred2_0 = arg4;
deferred2_1 = arg5;
window.onAuthOk(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3), getStringFromWasm0(arg4, arg5));
} finally {
wasm.__wbindgen_free(deferred0_0, deferred0_1);
wasm.__wbindgen_free(deferred1_0, deferred1_1);
wasm.__wbindgen_free(deferred2_0, deferred2_1);
}
};
imports.wbg.__wbg_new_39e958ac9d5cae7d = function() { return handleError(function (arg0, arg1) {
const ret = new WebSocket(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
const ret = getStringFromWasm0(arg0, arg1);
return addHeapObject(ret);
};
imports.wbg.__wbg_setbinaryType_2e2320b177c86b17 = function(arg0, arg1) {
getObject(arg0).binaryType = takeObject(arg1);
};
imports.wbg.__wbg_setonmessage_493b82147081ec7e = function(arg0, arg1) {
getObject(arg0).onmessage = getObject(arg1);
};
imports.wbg.__wbg_setonclose_6b22bc5d93628786 = function(arg0, arg1) {
getObject(arg0).onclose = getObject(arg1);
};
imports.wbg.__wbg_setonerror_9f7532626d7a9ce2 = function(arg0, arg1) {
getObject(arg0).onerror = getObject(arg1);
};
imports.wbg.__wbg_setonopen_6fd8b28538150568 = function(arg0, arg1) {
getObject(arg0).onopen = getObject(arg1);
};
imports.wbg.__wbindgen_closure_wrapper1710 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 70, __wbg_adapter_12);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1711 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 70, __wbg_adapter_12);
return addHeapObject(ret);
};
return imports;
}
function __wbg_init_memory(imports, maybe_memory) {
}
function __wbg_finalize_init(instance, module) {
wasm = instance.exports;
__wbg_init.__wbindgen_wasm_module = module;
cachedInt32Memory0 = null;
cachedUint8Memory0 = null;
return wasm;
}
function initSync(module) {
if (wasm !== undefined) return wasm;
const imports = __wbg_get_imports();
__wbg_init_memory(imports);
if (!(module instanceof WebAssembly.Module)) {
module = new WebAssembly.Module(module);
}
const instance = new WebAssembly.Instance(module, imports);
return __wbg_finalize_init(instance, module);
}
async function __wbg_init(input) {
if (wasm !== undefined) return wasm;
if (typeof input === 'undefined') {
input = new URL('wasm_pipe_bg.wasm', import.meta.url);
}
const imports = __wbg_get_imports();
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
__wbg_init_memory(imports);
const { instance, module } = await __wbg_load(await input, imports);
return __wbg_finalize_init(instance, module);
}
export { initSync }
export default __wbg_init;

View File

@ -0,0 +1,33 @@
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export function connect_wasm_pipe(a: number, b: number): void;
export function is_wasm_connected(): number;
export function send_token(a: number, b: number): void;
export function send_login(a: number, b: number, c: number, d: number, e: number, f: number): void;
export function request_node_status(): void;
export function request_packet_chart(a: number, b: number): void;
export function request_packet_chart_for_node(a: number, b: number, c: number, d: number, e: number, f: number): void;
export function request_throughput_chart(a: number, b: number): void;
export function request_throughput_chart_for_site(a: number, b: number, c: number, d: number): void;
export function request_throughput_chart_for_node(a: number, b: number, c: number, d: number, e: number, f: number): void;
export function request_throughput_chart_for_circuit(a: number, b: number, c: number, d: number): void;
export function request_site_stack(a: number, b: number, c: number, d: number): void;
export function request_rtt_chart(a: number, b: number): void;
export function request_rtt_chart_for_site(a: number, b: number, c: number, d: number): void;
export function request_rtt_chart_for_node(a: number, b: number, c: number, d: number, e: number, f: number): void;
export function request_rtt_chart_for_circuit(a: number, b: number, c: number, d: number): void;
export function request_node_perf_chart(a: number, b: number, c: number, d: number, e: number, f: number): void;
export function request_root_heat(a: number, b: number): void;
export function request_site_heat(a: number, b: number, c: number, d: number): void;
export function request_tree(a: number, b: number): void;
export function request_site_info(a: number, b: number): void;
export function request_site_parents(a: number, b: number): void;
export function request_search(a: number, b: number): void;
export function request_circuit_info(a: number, b: number): void;
export function __wbindgen_malloc(a: number): number;
export function __wbindgen_realloc(a: number, b: number, c: number): number;
export const __wbindgen_export_2: WebAssembly.Table;
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h66d1b1d022c5cd85(a: number, b: number, c: number): void;
export function __wbindgen_free(a: number, b: number): void;
export function __wbindgen_exn_store(a: number): void;

View File

@ -0,0 +1,27 @@
[package]
name = "wasm_pipe"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.86"
js-sys = "0.3"
miniz_oxide = "0.7.1"
serde_cbor = "0" # For RFC8949/7409 format C binary objects
wasm_pipe_types = { path = "../wasm_pipe_types" }
serde_json = "1.0.96"
[dependencies.web-sys]
version = "0.3.22"
features = [
"BinaryType",
"Blob",
"ErrorEvent",
"FileReader",
"MessageEvent",
"ProgressEvent",
"WebSocket",
]

View File

@ -0,0 +1,5 @@
#!/bin/bash
cargo build --target wasm32-unknown-unknown --release
wasm-bindgen --target web --out-dir staging/ ../../target/wasm32-unknown-unknown/release/wasm_pipe.wasm
cp staging/* ../site_build/wasm
cp staging/wasm_pipe_bg.wasm ../lts_node/web

View File

@ -0,0 +1,224 @@
use wasm_bindgen::prelude::*;
use wasm_pipe_types::{WasmRequest, WasmResponse};
use web_sys::{BinaryType, ErrorEvent, MessageEvent, WebSocket};
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
#[wasm_bindgen(js_name = "window.bus.getToken")]
fn get_token() -> String;
#[wasm_bindgen(js_name = "window.onAuthOk")]
fn onAuthOk(token: String, name: String, license_key: String);
#[wasm_bindgen(js_name = "window.onAuthFail")]
fn onAuthFail();
#[wasm_bindgen(js_name = "window.onMessage")]
fn onMessage(json: String);
}
static mut CONNECTED: bool = false;
static mut WS: Option<WebSocket> = None;
#[wasm_bindgen]
pub fn connect_wasm_pipe(url: String) {
unsafe {
if CONNECTED {
log("Already connected");
return;
}
WS = Some(WebSocket::new(&url).unwrap());
if let Some(ws) = &mut WS {
ws.set_binary_type(BinaryType::Arraybuffer);
ws.set_binary_type(BinaryType::Arraybuffer);
let onmessage_callback = Closure::<dyn FnMut(_)>::new(move |e: MessageEvent| {
log("Message Received");
if let Ok(abuf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
let array = js_sys::Uint8Array::new(&abuf);
//let len = array.byte_length() as usize;
let raw = array.to_vec();
let decompressed = miniz_oxide::inflate::decompress_to_vec(&raw).unwrap();
let msg: WasmResponse = serde_cbor::from_slice(&decompressed).unwrap();
//log(&format!("Message: {:?}", msg));
match msg {
WasmResponse::AuthOk { token, name, license_key } => {
onAuthOk(token, name, license_key);
}
WasmResponse::AuthFail => {
onAuthFail();
}
_ => {
let json = serde_json::to_string(&msg).unwrap();
onMessage(json);
}
}
}
});
let onerror_callback = Closure::<dyn FnMut(_)>::new(move |e: ErrorEvent| {
log(&format!("Error Received: {e:?}"));
CONNECTED = false;
});
let onclose_callback = Closure::<dyn FnMut(_)>::new(move |_e: ErrorEvent| {
log("Close Received");
CONNECTED = false;
});
let onopen_callback = Closure::<dyn FnMut(_)>::new(move |_e: ErrorEvent| {
log("Open Received");
CONNECTED = true;
let token = get_token();
send_token(token);
});
ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
ws.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref()));
// Prevent closures from recursing
onopen_callback.forget();
onclose_callback.forget();
onerror_callback.forget();
onmessage_callback.forget();
}
}
}
#[wasm_bindgen]
pub fn is_wasm_connected() -> bool {
unsafe { CONNECTED && WS.is_some() }
}
fn build_message(msg: WasmRequest) -> Vec<u8> {
let cbor = serde_cbor::to_vec(&msg).unwrap();
miniz_oxide::deflate::compress_to_vec(&cbor, 8)
}
fn send_message(msg: WasmRequest) {
log(&format!("Sending message: {msg:?}"));
let msg = build_message(msg);
unsafe {
if let Some(ws) = &mut WS {
ws.send_with_u8_array(&msg).unwrap();
}
}
}
#[wasm_bindgen]
pub fn send_token(token: String) {
//log(&format!("Sending token: {token}"));
if token.is_empty() {
log("Token is empty");
return;
}
send_message(WasmRequest::Auth { token });
}
#[wasm_bindgen]
pub fn send_login(license: String, username: String, password: String) {
let msg = WasmRequest::Login { license, username, password };
send_message(msg);
}
#[wasm_bindgen]
pub fn request_node_status() {
send_message(WasmRequest::GetNodeStatus);
}
#[wasm_bindgen]
pub fn request_packet_chart(period: String) {
send_message(WasmRequest::PacketChart { period });
}
#[wasm_bindgen]
pub fn request_packet_chart_for_node(period: String, node_id: String, node_name: String) {
send_message(WasmRequest::PacketChartSingle { period, node_id, node_name });
}
#[wasm_bindgen]
pub fn request_throughput_chart(period: String) {
send_message(WasmRequest::ThroughputChart { period });
}
#[wasm_bindgen]
pub fn request_throughput_chart_for_site(period: String, site_id: String) {
send_message(WasmRequest::ThroughputChartSite { period, site_id });
}
#[wasm_bindgen]
pub fn request_throughput_chart_for_node(period: String, node_id: String, node_name: String) {
send_message(WasmRequest::ThroughputChartSingle { period, node_id, node_name });
}
#[wasm_bindgen]
pub fn request_throughput_chart_for_circuit(period: String, circuit_id: String) {
send_message(WasmRequest::ThroughputChartCircuit { period, circuit_id });
}
#[wasm_bindgen]
pub fn request_site_stack(period: String, site_id: String) {
send_message(WasmRequest::SiteStack { period, site_id });
}
#[wasm_bindgen]
pub fn request_rtt_chart(period: String) {
send_message(WasmRequest::RttChart { period });
}
#[wasm_bindgen]
pub fn request_rtt_chart_for_site(period: String, site_id: String) {
send_message(WasmRequest::RttChartSite { period, site_id });
}
#[wasm_bindgen]
pub fn request_rtt_chart_for_node(period: String, node_id: String, node_name: String) {
send_message(WasmRequest::RttChartSingle { period, node_id, node_name });
}
#[wasm_bindgen]
pub fn request_rtt_chart_for_circuit(period: String, circuit_id: String) {
send_message(WasmRequest::RttChartCircuit { period, circuit_id });
}
#[wasm_bindgen]
pub fn request_node_perf_chart(period: String, node_id: String, node_name: String) {
send_message(WasmRequest::NodePerfChart { period, node_id, node_name });
}
#[wasm_bindgen]
pub fn request_root_heat(period: String) {
send_message(WasmRequest::RootHeat { period });
}
#[wasm_bindgen]
pub fn request_site_heat(period: String, site_id: String) {
send_message(WasmRequest::SiteHeat { period, site_id });
}
#[wasm_bindgen]
pub fn request_tree(parent: String) {
send_message(WasmRequest::Tree { parent });
}
#[wasm_bindgen]
pub fn request_site_info(site_id: String) {
send_message(WasmRequest::SiteInfo { site_id });
}
#[wasm_bindgen]
pub fn request_site_parents(site_id: String) {
send_message(WasmRequest::SiteParents { site_id });
}
#[wasm_bindgen]
pub fn request_search(term: String) {
send_message(WasmRequest::Search { term });
}
#[wasm_bindgen]
pub fn request_circuit_info(circuit_id: String) {
send_message(WasmRequest::CircuitInfo { circuit_id });
}

View File

@ -0,0 +1,10 @@
[package]
name = "wasm_pipe_types"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0.163", features = ["derive"] }
chrono = { version = "0", features = [ "serde" ] }

View File

@ -0,0 +1,165 @@
use std::collections::HashMap;
use chrono::{DateTime, FixedOffset};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
pub enum WasmRequest {
Auth { token: String },
Login { license: String, username: String, password: String },
GetNodeStatus,
PacketChart { period: String },
PacketChartSingle { period: String, node_id: String, node_name: String },
ThroughputChart { period: String },
ThroughputChartSingle { period: String, node_id: String, node_name: String },
ThroughputChartSite { period: String, site_id: String },
ThroughputChartCircuit { period: String, circuit_id: String },
RttChart { period: String },
RttChartSingle { period: String, node_id: String, node_name: String },
RttChartSite { period: String, site_id: String },
RttChartCircuit { period: String, circuit_id: String },
SiteStack { period: String, site_id: String },
RootHeat { period: String },
SiteHeat { period: String, site_id: String },
NodePerfChart { period: String, node_id: String, node_name: String },
Tree { parent: String },
SiteInfo { site_id: String },
SiteParents { site_id: String },
Search { term: String },
CircuitInfo { circuit_id: String },
}
#[derive(Serialize, Deserialize, Debug)]
pub enum WasmResponse {
AuthOk { token: String, name: String, license_key: String },
AuthFail,
LoginOk { token: String, name: String, license_key: String },
LoginFail,
NodeStatus { nodes: Vec<Node> },
PacketChart { nodes: Vec<PacketHost> },
BitsChart { nodes: Vec<ThroughputHost> },
RttChart { nodes: Vec<RttHost>, histogram: Vec<u32> },
RttChartSite { nodes: Vec<RttHost>, histogram: Vec<u32> },
RttChartCircuit { nodes: Vec<RttHost>, histogram: Vec<u32> },
SiteStack { nodes: Vec<ThroughputHost> },
RootHeat { data: HashMap<String, Vec<(DateTime<FixedOffset>, f64)>>},
SiteHeat { data: HashMap<String, Vec<(DateTime<FixedOffset>, f64)>>},
NodePerfChart { nodes: Vec<PerfHost> },
SiteTree { data: Vec<SiteTree> },
SiteInfo { data: SiteTree },
SiteParents { data: Vec<(String, String)> },
SiteChildren { data: Vec<(String, String, String)> },
SearchResult { hits: Vec<SearchResult> },
CircuitInfo { data: Vec<CircuitList> },
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Node {
pub node_id: String,
pub node_name: String,
pub last_seen: i32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct PacketHost {
pub node_id: String,
pub node_name: String,
pub down: Vec<Packets>,
pub up: Vec<Packets>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Packets {
pub value: f64,
pub date: String,
pub l: f64,
pub u: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ThroughputHost {
pub node_id: String,
pub node_name: String,
pub down: Vec<Throughput>,
pub up: Vec<Throughput>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Throughput {
pub value: f64,
pub date: String,
pub l: f64,
pub u: f64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ThroughputChart {
pub msg: String,
pub nodes: Vec<ThroughputHost>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Rtt {
pub value: f64,
pub date: String,
pub l: f64,
pub u: f64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RttHost {
pub node_id: String,
pub node_name: String,
pub rtt: Vec<Rtt>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct PerfHost {
pub node_id: String,
pub node_name: String,
pub stats: Vec<Perf>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Perf {
pub date: String,
pub cpu: f64,
pub cpu_max: f64,
pub ram: f64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SiteTree {
pub index: i32,
pub site_name: String,
pub site_type: String,
pub parent: i32,
pub max_down: i32,
pub max_up: i32,
pub current_down: i32,
pub current_up: i32,
pub current_rtt: i32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SearchResult {
pub name: String,
pub url: String,
pub score: f64,
pub icon: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CircuitList {
pub circuit_name: String,
pub device_id: String,
pub device_name: String,
pub parent_node: String,
pub mac: String,
pub download_min_mbps: i32,
pub download_max_mbps: i32,
pub upload_min_mbps: i32,
pub upload_max_mbps: i32,
pub comment: String,
pub ip_range: String,
pub subnet: i32,
}