feat: use firebase instead of mixpanel in for event tracking (#32)
Co-authored-by: Diego Prats <diegoprats@Diegos-MacBook-Pro-m3.local> Co-authored-by: Collin Jackson <collin@nexus.xyz>
This commit is contained in:
parent
d9507aa2ac
commit
e084396969
@ -1,4 +1,4 @@
|
|||||||
use crate::config::analytics_token;
|
use crate::config::{analytics_id, analytics_api_key};
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
use chrono::Timelike;
|
use chrono::Timelike;
|
||||||
use reqwest::header::{ACCEPT, CONTENT_TYPE};
|
use reqwest::header::{ACCEPT, CONTENT_TYPE};
|
||||||
@ -16,14 +16,27 @@ pub fn track(
|
|||||||
) {
|
) {
|
||||||
println!("{}", description);
|
println!("{}", description);
|
||||||
|
|
||||||
let token = analytics_token(ws_addr_string);
|
let firebase_app_id = analytics_id(ws_addr_string);
|
||||||
if token.is_empty() {
|
let firebase_api_key = analytics_api_key(ws_addr_string);
|
||||||
|
if firebase_app_id.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let local_now = chrono::offset::Local::now();
|
let local_now = chrono::offset::Local::now();
|
||||||
|
|
||||||
|
// For tracking events, we use the Firebase Measurement Protocol
|
||||||
|
// Firebase is mostly designed for mobile and web apps, but for our use case of a CLI,
|
||||||
|
// we can use the Measurement Protocol to track events by POST to a URL.
|
||||||
|
// The only thing that may be unexpected is that the URL we use includes a firebase key
|
||||||
|
|
||||||
|
// Firebase format for properties for Measurement protocol:
|
||||||
|
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=firebase#payload
|
||||||
|
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=firebase#payload_query_parameters
|
||||||
let mut properties = json!({
|
let mut properties = json!({
|
||||||
"token": token,
|
|
||||||
"time": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(),
|
"time": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(),
|
||||||
|
// app_instance_id is the standard key Firebase uses this key to track the same user across sessions
|
||||||
|
// It is a bit redundant, but I wanted to keep the recommended format Firebase uses to minimize surprises
|
||||||
|
// I still left the distinct_id key as well for backwards compatibility
|
||||||
|
"app_instance_id": event_properties["prover_id"],
|
||||||
"distinct_id": event_properties["prover_id"],
|
"distinct_id": event_properties["prover_id"],
|
||||||
"prover_type": "volunteer",
|
"prover_type": "volunteer",
|
||||||
"client_type": "cli",
|
"client_type": "cli",
|
||||||
@ -36,14 +49,26 @@ pub fn track(
|
|||||||
for (k, v) in event_properties.as_object().unwrap() {
|
for (k, v) in event_properties.as_object().unwrap() {
|
||||||
properties[k] = v.clone();
|
properties[k] = v.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firebase format for events
|
||||||
let body = json!({
|
let body = json!({
|
||||||
"event": event_name,
|
"app_instance_id": event_properties["prover_id"],
|
||||||
"properties": properties
|
"events": [{
|
||||||
|
"name": event_name,
|
||||||
|
"params": properties
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
let _ = client
|
let _ = client
|
||||||
.post("https://api.mixpanel.com/track?ip=1")
|
// URL is the Google Analytics endpoint for Firebase: https://stackoverflow.com/questions/50355752/firebase-analytics-from-remote-rest-api
|
||||||
|
.post(format!(
|
||||||
|
"https://www.google-analytics.com/mp/collect?firebase_app_id={}&api_secret={}",
|
||||||
|
firebase_app_id,
|
||||||
|
firebase_api_key
|
||||||
|
))
|
||||||
.body(format!("[{}]", body))
|
.body(format!("[{}]", body))
|
||||||
.header(ACCEPT, "text/plain")
|
.header(ACCEPT, "text/plain")
|
||||||
.header(CONTENT_TYPE, "application/json")
|
.header(CONTENT_TYPE, "application/json")
|
||||||
|
@ -1,18 +1,78 @@
|
|||||||
|
// Debug version of analytics_id
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub fn analytics_token(_ws_addr_string: &str) -> String {
|
pub fn analytics_id(_ws_addr_string: &str) -> String {
|
||||||
// Use one of the tokens in the release version if debugging analytics
|
// Use one of the tokens in the release version if debugging analytics
|
||||||
"".into()
|
"".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
// Debug version of analytics_api_key
|
||||||
pub fn analytics_token(ws_addr_string: &str) -> String {
|
#[cfg(debug_assertions)]
|
||||||
if ws_addr_string.starts_with("wss://dev.orchestrator.nexus.xyz:443/") {
|
pub fn analytics_api_key(_ws_addr_string: &str) -> String {
|
||||||
return "".into(); // TODO: Firebase Analytics tid
|
// Use one of the tokens in the release version if debugging analytics
|
||||||
} else if ws_addr_string.starts_with("wss://staging.orchestrator.nexus.xyz:443/") {
|
"".into()
|
||||||
return "".into(); // TODO: Firebase Analytics tid
|
|
||||||
} else if ws_addr_string.starts_with("wss://beta.orchestrator.nexus.xyz:443/") {
|
|
||||||
return "".into(); // TODO: Firebase Analytics tid
|
|
||||||
} else {
|
|
||||||
return "".into();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The following enum is used to determine the environment from the web socket string
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
enum Environment {
|
||||||
|
Dev,
|
||||||
|
Staging,
|
||||||
|
Beta,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The web socket addresses for the different environments
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
mod web_socket_urls {
|
||||||
|
pub const DEV: &str = "wss://dev.orchestrator.nexus.xyz:443/";
|
||||||
|
pub const STAGING: &str = "wss://staging.orchestrator.nexus.xyz:443/";
|
||||||
|
pub const BETA: &str = "wss://beta.orchestrator.nexus.xyz:443/";
|
||||||
|
}
|
||||||
|
|
||||||
|
// the firebase APP IDS by environment
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
mod firebase {
|
||||||
|
pub const DEV_APP_ID: &str = "1:954530464230:web:f0a14de14ef7bcdaa99627";
|
||||||
|
pub const STAGING_APP_ID: &str = "1:222794630996:web:1758d64a85eba687eaaac1";
|
||||||
|
pub const BETA_APP_ID: &str = "1:279395003658:web:04ee2c524474d683d75ef3";
|
||||||
|
|
||||||
|
// Analytics keys for the different environments
|
||||||
|
// These are keys that allow the measurement protocol to write to the analytics database
|
||||||
|
// They are not sensitive. Worst case, if a malicious actor obtains the secret, they could potentially send false or misleading data to your GA4 property
|
||||||
|
pub const DEV_API_SECRET: &str = "8ySxiKrtT8a76zClqqO8IQ";
|
||||||
|
pub const STAGING_API_SECRET: &str = "OI7H53soRMSDWfJf1ittHQ";
|
||||||
|
pub const BETA_API_SECRET: &str = "gxxzKAQLSl-uYI0eKbIi_Q";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release versions (existing code)
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
pub fn analytics_id(ws_addr_string: &str) -> String {
|
||||||
|
|
||||||
|
// Determine the environment from the web socket string (ws_addr_string)
|
||||||
|
let env = match ws_addr_string {
|
||||||
|
web_socket_urls::DEV => Environment::Dev,
|
||||||
|
web_socket_urls::STAGING => Environment::Staging,
|
||||||
|
web_socket_urls::BETA => Environment::Beta,
|
||||||
|
_ => Environment::Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return the appropriate Firebase App ID based on the environment
|
||||||
|
match env {
|
||||||
|
Environment::Dev => firebase::DEV_APP_ID.to_string(),
|
||||||
|
Environment::Staging => firebase::STAGING_APP_ID.to_string(),
|
||||||
|
Environment::Beta => firebase::BETA_APP_ID.to_string(),
|
||||||
|
Environment::Unknown => String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
pub fn analytics_api_key(ws_addr_string: &str) -> String {
|
||||||
|
match ws_addr_string {
|
||||||
|
web_socket_urls::DEV => firebase::DEV_API_SECRET.to_string(),
|
||||||
|
web_socket_urls::STAGING => firebase::STAGING_API_SECRET.to_string(),
|
||||||
|
web_socket_urls::BETA => firebase::BETA_API_SECRET.to_string(),
|
||||||
|
_ => String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user