Version checks runs in a thread separate from the UI, and the UI no longer has to wait for a version check to happen.

This commit is contained in:
Herbert Wolverson
2024-12-21 08:43:52 -06:00
parent 5de9a29f09
commit 1ac32d7b40
6 changed files with 130 additions and 16 deletions

34
src/rust/Cargo.lock generated
View File

@@ -2105,6 +2105,7 @@ dependencies = [
"tower-http",
"tracing",
"tracing-subscriber",
"ureq",
"uuid",
"zerocopy 0.8.9",
]
@@ -2980,11 +2981,13 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.16"
version = "0.23.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -3946,6 +3949,24 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"url",
"webpki-roots",
]
[[package]]
name = "url"
version = "2.5.3"
@@ -4112,6 +4133,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.26.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi"
version = "0.3.9"

View File

@@ -61,3 +61,4 @@ allocative_derive = "0.3.3"
#[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
#jemallocator = { workspace = true }
mimalloc = { workspace = true }
ureq = { version = "2.12.1", features = ["json"] }

View File

@@ -16,6 +16,7 @@ mod system_stats;
mod blackboard;
mod remote_commands;
pub mod lts2_sys;
mod version_checks;
#[cfg(feature = "flamegraphs")]
use std::io::Write;
@@ -164,6 +165,7 @@ fn main() -> Result<()> {
throughput_tracker::spawn_throughput_monitor(long_term_stats_tx.clone(), flow_tx, system_usage_tx.clone())?;
spawn_queue_monitor()?;
lqos_sys::bpf_garbage_collector();
version_checks::start_version_check()?;
// Handle signals
let mut signals = Signals::new([SIGINT, SIGHUP, SIGTERM])?;

View File

@@ -1,11 +1,9 @@
export function checkForUpgrades(parentId) {
$.get("/local-api/versionCheck", (data) => {
if (data === "Update available") {
let div = document.createElement("div");
div.innerHTML = "<i class='fa fa-info-circle'></i> A New Version of LibreQoS is Available";
div.classList.add("alert", "alert-success");
let parent = document.getElementById(parentId);
parent.appendChild(div);
}
});
if (window.newVersion) {
let div = document.createElement("div");
div.innerHTML = "<i class='fa fa-info-circle'></i> A New Version of LibreQoS is Available";
div.classList.add("alert", "alert-success");
let parent = document.getElementById(parentId);
parent.appendChild(div);
}
}

View File

@@ -43,6 +43,14 @@ const LTS1_LINK_OFFER_TRIAL: &str = r#"
</li>
"#;
fn js_tf(b: bool) -> &'static str {
if b {
"true"
} else {
"false"
}
}
pub async fn apply_templates(
jar: CookieJar,
req: Request<axum::body::Body>,
@@ -68,7 +76,10 @@ pub async fn apply_templates(
let template_text = template_text.replace("%%USERNAME%%", &username);
let res = next.run(req).await;
let mut lts_script = "<script>window.hasLts = false;</script>";
//let mut lts_script = "<script>window.hasLts = false;</script>";
let mut script_has_lts = false;
let mut script_has_insight = false;
let new_version = crate::version_checks::new_version_available();
if apply_template {
// Check to see if the box is participating in the Insight Alpha Test
@@ -84,20 +95,23 @@ pub async fn apply_templates(
_ => {
// Link to it
trial_link = INSIGHT_LINK_ACTIVE.to_string();
lts_script = "<script>window.hasLts = true; window.hasInsight = true;</script>";
script_has_insight = true;
script_has_lts = true;
}
}
} else {
if config.long_term_stats.gather_stats && config.long_term_stats.license_key.is_some() {
// LTS is enabled
trial_link = LTS1_LINK_ACTIVE.to_string();
lts_script = "<script>window.hasLts = true; window.hasInsight = false;</script>";
script_has_lts = true;
script_has_insight = false;
} else {
trial_link = LTS1_LINK_OFFER_TRIAL.replace(
"%%LTS_TRIAL_LINK%%",
&format!("https://stats.libreqos.io/trial1/{}", config.node_id)
);
lts_script = "<script>window.hasLts = false; window.hasInsight = false;</script>";
script_has_insight = false;
script_has_insight = false;
}
}
@@ -107,6 +121,10 @@ pub async fn apply_templates(
title = config.node_name.clone();
}
// "LTS script" - which is increasingly becoming a misnomer
let lts_script = format!("<script>window.hasLts = {}; window.hasInsight = {}; window.newVersion = {};</script>",
js_tf(script_has_lts), js_tf(script_has_insight), js_tf(new_version));
let (mut res_parts, res_body) = res.into_parts();
let bytes = to_bytes(res_body, 1_000_000).await.unwrap();
let byte_string = String::from_utf8_lossy(&bytes).to_string();
@@ -115,7 +133,7 @@ pub async fn apply_templates(
.replace("%%VERSION%%", VERSION_STRING)
.replace("%%TITLE%%", &title)
.replace("%%LTS_LINK%%", &trial_link)
.replace("%%%LTS_SCRIPT%%%", lts_script);
.replace("%%%LTS_SCRIPT%%%", &lts_script);
if let Some(length) = res_parts.headers.get_mut("content-length") {
*length = HeaderValue::from(byte_string.len());
}

View File

@@ -0,0 +1,65 @@
//! Moves version checking out of the web system and into its
//! own module/thread/actor. This removes any delay when the
//! web system is running without Internet access.
use std::sync::atomic::AtomicBool;
use std::thread;
use anyhow::Result;
use serde::{Deserialize, Serialize};
const VERSION_STRING: &str = include_str!("../../../VERSION_STRING");
#[derive(Serialize)]
struct VersionCheckRequest {
current_git_hash: String,
version_string: String,
node_id: String,
}
#[derive(Deserialize, Debug, Default)]
pub struct VersionCheckResponse {
update_available: bool,
}
static NEW_VERSION_AVAILABLE: AtomicBool = AtomicBool::new(false);
/// Initializes the version checking system.
pub fn start_version_check() -> Result<()> {
thread::Builder::new()
.name("version_check".to_string())
.spawn(|| {
loop {
let Ok(cfg) = lqos_config::load_config() else {
continue;
};
let current_hash = env!("GIT_HASH");
let request = VersionCheckRequest {
current_git_hash: current_hash.to_string(),
version_string: VERSION_STRING.to_string(),
node_id: cfg.node_id.to_string(),
};
let update_available = check_version(request).unwrap_or(false);
NEW_VERSION_AVAILABLE.store(update_available, std::sync::atomic::Ordering::Relaxed);
// Sleep for 12 hours
thread::sleep(std::time::Duration::from_secs(12 * 60 * 60));
}
})
.expect("Failed to start version check thread");
Ok(())
}
/// Returns true if a new version is available.
pub fn new_version_available() -> bool {
NEW_VERSION_AVAILABLE.load(std::sync::atomic::Ordering::Relaxed)
}
fn check_version(request: VersionCheckRequest) -> Result<bool> {
let response: VersionCheckResponse = ureq::post("https://insight.libreqos.com/shaper_api/version_check")
.send_json(serde_json::to_value(&request)?)?
.into_json()?;
Ok(response.update_available)
}