diff --git a/.env.example b/.env.example index dda5314d8..2c56497e7 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,8 @@ REDIS_URL="redis://buster-redis:6379" LOG_LEVEL="debug" SUPABASE_URL="http://kong:8000" SUPABASE_SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ey AgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" +POSTHOG_TELEMETRY_KEY="phc_zZraCicSTfeXX5b9wWQv2rWG8QB4Z3xlotOT7gFtoNi" +TELEMETRY_ENABLED="true" # AI VARS RERANK_API_KEY="your_rerank_api_key" diff --git a/api/Cargo.toml b/api/Cargo.toml index e32ec706b..07532fa64 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -34,6 +34,7 @@ diesel = { version = "2", features = [ "serde_json", "postgres", ] } +posthog-rs = "0.3.5" diesel-async = { version = "0.5.2", features = ["postgres", "bb8"] } futures = "0.3.30" async-trait = "0.1.85" diff --git a/api/libs/handlers/Cargo.toml b/api/libs/handlers/Cargo.toml index 4aed53257..84863d297 100644 --- a/api/libs/handlers/Cargo.toml +++ b/api/libs/handlers/Cargo.toml @@ -19,6 +19,7 @@ futures = { workspace = true } regex = { workspace = true } indexmap = { workspace = true } async-trait = { workspace = true } +posthog-rs = { workspace = true } # Local dependencies diff --git a/api/libs/handlers/src/organizations/post_organization_handler.rs b/api/libs/handlers/src/organizations/post_organization_handler.rs index 4d92775ca..6e86ffdd8 100644 --- a/api/libs/handlers/src/organizations/post_organization_handler.rs +++ b/api/libs/handlers/src/organizations/post_organization_handler.rs @@ -1,3 +1,5 @@ +use std::env; + use anyhow::{Context, Result}; use chrono::Utc; use database::{ @@ -9,13 +11,11 @@ use database::{ use diesel::insert_into; use diesel_async::RunQueryDsl; use middleware::AuthenticatedUser; +use posthog_rs::{client, ClientOptions, ClientOptionsBuilder, Event}; use uuid::Uuid; /// Creates a new organization and adds the creating user as a WorkspaceAdmin. -pub async fn post_organization_handler( - name: String, - user: AuthenticatedUser, -) -> Result<()> { +pub async fn post_organization_handler(name: String, user: AuthenticatedUser) -> Result<()> { let pool = get_pg_pool(); let mut conn = pool .get() @@ -75,5 +75,48 @@ pub async fn post_organization_handler( user.id ); + if env::var("TELEMETRY_ENABLED").unwrap_or_default() == "true" { + let user_domain = user.email.split('@').last().unwrap_or_default().to_string(); + let user_company_name = new_organization.name.clone(); + tokio::spawn(async move { + let posthog_telemetry_key = env::var("POSTHOG_TELEMETRY_KEY").unwrap_or_default(); + let client_options = match ClientOptionsBuilder::default() + .api_key(posthog_telemetry_key) + .api_endpoint("https://us.i.posthog.com/capture".to_string()) + .build() + { + Ok(client_options) => client_options, + Err(e) => { + tracing::error!("Failed to create client options: {}", e); + return; + } + }; + + let client = client(client_options).await; + + let mut event = Event::new("New Company Signup", &user.id.to_string()); + + let _ = event + .insert_prop("User Domain", &user_domain) + .map_err(|e| { + tracing::error!("Failed to insert user domain: {}", e); + e + }); + let _ = event + .insert_prop("User Company Name", &user_company_name) + .map_err(|e| { + tracing::error!("Failed to insert user company name: {}", e); + e + }); + + match client.capture(event).await { + Ok(_) => {} + Err(e) => { + tracing::error!("Failed to capture event: {}", e); + } + } + }); + } + Ok(()) -} \ No newline at end of file +} diff --git a/cli/cli/src/commands/run.rs b/cli/cli/src/commands/run.rs index 839da5ee6..284148c93 100644 --- a/cli/cli/src/commands/run.rs +++ b/cli/cli/src/commands/run.rs @@ -152,9 +152,40 @@ async fn setup_persistent_app_environment() -> Result { async fn run_docker_compose_command( args: &[&str], operation_name: &str, + no_track: bool, ) -> Result<(), BusterError> { let persistent_app_dir = setup_persistent_app_environment().await?; + // --- BEGIN Telemetry Update --- + if operation_name == "Starting" && no_track { + let main_dot_env_target_path = persistent_app_dir.join(".env"); + if main_dot_env_target_path.exists() { + let mut content = fs::read_to_string(&main_dot_env_target_path).map_err(|e| { + BusterError::CommandError(format!( + "Failed to read root .env file for telemetry update: {}", + e + )) + })?; + + let original_content = content.clone(); + content = content.replace("TELEMETRY_ENABLED=\"true\"", "TELEMETRY_ENABLED=\"false\""); + content = content.replace("TELEMETRY_ENABLED=true", "TELEMETRY_ENABLED=false"); // Also handle without quotes + + if content != original_content { + fs::write(&main_dot_env_target_path, content).map_err(|e| { + BusterError::CommandError(format!( + "Failed to write updated .env file for telemetry update: {}", + e + )) + })?; + println!("Telemetry disabled in {}", main_dot_env_target_path.display()); + } + } else { + println!("Warning: Root .env file not found at {}. Could not disable telemetry.", main_dot_env_target_path.display()); + } + } + // --- END Telemetry Update --- + // Handle LiteLLM config if a start or reset operation is being performed if operation_name == "Starting" || operation_name == "Resetting" { // Check if litellm_config path is in environment @@ -306,12 +337,13 @@ async fn run_docker_compose_command( } } -pub async fn start() -> Result<(), BusterError> { - run_docker_compose_command(&["up", "-d"], "Starting").await +pub async fn start(no_track: bool) -> Result<(), BusterError> { + run_docker_compose_command(&["up", "-d"], "Starting", no_track).await } pub async fn stop() -> Result<(), BusterError> { - run_docker_compose_command(&["down"], "Stopping").await + // Pass false for no_track as it's irrelevant for 'stop' + run_docker_compose_command(&["down"], "Stopping", false).await } pub async fn reset() -> Result<(), BusterError> { diff --git a/cli/cli/src/main.rs b/cli/cli/src/main.rs index 9649d4e2b..483c09e8a 100644 --- a/cli/cli/src/main.rs +++ b/cli/cli/src/main.rs @@ -91,7 +91,11 @@ pub enum Commands { /// Interactively manage LLM and Reranker configurations Config, /// Start the Buster services - Start, + Start { + /// Disable telemetry tracking + #[arg(long, default_value_t = false)] + no_track: bool, + }, /// Stop the Buster services Stop, /// Restart the Buster services @@ -157,7 +161,7 @@ async fn main() { } => commands::generate::generate_semantic_models_command(path, target_semantic_file).await, Commands::Parse { path } => commands::parse::parse_models_command(path).await, Commands::Config => commands::config::manage_settings_interactive().await.map_err(anyhow::Error::from), - Commands::Start => run::start().await.map_err(anyhow::Error::from), + Commands::Start { no_track } => run::start(no_track).await.map_err(anyhow::Error::from), Commands::Stop => run::stop().await.map_err(anyhow::Error::from), Commands::Reset => run::reset().await.map_err(anyhow::Error::from), }; diff --git a/docker-compose.yml b/docker-compose.yml index e1100b6cb..9b479acbe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,6 +41,8 @@ services: - LLM_API_KEY=${LLM_API_KEY} - LLM_BASE_URL=${LLM_BASE_URL} - RUST_LOG=debug + - POSTHOG_TELEMETRY_KEY=${POSTHOG_TELEMETRY_KEY} + - TELEMETRY_ENABLED=${TELEMETRY_ENABLED} ports: - "3001:3001" - "3000:3000"