From e161addb5b1a2f8e380a48bc07eebdfd052bcaee Mon Sep 17 00:00:00 2001 From: Herbert Wolverson Date: Sun, 29 Mar 2026 16:58:01 -0500 Subject: [PATCH] Feat: add Insight signup handoff --- .../src/node_manager/js_build/entrypoints.txt | 1 + .../node_manager/js_build/src/lts_trial.js | 76 ++-- .../js_build/src/lts_trial_setup.js | 97 +++++ .../js_build/src/lts_trial_shared.js | 58 +++ .../lqosd/src/node_manager/local_api/lts.rs | 352 +++++++++++++++++- .../src/node_manager/static2/lts_trial.html | 5 + .../node_manager/static2/lts_trial_setup.html | 37 ++ .../lqosd/src/node_manager/static_pages.rs | 1 + src/rust/lqosd/src/node_manager/ws.rs | 28 ++ .../lqosd/src/node_manager/ws/messages.rs | 4 + 10 files changed, 593 insertions(+), 66 deletions(-) create mode 100644 src/rust/lqosd/src/node_manager/js_build/src/lts_trial_setup.js create mode 100644 src/rust/lqosd/src/node_manager/js_build/src/lts_trial_shared.js create mode 100644 src/rust/lqosd/src/node_manager/static2/lts_trial_setup.html diff --git a/src/rust/lqosd/src/node_manager/js_build/entrypoints.txt b/src/rust/lqosd/src/node_manager/js_build/entrypoints.txt index b8d41373..36fd33a5 100644 --- a/src/rust/lqosd/src/node_manager/js_build/entrypoints.txt +++ b/src/rust/lqosd/src/node_manager/js_build/entrypoints.txt @@ -13,6 +13,7 @@ flow_map.js all_tree_sankey.js asn_analysis.js lts_trial.js +lts_trial_setup.js config_general.js config_rtt.js config_tuning.js diff --git a/src/rust/lqosd/src/node_manager/js_build/src/lts_trial.js b/src/rust/lqosd/src/node_manager/js_build/src/lts_trial.js index 86f93648..38ec2e6b 100644 --- a/src/rust/lqosd/src/node_manager/js_build/src/lts_trial.js +++ b/src/rust/lqosd/src/node_manager/js_build/src/lts_trial.js @@ -1,37 +1,5 @@ import {PLACEHOLDER_TEASERS} from "./lts_teasers_shared"; -import {get_ws_client} from "./pubsub/ws"; - -const wsClient = get_ws_client(); -const listenOnce = (eventName, handler) => { - const wrapped = (msg) => { - wsClient.off(eventName, wrapped); - handler(msg); - }; - wsClient.on(eventName, wrapped); -}; - -function sendWsRequest(responseEvent, request) { - return new Promise((resolve, reject) => { - let done = false; - const onResponse = (msg) => { - if (done) return; - done = true; - wsClient.off(responseEvent, onResponse); - wsClient.off("Error", onError); - resolve(msg); - }; - const onError = (msg) => { - if (done) return; - done = true; - wsClient.off(responseEvent, onResponse); - wsClient.off("Error", onError); - reject(msg); - }; - wsClient.on(responseEvent, onResponse); - wsClient.on("Error", onError); - wsClient.send(request); - }); -} +import {buildSignupApiUrl, sendWsRequest, wsClient} from "./lts_trial_shared"; // Paddle-compatible countries (excluding embargoed nations) const ALLOWED_COUNTRIES = [ @@ -288,6 +256,7 @@ $(document).ready(async function() { loadTeasers(); // Then load teasers with the correct URL fetchCircuitCount(); attachEventHandlers(); + openInitialViewFromQuery(); // Apply dark mode if needed if (isDarkMode()) { @@ -434,7 +403,7 @@ function displayTeasers() { // Add click handlers to CTA buttons $('.btn-teaser-cta').on('click', function() { - showSection('signupSection'); + redirectToTrialSetup(); }); // Reinitialize carousel if needed @@ -448,19 +417,7 @@ function displayTeasers() { // Helper function to construct full LTS URLs function getLtsUrl(endpoint) { - // Ensure ltsBaseUrl starts with http:// or https://, otherwise prefix with https:// - let baseUrl = ltsBaseUrl; - if (!/^https?:\/\//i.test(baseUrl)) { - baseUrl = 'https://' + baseUrl; - } - // Ensure baseUrl ends with a single slash - let base = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/'; - // Ensure 'signup-api/' is appended exactly once - base += base.endsWith('signup-api/') ? '' : 'signup-api/'; - // Remove any leading slash from endpoint - endpoint = endpoint.replace(/^\/+/, ''); - //console.log("Base: ", base, "Endpoint:", endpoint); - return base + endpoint; + return buildSignupApiUrl(ltsBaseUrl, endpoint); } // Fetch node ID and LTS URL from configuration @@ -517,6 +474,29 @@ function showSection(sectionId) { $(`#${sectionId}`).fadeIn(); } +function redirectToTrialSetup() { + window.location.href = 'lts_trial_setup.html'; +} + +function openInitialViewFromQuery() { + const params = new URLSearchParams(window.location.search); + const view = params.get('view'); + + if (view === 'signup') { + showSection('signupSection'); + return; + } + + if (view === 'license') { + showSection('licenseKeySection'); + return; + } + + if (view === 'recover') { + showSection('licenseRecoverySection'); + } +} + // Show loading modal function showLoading(message = 'Processing...') { $('#loadingMessage').text(message); @@ -579,7 +559,7 @@ function attachEventHandlers() { // Analytics tracking for new customer path const trackingImg = new Image(); trackingImg.src = getLtsUrl('signupPing') + '?t=' + Date.now() + '&type=new'; - showSection('signupSection'); + redirectToTrialSetup(); }); $('#btnBackFromLicense').on('click', () => showSection('teaserSection')); $('#btnBackFromSignup').on('click', () => showSection('teaserSection')); diff --git a/src/rust/lqosd/src/node_manager/js_build/src/lts_trial_setup.js b/src/rust/lqosd/src/node_manager/js_build/src/lts_trial_setup.js new file mode 100644 index 00000000..f6adb028 --- /dev/null +++ b/src/rust/lqosd/src/node_manager/js_build/src/lts_trial_setup.js @@ -0,0 +1,97 @@ +import {buildInsightUrl, sendWsRequest} from "./lts_trial_shared"; + +const setupStatusClasses = [ + 'alert-primary', + 'alert-success', + 'alert-warning', + 'alert-danger', + 'alert-secondary', +]; +const defaultInsightBaseUrl = 'https://insight.libreqos.com/'; +let insightBaseUrl = defaultInsightBaseUrl; + +function setSetupStatus(message, tone = 'primary', spinning = true) { + const status = $('#signupSetupStatus'); + const statusText = $('#signupSetupStatusText'); + const spinner = status.find('.spinner-border'); + + if (!status.length || !statusText.length) { + return; + } + + status.removeClass(setupStatusClasses.join(' ')).addClass(`alert-${tone}`); + statusText.text(message); + + if (spinning) { + spinner.show(); + } else { + spinner.hide(); + } +} + +function setSetupHelp(message) { + $('#signupSetupHelp').text(message); +} + +function getErrorMessage(error, fallback) { + if (error && typeof error.message === 'string' && error.message.trim().length > 0) { + return error.message.trim(); + } + return fallback; +} + +async function fetchTrialConfig() { + try { + const response = await sendWsRequest("LtsTrialConfigResult", { LtsTrialConfig: {} }); + const data = response && response.data ? response.data : {}; + if (typeof data.lts_url === 'string' && data.lts_url.trim().length > 0) { + insightBaseUrl = data.lts_url; + } + } catch (error) { + console.warn('Unable to load LTS trial config, using default Insight URL.', error); + insightBaseUrl = defaultInsightBaseUrl; + } +} + +function redirectToInsightSignup(claimId) { + window.location.href = buildInsightUrl( + insightBaseUrl, + `su/signup1.html?${encodeURIComponent(claimId)}`, + ); +} + +async function startSignupSession() { + setSetupStatus('Creating your Insight signup session…'); + setSetupHelp('LibreQoS is contacting Insight and preparing your claim.'); + await fetchTrialConfig(); + + try { + const response = await sendWsRequest("LtsStartSignupResult", { LtsStartSignup: {} }); + const claimId = response && typeof response.claim_id === 'string' + ? response.claim_id.trim() + : ''; + + if (!claimId) { + throw new Error('Missing claim ID from signup response.'); + } + + setSetupStatus('Signup session ready. Redirecting to Insight…', 'success', false); + setSetupHelp('Your signup session is ready. Continuing to Insight now.'); + redirectToInsightSignup(claimId); + } catch (error) { + console.error('Failed to start Insight signup session.', error); + setSetupStatus( + getErrorMessage(error, 'Unable to start the Insight signup session right now.'), + 'danger', + false, + ); + setSetupHelp( + 'Retry this page, use the direct signup form, or validate an existing license key below.', + ); + } +} + +$(document).ready(() => { + setSetupStatus('Preparing the Insight signup handoff…'); + void startSignupSession(); +}); diff --git a/src/rust/lqosd/src/node_manager/js_build/src/lts_trial_shared.js b/src/rust/lqosd/src/node_manager/js_build/src/lts_trial_shared.js new file mode 100644 index 00000000..c2386e31 --- /dev/null +++ b/src/rust/lqosd/src/node_manager/js_build/src/lts_trial_shared.js @@ -0,0 +1,58 @@ +import {get_ws_client} from "./pubsub/ws"; + +const DEFAULT_INSIGHT_BASE_URL = 'https://insight.libreqos.com/'; +const wsClient = get_ws_client(); + +function normalizeInsightBaseUrl(rawBaseUrl) { + let baseUrl = (rawBaseUrl || DEFAULT_INSIGHT_BASE_URL).trim(); + + if (!baseUrl) { + baseUrl = DEFAULT_INSIGHT_BASE_URL; + } + + if (!/^https?:\/\//i.test(baseUrl)) { + baseUrl = `https://${baseUrl}`; + } + + baseUrl = baseUrl.replace(/\/+$/, ''); + if (baseUrl.endsWith('/signup-api')) { + baseUrl = baseUrl.slice(0, -'/signup-api'.length); + } + + return `${baseUrl}/`; +} + +function buildInsightUrl(rawBaseUrl, endpoint) { + const path = String(endpoint || '').replace(/^\/+/, ''); + return `${normalizeInsightBaseUrl(rawBaseUrl)}${path}`; +} + +function buildSignupApiUrl(rawBaseUrl, endpoint) { + const path = String(endpoint || '').replace(/^\/+/, ''); + return buildInsightUrl(rawBaseUrl, `signup-api/${path}`); +} + +function sendWsRequest(responseEvent, request) { + return new Promise((resolve, reject) => { + let done = false; + const onResponse = (msg) => { + if (done) return; + done = true; + wsClient.off(responseEvent, onResponse); + wsClient.off("Error", onError); + resolve(msg); + }; + const onError = (msg) => { + if (done) return; + done = true; + wsClient.off(responseEvent, onResponse); + wsClient.off("Error", onError); + reject(msg); + }; + wsClient.on(responseEvent, onResponse); + wsClient.on("Error", onError); + wsClient.send(request); + }); +} + +export {buildInsightUrl, buildSignupApiUrl, normalizeInsightBaseUrl, sendWsRequest, wsClient}; diff --git a/src/rust/lqosd/src/node_manager/local_api/lts.rs b/src/rust/lqosd/src/node_manager/local_api/lts.rs index 9d12686f..89c690bf 100644 --- a/src/rust/lqosd/src/node_manager/local_api/lts.rs +++ b/src/rust/lqosd/src/node_manager/local_api/lts.rs @@ -1,8 +1,10 @@ mod last_24_hours; mod shaper_status; +use crate::lts2_sys::lts2_client::{LicenseStatus, set_license_status}; use crate::lts2_sys::shared_types::LtsStatus; use crate::node_manager::auth::LoginResult; +use crate::node_manager::local_api::circuit_count; use axum::http::StatusCode; pub use last_24_hours::*; use lqos_bus::{BusRequest, bus_request}; @@ -10,9 +12,14 @@ use lqos_config::load_config; use serde::{Deserialize, Serialize}; pub use shaper_status::ShaperStatus; pub use shaper_status::shaper_status_data; -use std::ops::Deref; use std::process::Command; -use tracing::{info, warn}; +use std::time::Duration; +use tracing::{debug, info, warn}; +use uuid::Uuid; + +const SIGNUP_POLL_INTERVAL: Duration = Duration::from_secs(10); +const INSIGHT_FREE_TRIAL_STATUS_CODE: i32 = 2; +const INSIGHT_TRIAL_DAYS_HINT: i32 = 30; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct LtsTrialConfig { @@ -20,6 +27,37 @@ pub struct LtsTrialConfig { pub lts_url: Option, } +#[derive(Debug, Serialize)] +struct SignupStartRequest { + node_id: String, + node_name: String, + active_circuits: usize, +} + +#[derive(Debug, Deserialize)] +struct SignupStartResponse { + claim_id: Uuid, +} + +#[derive(Debug, Serialize)] +struct SignupCheckRequest { + claim_id: String, +} + +#[derive(Debug, Deserialize)] +struct SignupCheckResponse { + status: String, + #[serde(default)] + account_key: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum SignupCheckState { + Pending, + Provisioned { account_key: Uuid }, + Expired, +} + pub fn lts_trial_config_data(login: LoginResult) -> Result { if login != LoginResult::Admin { return Err(StatusCode::FORBIDDEN); @@ -31,6 +69,221 @@ pub fn lts_trial_config_data(login: LoginResult) -> Result) -> String { + let mut base_url = lts_url + .map(str::trim) + .filter(|value| !value.is_empty()) + .unwrap_or("https://insight.libreqos.com/") + .to_string(); + + if !base_url.starts_with("http://") && !base_url.starts_with("https://") { + base_url = format!("https://{base_url}"); + } + + while base_url.ends_with('/') { + base_url.pop(); + } + + if let Some(stripped) = base_url.strip_suffix("/signup-api") { + base_url = stripped.to_string(); + } + + format!("{base_url}/") +} + +fn build_insight_url(lts_url: Option<&str>, path: &str) -> String { + let path = path.trim_start_matches('/'); + format!("{}{}", normalize_insight_base_url(lts_url), path) +} + +fn insight_http_client() -> Result { + reqwest::Client::builder() + .connect_timeout(Duration::from_secs(5)) + .timeout(Duration::from_secs(15)) + .danger_accept_invalid_certs(true) + .build() + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) +} + +fn classify_signup_check_response(response: SignupCheckResponse) -> Result { + match response.status.as_str() { + "pending" => Ok(SignupCheckState::Pending), + "expired" => Ok(SignupCheckState::Expired), + "provisioned" => response + .account_key + .map(|account_key| SignupCheckState::Provisioned { account_key }) + .ok_or(()), + _ => Err(()), + } +} + +async fn apply_insight_license(license_key: String, restart_lqos_api: bool) -> Result<(), StatusCode> { + let license_key = license_key.trim().to_string(); + if license_key.is_empty() { + return Err(StatusCode::BAD_GATEWAY); + } + + let mut cfg = load_config() + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? + .as_ref() + .clone(); + cfg.long_term_stats.gather_stats = true; + cfg.long_term_stats.license_key = Some(license_key.clone()); + bus_request(vec![BusRequest::UpdateLqosdConfig(Box::new(cfg))]) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + set_license_status(LicenseStatus { + license_type: INSIGHT_FREE_TRIAL_STATUS_CODE, + trial_expires: INSIGHT_TRIAL_DAYS_HINT, + }); + + info!("LQOSD configuration updated with new Insight license key."); + + if restart_lqos_api { + let _ = Command::new("/bin/systemctl") + .args(["restart", "lqos_api"]) + .output(); + } + + Ok(()) +} + +pub async fn lts_trial_start_signup_data() -> Result { + let config = load_config().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let start_url = build_insight_url(config.long_term_stats.lts_url.as_deref(), "/su/start"); + let request = SignupStartRequest { + node_id: config.node_id.clone(), + node_name: config.node_name.clone(), + active_circuits: circuit_count::circuit_count_data().count, + }; + debug!( + node_id = %request.node_id, + active_circuits = request.active_circuits, + url = %start_url, + "starting Insight signup session" + ); + + let client = insight_http_client()?; + + let response = client + .post(&start_url) + .json(&request) + .send() + .await + .map_err(|_| StatusCode::BAD_GATEWAY)?; + + if !response.status().is_success() { + warn!( + status = %response.status(), + url = %start_url, + "Insight signup start request failed" + ); + return Err(StatusCode::BAD_GATEWAY); + } + + let response = response + .json::() + .await + .map_err(|_| StatusCode::BAD_GATEWAY)?; + + Ok(response.claim_id.to_string()) +} + +pub fn spawn_signup_poll_loop(claim_id: String) { + tokio::spawn(async move { + if let Err(status) = poll_signup_until_complete(claim_id.clone()).await { + warn!( + claim_id = %claim_id, + status = %status, + "Insight signup poll loop stopped with an error" + ); + } + }); +} + +async fn poll_signup_until_complete(claim_id: String) -> Result<(), StatusCode> { + let config = load_config().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let check_url = build_insight_url(config.long_term_stats.lts_url.as_deref(), "/su/check"); + let client = insight_http_client()?; + let request = SignupCheckRequest { + claim_id: claim_id.clone(), + }; + + loop { + tokio::time::sleep(SIGNUP_POLL_INTERVAL).await; + + let response = match client.post(&check_url).json(&request).send().await { + Ok(response) => response, + Err(error) => { + warn!( + claim_id = %claim_id, + error = ?error, + "Insight signup status poll failed; retrying" + ); + continue; + } + }; + + if !response.status().is_success() { + warn!( + claim_id = %claim_id, + status = %response.status(), + "Insight signup status poll returned non-success; retrying" + ); + continue; + } + + let response = match response.json::().await { + Ok(response) => response, + Err(error) => { + warn!( + claim_id = %claim_id, + error = ?error, + "Insight signup status poll returned invalid JSON; retrying" + ); + continue; + } + }; + + match classify_signup_check_response(response) { + Ok(SignupCheckState::Pending) => {} + Ok(SignupCheckState::Expired) => { + debug!(claim_id = %claim_id, "Insight signup claim expired"); + return Ok(()); + } + Ok(SignupCheckState::Provisioned { account_key }) => { + match apply_insight_license(account_key.to_string(), false).await { + Ok(()) => { + info!( + claim_id = %claim_id, + account_key = %account_key, + "Applied Insight license from signup flow" + ); + return Ok(()); + } + Err(status) => { + warn!( + claim_id = %claim_id, + account_key = %account_key, + status = %status, + "Failed to apply Insight license; stopping signup poll loop" + ); + return Err(status); + } + } + } + Err(()) => { + warn!( + claim_id = %claim_id, + "Insight signup status poll returned an unexpected payload" + ); + return Err(StatusCode::BAD_GATEWAY); + } + } + } +} + pub(crate) async fn insight_gate() -> Result<(), StatusCode> { let (status, _) = crate::lts2_sys::get_lts_license_status_async().await; match status { @@ -56,20 +309,83 @@ pub async fn lts_trial_signup_data(license_key: String) -> Result<(), StatusCode return Err(StatusCode::INTERNAL_SERVER_ERROR); } info!("Free trial request succeeded, license key: {}", license_key); - let mut cfg = load_config() - .expect("Unable to load LibreQoS config") - .deref() - .clone(); - cfg.long_term_stats.gather_stats = true; - cfg.long_term_stats.license_key = Some(license_key); - bus_request(vec![BusRequest::UpdateLqosdConfig(Box::new(cfg))]) - .await - .expect("Unable to update lqosd config"); - info!("LQOSD configuration updated with new license key."); - // Best-effort: ensure the bundled lqos_api also reloads to pick up the new license - // Ignore errors if systemctl isn't present or permission is denied. - let _ = Command::new("/bin/systemctl") - .args(["restart", "lqos_api"]) - .output(); - Ok(()) + apply_insight_license(license_key, true).await +} + +#[cfg(test)] +mod tests { + use super::{ + SignupCheckResponse, SignupCheckState, build_insight_url, classify_signup_check_response, + normalize_insight_base_url, + }; + use uuid::Uuid; + + #[test] + fn normalize_insight_base_url_defaults_to_hosted_insight() { + assert_eq!( + normalize_insight_base_url(None), + "https://insight.libreqos.com/" + ); + } + + #[test] + fn normalize_insight_base_url_adds_scheme_and_trailing_slash() { + assert_eq!( + normalize_insight_base_url(Some("insight.example.com")), + "https://insight.example.com/" + ); + } + + #[test] + fn normalize_insight_base_url_strips_signup_api_suffix() { + assert_eq!( + normalize_insight_base_url(Some("https://insight.example.com/signup-api/")), + "https://insight.example.com/" + ); + } + + #[test] + fn build_insight_url_joins_paths_cleanly() { + assert_eq!( + build_insight_url(Some("insight.example.com"), "/su/start"), + "https://insight.example.com/su/start" + ); + } + + #[test] + fn classify_signup_check_response_handles_pending() { + let response = SignupCheckResponse { + status: "pending".to_string(), + account_key: None, + }; + + assert_eq!( + classify_signup_check_response(response), + Ok(SignupCheckState::Pending) + ); + } + + #[test] + fn classify_signup_check_response_requires_account_key_for_provisioned() { + let response = SignupCheckResponse { + status: "provisioned".to_string(), + account_key: None, + }; + + assert!(classify_signup_check_response(response).is_err()); + } + + #[test] + fn classify_signup_check_response_returns_provisioned_account_key() { + let account_key = Uuid::new_v4(); + let response = SignupCheckResponse { + status: "provisioned".to_string(), + account_key: Some(account_key), + }; + + assert_eq!( + classify_signup_check_response(response), + Ok(SignupCheckState::Provisioned { account_key }) + ); + } } diff --git a/src/rust/lqosd/src/node_manager/static2/lts_trial.html b/src/rust/lqosd/src/node_manager/static2/lts_trial.html index 4bcbc422..427eca7e 100644 --- a/src/rust/lqosd/src/node_manager/static2/lts_trial.html +++ b/src/rust/lqosd/src/node_manager/static2/lts_trial.html @@ -164,6 +164,11 @@ body.dark-mode .card-header.bg-light { + diff --git a/src/rust/lqosd/src/node_manager/static2/lts_trial_setup.html b/src/rust/lqosd/src/node_manager/static2/lts_trial_setup.html new file mode 100644 index 00000000..4808c4c6 --- /dev/null +++ b/src/rust/lqosd/src/node_manager/static2/lts_trial_setup.html @@ -0,0 +1,37 @@ +
+
+
+
+

+ + Preparing Your Insight Sign-Up Session +

+
+
+

+ LibreQoS will create a short-lived Insight claim and then hand your browser to the hosted signup flow. +

+
+ + Preparing the Insight signup handoff… +
+

+ Existing license validation and the direct signup form remain available if you need them. +

+ +
+
+
+
+ + diff --git a/src/rust/lqosd/src/node_manager/static_pages.rs b/src/rust/lqosd/src/node_manager/static_pages.rs index ed103145..b4d63ecf 100644 --- a/src/rust/lqosd/src/node_manager/static_pages.rs +++ b/src/rust/lqosd/src/node_manager/static_pages.rs @@ -44,6 +44,7 @@ pub(super) fn static_routes() -> Result { "api_docs.html", "chatbot.html", "lts_trial.html", + "lts_trial_setup.html", "lts_trial_success.html", "lts_trial_fail.html", "executive_worst_sites.html", diff --git a/src/rust/lqosd/src/node_manager/ws.rs b/src/rust/lqosd/src/node_manager/ws.rs index 08c5b8d9..2648fc0c 100644 --- a/src/rust/lqosd/src/node_manager/ws.rs +++ b/src/rust/lqosd/src/node_manager/ws.rs @@ -988,6 +988,34 @@ async fn receive_channel_message( return true; } } + WsRequest::LtsStartSignup => { + let result = lts::lts_trial_start_signup_data().await; + match result { + Ok(claim_id) => { + lts::spawn_signup_poll_loop(claim_id.clone()); + let response = WsResponse::LtsStartSignupResult { claim_id }; + if send_ws_response(&tx, response).await { + return true; + } + } + Err(StatusCode::FORBIDDEN) => { + let response = WsResponse::Error { + message: "Unauthorized".to_string(), + }; + if send_ws_response(&tx, response).await { + return true; + } + } + Err(_) => { + let response = WsResponse::Error { + message: "Unable to start the Insight signup session.".to_string(), + }; + if send_ws_response(&tx, response).await { + return true; + } + } + } + } WsRequest::LtsSignUp { license_key } => { let result = lts::lts_trial_signup_data(license_key).await; let (ok, message) = match result { diff --git a/src/rust/lqosd/src/node_manager/ws/messages.rs b/src/rust/lqosd/src/node_manager/ws/messages.rs index 6a53a993..613c6015 100644 --- a/src/rust/lqosd/src/node_manager/ws/messages.rs +++ b/src/rust/lqosd/src/node_manager/ws/messages.rs @@ -126,6 +126,7 @@ pub enum WsRequest { ReloadLibreQoS, LtsTrialConfig, CircuitCount, + LtsStartSignup, LtsSignUp { license_key: String, }, @@ -670,6 +671,9 @@ pub enum WsResponse { CircuitCountResult { data: CircuitCount, }, + LtsStartSignupResult { + claim_id: String, + }, LtsSignUpResult { ok: bool, message: String,