diff --git a/apps/cargo-scout-audit/src/scout/telemetry.rs b/apps/cargo-scout-audit/src/scout/telemetry.rs index 311dcafe..f7434f48 100644 --- a/apps/cargo-scout-audit/src/scout/telemetry.rs +++ b/apps/cargo-scout-audit/src/scout/telemetry.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use reqwest::blocking::Client; use serde::{Deserialize, Serialize}; -use std::{env, fs, path::PathBuf}; +use std::{env, fs, path::PathBuf, time::{SystemTime, UNIX_EPOCH}}; use strum::EnumIter; use super::blockchain::BlockChain; @@ -79,9 +79,11 @@ impl TelemetryClient { .join("user_id.txt"); // Read user ID from file - if let Ok(user_id) = fs::read_to_string(&user_id_path) { - if !user_id.trim().is_empty() { - return user_id; + if let Ok(content) = fs::read_to_string(&user_id_path) { + if let Some(last_line) = content.lines().last() { + if !last_line.trim().is_empty() { + return last_line.to_string(); + } } } @@ -96,7 +98,18 @@ impl TelemetryClient { // Request new user ID from server match Self::request_new_user_id() { Ok(user_id) => { - if let Err(e) = fs::write(&user_id_path, &user_id) { + let file_content = format!( + "# This file is used by Scout to gather anonymous usage data. You can see the\n\ + # last few reports that have been sent in the reports/ subdirectory.\n\ + #\n\ + # View your data at: https://scout-api.coinfabrik.com/report/data/{}\n\ + # Delete your data at: https://scout-api.coinfabrik.com/user/delete/{}\n\ + # To opt-out of telemetry, replace the following line with DONOTTRACK\n\ + {}", + user_id, user_id, user_id + ); + + if let Err(e) = fs::write(&user_id_path, file_content) { tracing::error!("Failed to cache user ID: {}", e); } user_id @@ -145,6 +158,17 @@ impl TelemetryClient { .send() .context("Failed to send telemetry report")?; + let reports_dir = get_home_directory() + .join(".scout-audit") + .join("telemetry") + .join("reports"); + fs::create_dir_all(&reports_dir)?; + + let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); + let report_path = reports_dir.join(format!("report_{}.json", timestamp)); + let json = serde_json::to_string_pretty(&self.report)?; + fs::write(&report_path, json)?; + Ok(()) } } diff --git a/apps/cargo-scout-audit/src/startup.rs b/apps/cargo-scout-audit/src/startup.rs index ad406e80..dcfb1003 100644 --- a/apps/cargo-scout-audit/src/startup.rs +++ b/apps/cargo-scout-audit/src/startup.rs @@ -103,11 +103,6 @@ pub fn run_scout(mut opts: Scout) -> Result { BlockChain::get_blockchain_dependency(&metadata).map_err(ScoutError::BlockchainFailed)?; let toolchain = blockchain.get_toolchain(); - // Send telemetry data - let client_type = TelemetryClient::detect_client_type(&opts.args); - let telemetry_client = TelemetryClient::new(blockchain, client_type); - let _ = telemetry_client.send_report(); - if opts.toolchain { println!("{}", toolchain); return Ok(ScoutResult::from_string(toolchain)); @@ -120,6 +115,11 @@ pub fn run_scout(mut opts: Scout) -> Result { return Ok(ScoutResult::default()); } + // Send telemetry data + let client_type = TelemetryClient::detect_client_type(&opts.args); + let telemetry_client = TelemetryClient::new(blockchain, client_type); + let _ = telemetry_client.send_report(); + if let Err(e) = VersionChecker::new().check_for_updates() { // This is not a critical error, so we don't need to bail and we don't need a ScoutError print_error(&format!(