Skip to content

Commit

Permalink
Merge branch 'config2_ui2' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
thebracket committed Feb 4, 2025
2 parents 01de67d + 1a600c2 commit d7fd99e
Show file tree
Hide file tree
Showing 48 changed files with 3,634 additions and 75 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,4 @@ src/ShapedDevices.csv.good
src/rust/lqosd/lts_keys.bin
src/rust/lqosd/src/node_manager/js_build/out
src/bin/dashboards
.aider*
2 changes: 1 addition & 1 deletion src/rust/lqos_bus/src/bus/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub async fn bus_request(requests: Vec<BusRequest>) -> Result<Vec<BusResponse>,
let stream = UnixStream::connect(BUS_SOCKET_PATH).await;
if let Err(e) = &stream {
if e.kind() == std::io::ErrorKind::NotFound {
error!("Unable to access {BUS_SOCKET_PATH}. Check that lqosd is running and you have appropriate permissions.");
//error!("Unable to access {BUS_SOCKET_PATH}. Check that lqosd is running and you have appropriate permissions.");
return Err(BusClientError::SocketNotFound);
}
}
Expand Down
26 changes: 21 additions & 5 deletions src/rust/lqos_config/src/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ impl From<&str> for UserRole {
}
}

impl From<String> for UserRole {
fn from(s: String) -> Self {
let s = s.to_lowercase();
if s == "admin" {
UserRole::Admin
} else {
UserRole::ReadOnly
}
}
}

impl Display for UserRole {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand All @@ -43,11 +54,11 @@ impl Display for UserRole {
}

#[derive(Clone, Debug, Deserialize, Serialize)]
struct WebUser {
username: String,
password_hash: String,
role: UserRole,
token: String,
pub struct WebUser {
pub username: String,
pub password_hash: String,
pub role: UserRole,
pub token: String,
}

/// Container holding the authorized web users.
Expand Down Expand Up @@ -237,6 +248,11 @@ impl WebUsers {
Ok(())
}

/// Return a list of user objects
pub fn get_users(&self) -> Vec<WebUser> {
self.users.clone()
}

/// Sets the "allow unauthenticated users" field. If true,
/// unauthenticated users gain read-only access. This is useful
/// for demonstration purposes.
Expand Down
2 changes: 1 addition & 1 deletion src/rust/lqos_config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod network_json;
mod program_control;
mod shaped_devices;

pub use authentication::{UserRole, WebUsers};
pub use authentication::{UserRole, WebUsers, WebUser};
pub use etc::{load_config, Config, enable_long_term_stats, Tunables, BridgeConfig, update_config, disable_xdp_bridge};
pub use network_json::{NetworkJson, NetworkJsonNode, NetworkJsonTransport};
pub use program_control::load_libreqos;
Expand Down
2 changes: 1 addition & 1 deletion src/rust/lqosd/copy_files.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
echo "Copying static"
mkdir ../../bin/static2/
mkdir -p ../../bin/static2/
cp -v -R src/node_manager/static2/* ../../bin/static2/
echo "Done"
pushd src/node_manager/js_build || exit
Expand Down
2 changes: 1 addition & 1 deletion src/rust/lqosd/src/node_manager/js_build/esbuild.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
set -e
scripts=( index.js template.js login.js first-run.js shaped-devices.js tree.js help.js unknown-ips.js configuration.js circuit.js flow_map.js all_tree_sankey.js asn_explorer.js lts_trial.js )
scripts=( index.js template.js login.js first-run.js shaped-devices.js tree.js help.js unknown-ips.js configuration.js circuit.js flow_map.js all_tree_sankey.js asn_explorer.js lts_trial.js config_general.js config_anon.js config_tuning.js config_queues.js config_lts.js config_iprange.js config_flows.js config_integration.js config_spylnx.js config_uisp.js config_powercode.js config_sonar.js config_interface.js config_network.js config_devices.js config_users.js )
for script in "${scripts[@]}"
do
echo "Building {$script}"
Expand Down
135 changes: 135 additions & 0 deletions src/rust/lqosd/src/node_manager/js_build/src/config/config_helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
export function loadConfig(onComplete) {
$.get("/local-api/getConfig", (data) => {
window.config = data;
onComplete();
});
}

export function saveConfig(onComplete) {
$.ajax({
type: "POST",
url: "/local-api/updateConfig",
data: JSON.stringify(window.config),
contentType: 'application/json',
success: () => {
onComplete();
},
error: () => {
alert("That didn't work");
}
});
}

export function saveNetworkAndDevices(network_json, shaped_devices, onComplete) {
// Validate network_json structure
if (!network_json || typeof network_json !== 'object') {
alert("Invalid network configuration");
return;
}

// Validate shaped_devices structure
if (!Array.isArray(shaped_devices)) {
alert("Invalid shaped devices configuration");
return;
}

// Validate individual shaped devices
const validationErrors = [];
const validNodes = validNodeList(network_json);
console.log(validNodes);

shaped_devices.forEach((device, index) => {
// Required fields
if (!device.circuit_id || device.circuit_id.trim() === "") {
validationErrors.push(`Device ${index + 1}: Circuit ID is required`);
}
if (!device.device_id || device.device_id.trim() === "") {
validationErrors.push(`Device ${index + 1}: Device ID is required`);
}

// Parent node validation
if (device.parent_node && validNodes.length > 0 && !validNodes.includes(device.parent_node)) {
validationErrors.push(`Device ${index + 1}: Parent node '${device.parent_node}' does not exist`);
}

// Bandwidth validation
if (device.download_min_mbps < 1 || device.upload_min_mbps < 1 ||
device.download_max_mbps < 1 || device.upload_max_mbps < 1) {
validationErrors.push(`Device ${index + 1}: Bandwidth values must be greater than 0`);
}
});

if (validationErrors.length > 0) {
alert("Validation errors:\n" + validationErrors.join("\n"));
return;
}

// Prepare data for submission
const submission = {
network_json,
shaped_devices
};
console.log(submission);

// Send to server with enhanced error handling
/*$.ajax({
type: "POST",
url: "/local-api/updateNetworkAndDevices",
contentType: 'application/json',
data: JSON.stringify(submission),
dataType: 'json', // Expect JSON response
success: (response) => {
try {
if (response && response.success) {
if (onComplete) onComplete(true, "Saved successfully");
} else {
const msg = response?.message || "Unknown error occurred";
if (onComplete) onComplete(false, msg);
alert("Failed to save: " + msg);
}
} catch (e) {
console.error("Error parsing response:", e);
if (onComplete) onComplete(false, "Invalid server response");
alert("Invalid server response format");
}
},
error: (xhr) => {
let errorMsg = "Request failed";
try {
if (xhr.responseText) {
const json = JSON.parse(xhr.responseText);
errorMsg = json.message || xhr.responseText;
} else if (xhr.statusText) {
errorMsg = xhr.statusText;
}
console.error("AJAX Error:", {
status: xhr.status,
statusText: xhr.statusText,
response: xhr.responseText
});
} catch (e) {
console.error("Error parsing error response:", e);
errorMsg = "Unknown error occurred";
}
if (onComplete) onComplete(false, errorMsg);
alert("Error saving configuration: " + errorMsg);
}
});*/
}

export function validNodeList(network_json) {
let nodes = [];

function iterate(data, level) {
for (const [key, value] of Object.entries(data)) {
nodes.push(key);
if (value.children != null)
iterate(value.children, level+1);
}
}

iterate(network_json, 0);

return nodes;
}
42 changes: 42 additions & 0 deletions src/rust/lqosd/src/node_manager/js_build/src/config_anon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {saveConfig, loadConfig} from "./config/config_helper";

function validateConfig() {
// Validate server address format if provided
const server = document.getElementById("anonymousServer").value.trim();
if (server) {
const parts = server.split(':');
if (parts.length !== 2 || isNaN(parseInt(parts[1]))) {
alert("Statistics Server must be in format HOST:PORT");
return false;
}
}
return true;
}

function updateConfig() {
// Update only the usage stats section
window.config.usage_stats.send_anonymous = document.getElementById("sendAnonymous").checked;
window.config.usage_stats.anonymous_server = document.getElementById("anonymousServer").value.trim();
}

loadConfig(() => {
// window.config now contains the configuration.
// Populate form fields with config values
if (window.config && window.config.usage_stats) {
// Required fields
document.getElementById("sendAnonymous").checked = window.config.usage_stats.send_anonymous ?? true;
document.getElementById("anonymousServer").value = window.config.usage_stats.anonymous_server ?? "stats.libreqos.io:9125";

// Add save button click handler
document.getElementById('saveButton').addEventListener('click', () => {
if (validateConfig()) {
updateConfig();
saveConfig(() => {
alert("Configuration saved successfully!");
});
}
});
} else {
console.error("Usage statistics configuration not found in window.config");
}
});
Loading

0 comments on commit d7fd99e

Please sign in to comment.