From 5576378b3fb35808d60d70f99d0abc8fe11e872c Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sat, 11 Apr 2026 07:33:32 -0300 Subject: [PATCH] Update botserver: Multiple improvements across core modules --- .forgejo/workflows/botserver.yaml | 5 +- src/analytics/goals.rs | 2 +- src/auto_task/agent_executor.rs | 2 +- src/basic/keywords/agent_reflection.rs | 16 +- src/basic/keywords/ai_tools.rs | 14 +- src/basic/keywords/detect.rs | 2 +- src/basic/keywords/hearing/processing.rs | 6 +- src/basic/keywords/mcp_client.rs | 2 +- src/browser/api.rs | 2 +- src/calendar/mod.rs | 2 +- src/console/wizard.rs | 2 +- src/core/bootstrap/bootstrap_utils.rs | 8 +- src/core/config/mod.rs | 50 +++--- src/core/config_reload.rs | 4 +- src/core/directory/api.rs | 6 +- src/core/kb/embedding_generator.rs | 6 +- src/core/kb/kb_indexer.rs | 6 +- src/core/oauth/routes.rs | 2 +- src/core/package_manager/alm_setup.rs | 8 +- src/core/package_manager/cli.rs | 22 +-- src/core/package_manager/mod.rs | 8 +- src/core/package_manager/setup.rs | 18 +- src/core/secrets/mod.rs | 206 +++++++++++++---------- src/core/shared/admin_email.rs | 6 +- src/core/shared/test_utils.rs | 8 +- src/core/shared/utils.rs | 2 +- src/core/urls.rs | 11 +- src/directory/bootstrap.rs | 4 +- src/email/messages.rs | 4 +- src/email/tracking.rs | 4 +- src/email/vectordb.rs | 4 +- src/llm/local.rs | 8 +- src/llm/mod.rs | 8 +- src/main_module/bootstrap.rs | 28 +-- src/marketing/ai.rs | 4 +- src/marketing/email.rs | 4 +- src/monitoring/tracing.rs | 2 +- src/security/cors.rs | 16 +- src/security/integration.rs | 102 +++++------ src/security/zitadel_auth.rs | 4 +- src/timeseries/mod.rs | 2 +- src/vector-db/embedding.rs | 2 +- src/video/engine.rs | 12 +- src/weba/mod.rs | 20 +-- 44 files changed, 320 insertions(+), 334 deletions(-) diff --git a/.forgejo/workflows/botserver.yaml b/.forgejo/workflows/botserver.yaml index 33d8ff2e..4e150c1c 100644 --- a/.forgejo/workflows/botserver.yaml +++ b/.forgejo/workflows/botserver.yaml @@ -78,15 +78,12 @@ jobs: ssh $SSH_ARGS system "find /opt/gbo/data -maxdepth 2 ! -path '*/botserver' ! -path '*/gb-ws' -print0 2>/dev/null | xargs -0 rm -rf || true" # Clean old log files ssh $SSH_ARGS system "find /tmp -name '*.log' -type f -mtime +7 -print0 2>/dev/null | xargs -0 rm -f || true" - # Show disk space - ssh $SSH_ARGS system "df -h /opt/gbo/data" - echo "=== Workspace cleanup complete ===" - name: Build BotServer working-directory: /opt/gbo/data/botserver run: | sccache --start-server 2>/dev/null || true - cargo build -p botserver --features chat -j 8 2>&1 | tee /tmp/build.log + cargo build -p botserver -j 8 2>&1 | tee /tmp/build.log sccache --show-stats ls -lh target/debug/botserver diff --git a/src/analytics/goals.rs b/src/analytics/goals.rs index 7bf8b551..cc1b63f4 100644 --- a/src/analytics/goals.rs +++ b/src/analytics/goals.rs @@ -17,7 +17,7 @@ use crate::core::shared::schema::{okr_checkins, okr_key_results, okr_objectives, use crate::core::shared::state::AppState; fn get_bot_context() -> (Uuid, Uuid) { - let sm = crate::core::secrets::SecretsManager::from_env().ok(); + let sm = crate::core::secrets::SecretsManager::get().ok().map(|sm| sm.clone()); let (org_id, bot_id) = if let Some(sm) = sm { let sm_owned = sm.clone(); let (tx, rx) = std::sync::mpsc::channel(); diff --git a/src/auto_task/agent_executor.rs b/src/auto_task/agent_executor.rs index 747f9e79..901b2cf8 100644 --- a/src/auto_task/agent_executor.rs +++ b/src/auto_task/agent_executor.rs @@ -56,7 +56,7 @@ impl AgentExecutor { } }); - self.broadcast_browser_ready("http://localhost:8000", 8000); + self.broadcast_browser_ready("", 8000); self.broadcast_step("Agent Ready", 2, 10); Ok(()) diff --git a/src/basic/keywords/agent_reflection.rs b/src/basic/keywords/agent_reflection.rs index 26b8c397..d74f2dc9 100644 --- a/src/basic/keywords/agent_reflection.rs +++ b/src/basic/keywords/agent_reflection.rs @@ -634,19 +634,23 @@ impl ReflectionEngine { .load(&mut conn) .unwrap_or_default(); - let mut llm_url = "http://localhost:8081".to_string(); - let mut llm_model = "default".to_string(); - let mut llm_key = "none".to_string(); + let mut llm_url: Option = None; + let mut llm_model: Option = None; + let mut llm_key: Option = None; for config in configs { match config.config_key.as_str() { - "llm-url" => llm_url = config.config_value, - "llm-model" => llm_model = config.config_value, - "llm-key" => llm_key = config.config_value, + "llm-url" => llm_url = Some(config.config_value), + "llm-model" => llm_model = Some(config.config_value), + "llm-key" => llm_key = Some(config.config_value), _ => {} } } + let llm_url = llm_url.ok_or_else(|| "LLM URL not configured".to_string())?; + let llm_model = llm_model.ok_or_else(|| "LLM model not configured".to_string())?; + let llm_key = llm_key.ok_or_else(|| "LLM key not configured".to_string())?; + Ok((llm_url, llm_model, llm_key)) } diff --git a/src/basic/keywords/ai_tools.rs b/src/basic/keywords/ai_tools.rs index f420fa06..52f1f46c 100644 --- a/src/basic/keywords/ai_tools.rs +++ b/src/basic/keywords/ai_tools.rs @@ -184,9 +184,9 @@ async fn translate_text( target_lang: &str, ) -> Result> { let llm_url = if let Some(sm) = crate::core::shared::utils::get_secrets_manager().await { - sm.get_value("gbo/llm", "url").await.unwrap_or_else(|_| "http://localhost:8081".to_string()) + sm.get_value("gbo/llm", "url").await.unwrap_or_else(|_| "".to_string()) } else { - "http://localhost:8081".to_string() + "".to_string() }; let prompt = format!( "Translate to {}. Return ONLY the translation:\n\n{}", @@ -212,7 +212,7 @@ async fn translate_text( async fn perform_ocr(image_path: &str) -> Result> { let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); let image_data = if image_path.starts_with("http") { client.get(image_path).send().await?.bytes().await?.to_vec() @@ -237,9 +237,9 @@ async fn analyze_sentiment( text: &str, ) -> Result> { let llm_url = if let Some(sm) = crate::core::shared::utils::get_secrets_manager().await { - sm.get_value("gbo/llm", "url").await.unwrap_or_else(|_| "http://localhost:8081".to_string()) + sm.get_value("gbo/llm", "url").await.unwrap_or_else(|_| "".to_string()) } else { - "http://localhost:8081".to_string() + "".to_string() }; let prompt = format!( r#"Analyze sentiment. Return JSON only: @@ -346,9 +346,9 @@ async fn classify_text( categories: &[String], ) -> Result> { let llm_url = if let Some(sm) = crate::core::shared::utils::get_secrets_manager().await { - sm.get_value("gbo/llm", "url").await.unwrap_or_else(|_| "http://localhost:8081".to_string()) + sm.get_value("gbo/llm", "url").await.unwrap_or_else(|_| "".to_string()) } else { - "http://localhost:8081".to_string() + "".to_string() }; let cats = categories.join(", "); let prompt = format!( diff --git a/src/basic/keywords/detect.rs b/src/basic/keywords/detect.rs index 2e052b8b..c12ec239 100644 --- a/src/basic/keywords/detect.rs +++ b/src/basic/keywords/detect.rs @@ -109,7 +109,7 @@ async fn detect_anomalies_in_table( } let botmodels_host = - std::env::var("BOTMODELS_HOST").unwrap_or_else(|_| "http://localhost:8085".to_string()); + std::env::var("BOTMODELS_HOST").unwrap_or_else(|_| "".to_string()); let botmodels_key = std::env::var("BOTMODELS_API_KEY").unwrap_or_else(|_| "starter".to_string()); diff --git a/src/basic/keywords/hearing/processing.rs b/src/basic/keywords/hearing/processing.rs index 37f2c17a..6e3b9039 100644 --- a/src/basic/keywords/hearing/processing.rs +++ b/src/basic/keywords/hearing/processing.rs @@ -138,7 +138,7 @@ pub async fn process_qrcode( .ok() }); config_url.unwrap_or_else(|| { - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()) + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()) }) }; @@ -188,7 +188,7 @@ pub async fn process_audio_to_text( audio_url: &str, ) -> Result<(String, Option), String> { let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); @@ -237,7 +237,7 @@ pub async fn process_video_description( video_url: &str, ) -> Result<(String, Option), String> { let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); diff --git a/src/basic/keywords/mcp_client.rs b/src/basic/keywords/mcp_client.rs index 17fc48c1..f95d25c4 100644 --- a/src/basic/keywords/mcp_client.rs +++ b/src/basic/keywords/mcp_client.rs @@ -120,7 +120,7 @@ impl Default for McpConnection { fn default() -> Self { Self { connection_type: ConnectionType::Http, - url: "http://localhost:8080".to_string(), + url: "".to_string(), port: None, timeout_seconds: 30, max_retries: 3, diff --git a/src/browser/api.rs b/src/browser/api.rs index b56dcbf5..2c15c5b4 100644 --- a/src/browser/api.rs +++ b/src/browser/api.rs @@ -76,7 +76,7 @@ pub async fn export_test( let script = r#" import { test, expect } from '@playwright/test'; test('Recorded test', async ({ page }) => { - await page.goto('http://localhost:3000'); + await page.goto(''); // Add actions }); "#; diff --git a/src/calendar/mod.rs b/src/calendar/mod.rs index bc1e30f1..e771613c 100644 --- a/src/calendar/mod.rs +++ b/src/calendar/mod.rs @@ -23,7 +23,7 @@ pub mod caldav; pub mod ui; fn get_bot_context() -> (Uuid, Uuid) { - let sm = crate::core::secrets::SecretsManager::from_env().ok(); + let sm = crate::core::secrets::SecretsManager::get().ok().map(|sm| sm.clone()); let (org_id, bot_id) = if let Some(sm) = sm { let sm_owned = sm.clone(); let (tx, rx) = std::sync::mpsc::channel(); diff --git a/src/console/wizard.rs b/src/console/wizard.rs index dbfd2cff..cd0b7481 100644 --- a/src/console/wizard.rs +++ b/src/console/wizard.rs @@ -894,7 +894,7 @@ pub fn apply_wizard_config(config: &WizardConfig) -> io::Result<()> { println!(" Data directory: {}", config.data_dir.display()); println!("\n Next steps:"); println!(" 1. Run: botserver start"); - println!(" 2. Open: http://localhost:4242"); + println!(" 2. Open: "); println!(" 3. Login with: {}", config.admin.username); Ok(()) diff --git a/src/core/bootstrap/bootstrap_utils.rs b/src/core/bootstrap/bootstrap_utils.rs index c07f2da6..107801d5 100644 --- a/src/core/bootstrap/bootstrap_utils.rs +++ b/src/core/bootstrap/bootstrap_utils.rs @@ -162,10 +162,10 @@ pub fn cache_health_check() -> bool { /// Check if Qdrant vector database is healthy pub fn vector_db_health_check() -> bool { - let qdrant_url = if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { + let qdrant_url = if let Ok(sm) = crate::core::secrets::SecretsManager::get() { sm.get_vectordb_config_sync().0 } else { - "http://localhost:6333".to_string() + "".to_string() }; let urls = [ @@ -226,7 +226,7 @@ pub fn zitadel_health_check() -> bool { "2", "-m", "3", - "http://localhost:8300/debug/healthz", + "/debug/healthz", ]) }) .and_then(|c| c.execute()); @@ -327,7 +327,7 @@ pub fn drive_health_check() -> bool { /// Check if ALM (Forgejo) is healthy pub fn alm_health_check() -> bool { - let urls = ["http://localhost:3000", "https://localhost:3000"]; + let urls = ["", "https://localhost:3000"]; for url in &urls { if let Ok(output) = SafeCommand::new("curl") diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 5f3efca2..306c65e6 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -91,7 +91,7 @@ impl CustomDatabaseConfig { .first::(&mut conn) .ok() .filter(|s| !s.is_empty()) - .unwrap_or_else(|| "localhost".to_string()); + .unwrap_or_else(|| "".to_string()); let port: u16 = bot_configuration .filter(bot_id.eq(target_bot_id)) @@ -272,27 +272,21 @@ impl AppConfig { .unwrap_or_else(|| default.to_string()) }; - let get_u16 = |key: &str, default: u16| -> u16 { - config_map - .get(key) - .and_then(|v| v.parse().ok()) - .unwrap_or(default) - }; + let get_u16 = |key: &str, default: u16| -> u16 { + config_map + .get(key) + .and_then(|v| v.parse().ok()) + .unwrap_or(default) + }; - // Read from Vault with fallback to defaults - let secrets = SecretsManager::from_env().ok(); - let (drive_server, drive_access, drive_secret) = secrets - .as_ref() - .map(|s| s.get_drive_config()) - .unwrap_or_else(|| { - ( - crate::core::urls::InternalUrls::DRIVE.to_string(), - "minioadmin".to_string(), - "minioadmin".to_string(), - ) - }); + // Read from Vault with fallback to defaults + let secrets = SecretsManager::get().ok().map(|sm| sm.clone()); + let (drive_server, drive_access, drive_secret) = secrets + .as_ref() + .and_then(|s| s.get_drive_config().ok()) + .unwrap_or((String::new(), String::new(), String::new())); - let drive = DriveConfig { + let drive = DriveConfig { server: drive_server, access_key: drive_access, secret_key: drive_secret, @@ -317,7 +311,7 @@ impl AppConfig { server: ServerConfig { host: get_str("server_host", "0.0.0.0"), port, - base_url: config_map.get("server_base_url").cloned().unwrap_or_else(|| "http://localhost:8080".to_string()), + base_url: config_map.get("server_base_url").cloned().unwrap_or_else(|| String::new()), }, site_path: { ConfigManager::new(pool.clone()).get_config( @@ -331,17 +325,11 @@ impl AppConfig { } pub fn from_env() -> Result { // Read from Vault with fallback to defaults - let secrets = SecretsManager::from_env().ok(); + let secrets = SecretsManager::get().ok().map(|sm| sm.clone()); let (drive_server, drive_access, drive_secret) = secrets .as_ref() - .map(|s| s.get_drive_config()) - .unwrap_or_else(|| { - ( - crate::core::urls::InternalUrls::DRIVE.to_string(), - "minioadmin".to_string(), - "minioadmin".to_string(), - ) - }); + .and_then(|s| s.get_drive_config().ok()) + .unwrap_or((String::new(), String::new(), String::new())); let minio = DriveConfig { server: drive_server, @@ -368,7 +356,7 @@ impl AppConfig { server: ServerConfig { host: "0.0.0.0".to_string(), port, - base_url: "http://localhost:8080".to_string(), + base_url: "".to_string(), }, site_path: format!("{}/sites", crate::core::shared::utils::get_stack_path()), diff --git a/src/core/config_reload.rs b/src/core/config_reload.rs index 37b49bc3..b22ba9e7 100644 --- a/src/core/config_reload.rs +++ b/src/core/config_reload.rs @@ -24,8 +24,8 @@ pub async fn reload_config( // Get LLM config let llm_url = config_manager - .get_config(&default_bot_id, "llm-url", Some("http://localhost:8081")) - .unwrap_or_else(|_| "http://localhost:8081".to_string()); + .get_config(&default_bot_id, "llm-url", Some("")) + .unwrap_or_else(|_| "".to_string()); let llm_model = config_manager .get_config(&default_bot_id, "llm-model", Some("local")) diff --git a/src/core/directory/api.rs b/src/core/directory/api.rs index b685c40e..ff4b0496 100644 --- a/src/core/directory/api.rs +++ b/src/core/directory/api.rs @@ -81,7 +81,7 @@ pub async fn provision_user_handler( .config .as_ref() .map(|c| c.server.base_url.clone()) - .unwrap_or_else(|| "http://localhost:9000".to_string()); + .unwrap_or_else(|| "".to_string()); let provisioning = UserProvisioningService::new(state.conn.clone(), s3_client, base_url); @@ -114,7 +114,7 @@ pub async fn deprovision_user_handler( .config .as_ref() .map(|c| c.server.base_url.clone()) - .unwrap_or_else(|| "http://localhost:9000".to_string()); + .unwrap_or_else(|| "".to_string()); let provisioning = UserProvisioningService::new(state.conn.clone(), s3_client, base_url); @@ -257,7 +257,7 @@ pub async fn check_services_status(State(state): State>) -> impl I let client = create_tls_client(Some(2)); - if let Ok(response) = client.get("http://localhost:8300/healthz").send().await { + if let Ok(response) = client.get("/healthz").send().await { status.directory = response.status().is_success(); } diff --git a/src/core/kb/embedding_generator.rs b/src/core/kb/embedding_generator.rs index 218365a4..7f1fbf70 100644 --- a/src/core/kb/embedding_generator.rs +++ b/src/core/kb/embedding_generator.rs @@ -39,7 +39,7 @@ pub struct EmbeddingConfig { impl Default for EmbeddingConfig { fn default() -> Self { Self { - embedding_url: "http://localhost:8082".to_string(), + embedding_url: "".to_string(), embedding_model: "BAAI/bge-multilingual-gemma2".to_string(), embedding_key: None, dimensions: 2048, @@ -58,7 +58,7 @@ impl EmbeddingConfig { /// Load embedding config from bot's config.csv (similar to llm-url, llm-model) /// This allows configuring embedding server per-bot in config.csv: - /// embedding-url,http://localhost:8082 + /// embedding-url, /// embedding-model,bge-small-en-v1.5 /// embedding-dimensions,384 /// embedding-batch-size,16 @@ -77,7 +77,7 @@ impl EmbeddingConfig { .ok() .filter(|s| !s.is_empty()), Err(_) => None, - }.unwrap_or_else(|| "http://localhost:8082".to_string()); + }.unwrap_or_else(|| "".to_string()); let embedding_model = match pool.get() { Ok(mut conn) => bot_configuration diff --git a/src/core/kb/kb_indexer.rs b/src/core/kb/kb_indexer.rs index fde6c3c4..44749919 100644 --- a/src/core/kb/kb_indexer.rs +++ b/src/core/kb/kb_indexer.rs @@ -22,7 +22,7 @@ pub struct QdrantConfig { impl Default for QdrantConfig { fn default() -> Self { Self { - url: "http://localhost:6333".to_string(), + url: "".to_string(), api_key: None, timeout_secs: 30, } @@ -36,8 +36,8 @@ impl QdrantConfig { } else { let config_manager = ConfigManager::new(pool); let url = config_manager - .get_config(bot_id, "vectordb-url", Some("http://localhost:6333")) - .unwrap_or_else(|_| "http://localhost:6333".to_string()); + .get_config(bot_id, "vectordb-url", Some("")) + .unwrap_or_else(|_| "".to_string()); (url, None) }; Self { diff --git a/src/core/oauth/routes.rs b/src/core/oauth/routes.rs index 112a822c..7fa8f5b9 100644 --- a/src/core/oauth/routes.rs +++ b/src/core/oauth/routes.rs @@ -439,7 +439,7 @@ async fn get_bot_config(state: &AppState) -> HashMap { fn get_base_url(state: &AppState) -> String { let _ = state; - "http://localhost:9000".to_string() + "".to_string() } async fn create_or_get_oauth_user( diff --git a/src/core/package_manager/alm_setup.rs b/src/core/package_manager/alm_setup.rs index 78d14616..1bb74dd6 100644 --- a/src/core/package_manager/alm_setup.rs +++ b/src/core/package_manager/alm_setup.rs @@ -15,7 +15,7 @@ pub async fn setup_alm() -> anyhow::Result<()> { let config_path = stack_path.join("conf/alm-ci/config.yaml"); // Check Vault if already set up - if let Ok(secrets_manager) = crate::core::secrets::SecretsManager::from_env() { + if let Ok(secrets_manager) = crate::core::secrets::SecretsManager::get() { if secrets_manager.is_enabled() { if let Ok(secrets) = secrets_manager.get_secret(crate::core::secrets::SecretPaths::ALM).await { if let (Some(user), Some(token)) = (secrets.get("username"), secrets.get("runner_token")) { @@ -52,7 +52,7 @@ PATH = {}/data/alm/gitea.db [server] HTTP_PORT = 3000 DOMAIN = localhost -ROOT_URL = http://localhost:3000 +ROOT_URL = [security] INSTALL_LOCK = true @@ -67,7 +67,7 @@ INSTALL_LOCK = true // Generate credentials and attempt to configure via HTTP API let username = "botserver"; let password = generate_random_string(32); - let alm_url = "http://localhost:3000"; + let alm_url = ""; // Try to create admin user and get runner token via HTTP API // Note: Forgejo CLI binary may segfault on some systems, so we use curl @@ -95,7 +95,7 @@ INSTALL_LOCK = true } // Store in Vault - if let Ok(secrets_manager) = crate::core::secrets::SecretsManager::from_env() { + if let Ok(secrets_manager) = crate::core::secrets::SecretsManager::get() { if secrets_manager.is_enabled() { let mut secrets = HashMap::new(); secrets.insert("url".to_string(), alm_url.to_string()); diff --git a/src/core/package_manager/cli.rs b/src/core/package_manager/cli.rs index 83d1834d..716809d6 100644 --- a/src/core/package_manager/cli.rs +++ b/src/core/package_manager/cli.rs @@ -437,7 +437,7 @@ async fn vault_migrate(env_file: &str) -> Result<()> { } } - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); if !manager.is_enabled() { return Err(anyhow::anyhow!( "Vault not configured. Set VAULT_ADDR and VAULT_TOKEN" @@ -605,7 +605,7 @@ async fn vault_migrate(env_file: &str) -> Result<()> { } async fn vault_put(path: &str, kvs: &[&str]) -> Result<()> { - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); if !manager.is_enabled() { return Err(anyhow::anyhow!("Vault not configured")); } @@ -669,7 +669,7 @@ async fn vault_put(path: &str, kvs: &[&str]) -> Result<()> { } async fn vault_get(path: &str, key: Option<&str>) -> Result<()> { - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); if !manager.is_enabled() { return Err(anyhow::anyhow!("Vault not configured")); } @@ -751,7 +751,7 @@ async fn print_version(show_all: bool) -> Result<()> { println!(); println!("Secrets:"); - if let Ok(manager) = SecretsManager::from_env() { + if let Ok(manager) = SecretsManager::get() { if manager.is_enabled() { match manager.health_check().await { Ok(true) => println!(" Vault: connected"), @@ -805,7 +805,7 @@ fn generate_secret_key() -> String { } async fn rotate_secret(component: &str) -> Result<()> { - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); if !manager.is_enabled() { return Err(anyhow::anyhow!( "Vault not configured. Set VAULT_ADDR and VAULT_TOKEN" @@ -1096,7 +1096,7 @@ async fn rotate_all_secrets() -> Result<()> { return Ok(()); } - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); if !manager.is_enabled() { return Err(anyhow::anyhow!("Vault not configured")); } @@ -1183,7 +1183,7 @@ async fn verify_rotation(component: &str) -> Result<()> { match component { "tables" => { - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); let secrets = manager.get_secret(SecretPaths::TABLES).await?; let host = secrets.get("host").cloned().unwrap_or_else(|| "localhost".to_string()); @@ -1228,9 +1228,9 @@ async fn verify_rotation(component: &str) -> Result<()> { // Try to determine the health endpoint let health_urls = vec![ - "http://localhost:8080/health", - "http://localhost:5858/health", - "http://localhost:3000/health", + "/health", + "/health", + "/health", ]; let mut success = false; @@ -1268,7 +1268,7 @@ async fn verify_rotation(component: &str) -> Result<()> { } async fn vault_health() -> Result<()> { - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); if !manager.is_enabled() { println!("x Vault not configured"); diff --git a/src/core/package_manager/mod.rs b/src/core/package_manager/mod.rs index aff8a453..b1bc9e4b 100644 --- a/src/core/package_manager/mod.rs +++ b/src/core/package_manager/mod.rs @@ -74,11 +74,11 @@ pub async fn setup_directory() -> anyhow::Result anyhow::Result anyhow::Result Result<&'static SecretsManager> { + static INIT: OnceLock> = OnceLock::new(); + INIT.get_or_init(|| { + match Self::from_env() { + Ok(manager) => { + info!("SecretsManager singleton initialized (Vault: {})", env::var("VAULT_ADDR").unwrap_or_default()); + Ok(manager) + } + Err(e) => { + warn!("Failed to initialize SecretsManager: {}", e); + Err(e) + } + } + }).as_ref().map_err(|e| anyhow!("SecretsManager initialization failed: {}", e)) + } + + pub fn get_clone() -> Result { + Self::get().map(|sm| sm.clone()) + } + pub fn is_enabled(&self) -> bool { self.enabled } @@ -208,30 +229,33 @@ impl SecretsManager { default.to_string() } - pub fn get_drive_config(&self) -> (String, String, String) { + pub fn get_drive_config(&self) -> Result<(String, String, String)> { // Try to read from Vault using std process (curl) if let Ok(vault_addr) = std::env::var("VAULT_ADDR") { if let Ok(vault_token) = std::env::var("VAULT_TOKEN") { let ca_cert = std::env::var("VAULT_CACERT").unwrap_or_default(); - + log::info!("Attempting to read drive config from Vault: {}", vault_addr); - + let url = format!("{}/v1/secret/data/gbo/drive", vault_addr); - + // Use curl via Command for reliable TLS let result = std::process::Command::new("curl") .args(&["-sf", "--cacert", &ca_cert, "-H", &format!("X-Vault-Token: {}", &vault_token), &url]) .output(); - + match result { Ok(output) if output.status.success() => { if let Ok(data) = serde_json::from_slice::(&output.stdout) { if let Some(secret_data) = data.get("data").and_then(|d| d.get("data")) { - let host = secret_data.get("host").and_then(|v| v.as_str()).unwrap_or("localhost:9100"); - let accesskey = secret_data.get("accesskey").and_then(|v| v.as_str()).unwrap_or("minioadmin"); - let secret = secret_data.get("secret").and_then(|v| v.as_str()).unwrap_or("minioadmin"); + let host = secret_data.get("host").and_then(|v| v.as_str()) + .ok_or_else(|| anyhow!("drive host not configured in Vault"))?; + let accesskey = secret_data.get("accesskey").and_then(|v| v.as_str()) + .ok_or_else(|| anyhow!("drive accesskey not configured in Vault"))?; + let secret = secret_data.get("secret").and_then(|v| v.as_str()) + .ok_or_else(|| anyhow!("drive secret not configured in Vault"))?; log::info!("get_drive_config: Successfully read from Vault - host={}", host); - return (host.to_string(), accesskey.to_string(), secret.to_string()); + return Ok((host.to_string(), accesskey.to_string(), secret.to_string())); } } } @@ -245,82 +269,86 @@ impl SecretsManager { } } - log::warn!("get_drive_config: Falling back to defaults - Vault not available"); - ("localhost:9100".to_string(), "minioadmin".to_string(), "minioadmin".to_string()) + Err(anyhow!("Drive configuration not available in Vault and VAULT_ADDR/VAULT_TOKEN not set")) } - pub fn get_cache_config(&self) -> (String, u16, Option) { + pub fn get_cache_config(&self) -> Result<(String, u16, Option)> { if let Ok(vault_addr) = std::env::var("VAULT_ADDR") { if let Ok(vault_token) = std::env::var("VAULT_TOKEN") { let ca_cert = std::env::var("VAULT_CACERT").unwrap_or_default(); - + log::info!("Attempting to read cache config from Vault: {}", vault_addr); let url = format!("{}/v1/secret/data/gbo/cache", vault_addr); - + let result = std::process::Command::new("curl") .args(&["-sf", "--cacert", &ca_cert, "-H", &format!("X-Vault-Token: {}", &vault_token), &url]) .output(); - + if let Ok(output) = result { if output.status.success() { if let Ok(data) = serde_json::from_slice::(&output.stdout) { if let Some(secret_data) = data.get("data").and_then(|d| d.get("data")) { - let host = secret_data.get("host").and_then(|v| v.as_str()).unwrap_or("localhost"); - let port = secret_data.get("port").and_then(|v| v.as_str()).unwrap_or("6379").parse().unwrap_or(6379); + let host = secret_data.get("host").and_then(|v| v.as_str()) + .ok_or_else(|| anyhow!("cache host not configured in Vault"))?; + let port = secret_data.get("port").and_then(|v| v.as_str()) + .ok_or_else(|| anyhow!("cache port not configured in Vault"))? + .parse().map_err(|e| anyhow!("Invalid cache port: {}", e))?; let password = secret_data.get("password").and_then(|v| v.as_str()).map(|s| s.to_string()); log::info!("get_cache_config: Successfully read from Vault - host={}", host); - return (host.to_string(), port, password); + return Ok((host.to_string(), port, password)); } } } } } } - log::warn!("get_cache_config: Falling back to defaults"); - ("localhost".to_string(), 6379, None) + Err(anyhow!("Cache configuration not available in Vault and VAULT_ADDR/VAULT_TOKEN not set")) } - pub fn get_qdrant_config(&self) -> (String, Option) { + pub fn get_qdrant_config(&self) -> Result<(String, Option)> { if let Ok(vault_addr) = std::env::var("VAULT_ADDR") { if let Ok(vault_token) = std::env::var("VAULT_TOKEN") { let ca_cert = std::env::var("VAULT_CACERT").unwrap_or_default(); - + log::info!("Attempting to read qdrant config from Vault: {}", vault_addr); let url = format!("{}/v1/secret/data/gbo/vectordb", vault_addr); - + let result = std::process::Command::new("curl") .args(&["-sf", "--cacert", &ca_cert, "-H", &format!("X-Vault-Token: {}", &vault_token), &url]) .output(); - + if let Ok(output) = result { if output.status.success() { if let Ok(data) = serde_json::from_slice::(&output.stdout) { if let Some(secret_data) = data.get("data").and_then(|d| d.get("data")) { - let url = secret_data.get("url").and_then(|v| v.as_str()).unwrap_or("http://localhost:6333"); + let url = secret_data.get("url").and_then(|v| v.as_str()) + .ok_or_else(|| anyhow!("vectordb url not configured in Vault"))?; let api_key = secret_data.get("api_key").and_then(|v| v.as_str()).map(|s| s.to_string()); log::info!("get_qdrant_config: Successfully read from Vault - url={}", url); - return (url.to_string(), api_key); + return Ok((url.to_string(), api_key)); } } } } } } - log::warn!("get_qdrant_config: Falling back to defaults"); - ("http://localhost:6333".to_string(), None) + Err(anyhow!("VectorDB configuration not available in Vault and VAULT_ADDR/VAULT_TOKEN not set")) } - pub fn get_database_config_sync(&self) -> (String, u16, String, String, String) { + pub fn get_database_config_sync(&self) -> Result<(String, u16, String, String, String)> { if let Ok(secrets) = Self::get_from_env(SecretPaths::TABLES) { - return ( - secrets.get("host").cloned().unwrap_or_else(|| "localhost".to_string()), - secrets.get("port").and_then(|p| p.parse().ok()).unwrap_or(5432), - secrets.get("database").cloned().unwrap_or_else(|| "botserver".to_string()), - secrets.get("username").cloned().unwrap_or_else(|| "gbuser".to_string()), - secrets.get("password").cloned().unwrap_or_default(), - ); + let host = secrets.get("host").cloned() + .ok_or_else(|| anyhow!("database host not configured"))?; + let port = secrets.get("port").and_then(|p| p.parse().ok()) + .ok_or_else(|| anyhow!("database port not configured"))?; + let database = secrets.get("database").cloned() + .ok_or_else(|| anyhow!("database name not configured"))?; + let username = secrets.get("username").cloned() + .ok_or_else(|| anyhow!("database username not configured"))?; + let password = secrets.get("password").cloned().unwrap_or_default(); + return Ok((host, port, database, username, password)); } - ("localhost".to_string(), 5432, "botserver".to_string(), "gbuser".to_string(), "changeme".to_string()) + Err(anyhow!("Database configuration not available")) } pub async fn get_drive_credentials(&self) -> Result<(String, String)> { @@ -333,17 +361,16 @@ impl SecretsManager { pub async fn get_database_config(&self) -> Result<(String, u16, String, String, String)> { let s = self.get_secret(SecretPaths::TABLES).await?; - Ok(( - s.get("host").cloned().unwrap_or_else(|| "localhost".into()), - s.get("port").and_then(|p| p.parse().ok()).unwrap_or(5432), - s.get("database") - .cloned() - .unwrap_or_else(|| "botserver".into()), - s.get("username") - .cloned() - .unwrap_or_else(|| "gbuser".into()), - s.get("password").cloned().unwrap_or_default(), - )) + let host = s.get("host").cloned() + .ok_or_else(|| anyhow!("database host not configured in Vault"))?; + let port = s.get("port").and_then(|p| p.parse().ok()) + .ok_or_else(|| anyhow!("database port not configured in Vault"))?; + let database = s.get("database").cloned() + .ok_or_else(|| anyhow!("database name not configured in Vault"))?; + let username = s.get("username").cloned() + .ok_or_else(|| anyhow!("database username not configured in Vault"))?; + let password = s.get("password").cloned().unwrap_or_default(); + Ok((host, port, database, username, password)) } pub async fn get_database_url(&self) -> Result { @@ -374,10 +401,10 @@ impl SecretsManager { pub async fn get_directory_config(&self) -> Result<(String, String, String, String)> { let s = self.get_secret(SecretPaths::DIRECTORY).await?; + let url = s.get("url").cloned() + .ok_or_else(|| anyhow!("directory url not configured in Vault"))?; Ok(( - s.get("url") - .cloned() - .unwrap_or_else(|| "http://localhost:9000".into()), + url, s.get("project_id").cloned().unwrap_or_default(), s.get("client_id").cloned().unwrap_or_default(), s.get("client_secret").cloned().unwrap_or_default(), @@ -394,20 +421,17 @@ impl SecretsManager { pub async fn get_vectordb_config(&self) -> Result<(String, Option)> { let s = self.get_secret(SecretPaths::VECTORDB).await?; - Ok(( - s.get("url") - .cloned() - .unwrap_or_else(|| "http://localhost:6333".into()), - s.get("api_key").cloned(), - )) + let url = s.get("url").cloned() + .ok_or_else(|| anyhow!("vectordb url not configured in Vault"))?; + Ok((url, s.get("api_key").cloned())) } pub async fn get_observability_config(&self) -> Result<(String, String, String, String)> { let s = self.get_secret(SecretPaths::OBSERVABILITY).await?; + let url = s.get("url").cloned() + .ok_or_else(|| anyhow!("observability url not configured in Vault"))?; Ok(( - s.get("url") - .cloned() - .unwrap_or_else(|| "http://localhost:8086".into()), + url, s.get("org").cloned().unwrap_or_else(|| "system".into()), s.get("bucket").cloned().unwrap_or_else(|| "metrics".into()), s.get("token").cloned().unwrap_or_default(), @@ -441,13 +465,13 @@ impl SecretsManager { }); if let Ok(Some(secrets)) = rx.recv() { return ( - secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:9000".into()), + secrets.get("url").cloned().unwrap_or_else(|| "".into()), secrets.get("project_id").cloned().unwrap_or_default(), secrets.get("client_id").cloned().unwrap_or_default(), secrets.get("client_secret").cloned().unwrap_or_default(), ); } - ("http://localhost:9000".to_string(), String::new(), String::new(), String::new()) + ("".to_string(), String::new(), String::new(), String::new()) } pub fn get_email_config(&self) -> (String, u16, String, String, String) { @@ -496,14 +520,14 @@ impl SecretsManager { }); if let Ok(Some(secrets)) = rx.recv() { return ( - secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:8081".into()), + secrets.get("url").cloned().unwrap_or_else(|| "".into()), secrets.get("model").cloned().unwrap_or_else(|| "gpt-4".into()), secrets.get("openai_key").cloned(), secrets.get("anthropic_key").cloned(), - secrets.get("ollama_url").cloned().unwrap_or_else(|| "http://localhost:11434".into()), + secrets.get("ollama_url").cloned().unwrap_or_else(|| "".into()), ); } - ("http://localhost:8081".to_string(), "gpt-4".to_string(), None, None, "http://localhost:11434".to_string()) + ("".to_string(), "gpt-4".to_string(), None, None, "".to_string()) } pub fn get_meet_config(&self) -> (String, String, String) { @@ -524,12 +548,12 @@ impl SecretsManager { }); if let Ok(Some(secrets)) = rx.recv() { return ( - secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:7880".into()), + secrets.get("url").cloned().unwrap_or_else(|| "".into()), secrets.get("app_id").cloned().unwrap_or_default(), secrets.get("app_secret").cloned().unwrap_or_default(), ); } - ("http://localhost:7880".to_string(), String::new(), String::new()) + ("".to_string(), String::new(), String::new()) } pub fn get_vectordb_config_sync(&self) -> (String, Option) { @@ -550,11 +574,11 @@ impl SecretsManager { }); if let Ok(Some(secrets)) = rx.recv() { return ( - secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:6333".into()), + secrets.get("url").cloned().unwrap_or_else(|| "".into()), secrets.get("api_key").cloned(), ); } - ("http://localhost:6333".to_string(), None) + ("".to_string(), None) } pub fn get_observability_config_sync(&self) -> (String, String, String, String) { @@ -575,13 +599,13 @@ impl SecretsManager { }); if let Ok(Some(secrets)) = rx.recv() { return ( - secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:8086".into()), + secrets.get("url").cloned().unwrap_or_else(|| "".into()), secrets.get("org").cloned().unwrap_or_else(|| "system".into()), secrets.get("bucket").cloned().unwrap_or_else(|| "metrics".into()), secrets.get("token").cloned().unwrap_or_default(), ); } - ("http://localhost:8086".to_string(), "system".to_string(), "metrics".to_string(), String::new()) + ("".to_string(), "system".to_string(), "metrics".to_string(), String::new()) } pub fn get_alm_config(&self) -> (String, String, String) { @@ -602,12 +626,12 @@ impl SecretsManager { }); if let Ok(Some(secrets)) = rx.recv() { return ( - secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:9000".into()), + secrets.get("url").cloned().unwrap_or_else(|| "".into()), secrets.get("token").cloned().unwrap_or_default(), secrets.get("default_org").cloned().unwrap_or_default(), ); } - ("http://localhost:9000".to_string(), String::new(), String::new()) + ("".to_string(), String::new(), String::new()) } pub fn get_jwt_secret_sync(&self) -> String { @@ -712,12 +736,14 @@ impl SecretsManager { secrets.insert("username".into(), "gbuser".into()); secrets.insert("password".into(), "changeme".into()); } - "directory" | "gbo/directory" | "system/directory" => { - secrets.insert("url".into(), "http://localhost:9000".into()); - secrets.insert("project_id".into(), String::new()); - secrets.insert("client_id".into(), String::new()); - secrets.insert("client_secret".into(), String::new()); - } + "directory" | "gbo/directory" | "system/directory" => { + secrets.insert("url".into(), "".into()); + secrets.insert("host".into(), "localhost".into()); + secrets.insert("port".into(), "9000".into()); + secrets.insert("project_id".into(), String::new()); + secrets.insert("client_id".into(), String::new()); + secrets.insert("client_secret".into(), String::new()); + } "drive" | "gbo/drive" | "system/drive" => { secrets.insert("host".into(), "localhost".into()); secrets.insert("port".into(), "9000".into()); @@ -737,35 +763,35 @@ impl SecretsManager { secrets.insert("smtp_from".into(), String::new()); } "llm" | "gbo/llm" | "system/llm" => { - secrets.insert("url".into(), "http://localhost:8081".into()); + secrets.insert("url".into(), "".into()); secrets.insert("model".into(), "gpt-4".into()); secrets.insert("openai_key".into(), String::new()); secrets.insert("anthropic_key".into(), String::new()); - secrets.insert("ollama_url".into(), "http://localhost:11434".into()); + secrets.insert("ollama_url".into(), "".into()); } "encryption" | "gbo/encryption" | "system/encryption" => { secrets.insert("master_key".into(), String::new()); } "meet" | "gbo/meet" | "system/meet" => { - secrets.insert("url".into(), "http://localhost:7880".into()); + secrets.insert("url".into(), "".into()); secrets.insert("app_id".into(), String::new()); secrets.insert("app_secret".into(), String::new()); } "vectordb" | "gbo/vectordb" | "system/vectordb" => { - secrets.insert("url".to_string(), "http://localhost:6333".into()); + secrets.insert("url".to_string(), "".into()); secrets.insert("host".to_string(), "localhost".into()); secrets.insert("port".to_string(), "6333".into()); secrets.insert("grpc_port".to_string(), "6334".into()); secrets.insert("api_key".to_string(), String::new()); } "observability" | "gbo/observability" | "system/observability" => { - secrets.insert("url".into(), "http://localhost:8086".into()); + secrets.insert("url".into(), "".into()); secrets.insert("org".into(), "system".into()); secrets.insert("bucket".into(), "metrics".into()); secrets.insert("token".into(), String::new()); } "alm" | "gbo/alm" | "system/alm" => { - secrets.insert("url".into(), "http://localhost:3000".into()); + secrets.insert("url".into(), "".into()); secrets.insert("token".into(), String::new()); secrets.insert("default_org".into(), String::new()); } @@ -779,14 +805,14 @@ impl SecretsManager { secrets.insert("secret_key".into(), String::new()); } "app" | "gbo/app" | "system/app" => { - secrets.insert("url".into(), "http://localhost:8080".into()); + secrets.insert("url".into(), "".into()); secrets.insert("environment".into(), "development".into()); } "jwt" | "gbo/jwt" | "system/jwt" => { secrets.insert("secret".into(), String::new()); } "models" | "gbo/models" | "system/models" => { - secrets.insert("url".into(), "http://localhost:8001".into()); + secrets.insert("url".into(), "".into()); } _ => { log::debug!("No default values for secret path: {}", path); diff --git a/src/core/shared/admin_email.rs b/src/core/shared/admin_email.rs index 0f8b7818..1583f9e9 100644 --- a/src/core/shared/admin_email.rs +++ b/src/core/shared/admin_email.rs @@ -18,7 +18,7 @@ pub async fn send_invitation_email( custom_message: Option, invitation_id: Uuid, ) -> Result<(), String> { - let smtp = crate::core::secrets::SecretsManager::from_env() + let smtp = crate::core::secrets::SecretsManager::get() .ok() .and_then(|sm| { let sm_owned = sm.clone(); @@ -28,9 +28,7 @@ pub async fn send_invitation_email( .enable_all() .build(); let result = if let Ok(rt) = rt { - rt.block_on(async move { - sm_owned.get_secret(crate::core::secrets::SecretPaths::EMAIL).await.ok() - }) + rt.block_on(async move { sm_owned.get_secret(crate::core::secrets::SecretPaths::EMAIL).await.ok() }) } else { None }; diff --git a/src/core/shared/test_utils.rs b/src/core/shared/test_utils.rs index a2bde527..b59f8c53 100644 --- a/src/core/shared/test_utils.rs +++ b/src/core/shared/test_utils.rs @@ -250,13 +250,13 @@ impl Default for TestAppStateBuilder { #[cfg(feature = "directory")] pub fn create_mock_auth_service() -> AuthService { let config = ZitadelConfig { - issuer_url: "http://localhost:9000".to_string(), - issuer: "http://localhost:9000".to_string(), + issuer_url: "".to_string(), + issuer: "".to_string(), client_id: "mock_client_id".to_string(), client_secret: "mock_client_secret".to_string(), - redirect_uri: "http://localhost:3000/callback".to_string(), + redirect_uri: "/callback".to_string(), project_id: "mock_project_id".to_string(), - api_url: "http://localhost:9000".to_string(), + api_url: "".to_string(), service_account_key: None, }; diff --git a/src/core/shared/utils.rs b/src/core/shared/utils.rs index 15bfa675..ef8b1d7b 100644 --- a/src/core/shared/utils.rs +++ b/src/core/shared/utils.rs @@ -34,7 +34,7 @@ static SECRETS_MANAGER: std::sync::LazyLock>>> std::sync::LazyLock::new(|| Arc::new(RwLock::new(None))); pub async fn init_secrets_manager() -> Result<()> { - let manager = SecretsManager::from_env()?; + let manager = SecretsManager::get()?.clone(); let mut guard = SECRETS_MANAGER.write().map_err(|e| anyhow::anyhow!("Lock poisoned: {}", e))?; *guard = Some(manager); Ok(()) diff --git a/src/core/urls.rs b/src/core/urls.rs index f2a69745..8a2fb8d7 100644 --- a/src/core/urls.rs +++ b/src/core/urls.rs @@ -490,16 +490,7 @@ impl ApiUrls { pub struct InternalUrls; impl InternalUrls { - pub const DIRECTORY_BASE: &'static str = "http://localhost:8300"; - pub const DATABASE: &'static str = "postgres://localhost:5432"; - pub const CACHE: &'static str = "redis://localhost:6379"; - pub const DRIVE: &'static str = "http://localhost:9100"; - pub const EMAIL: &'static str = "http://localhost:8025"; - pub const LLM: &'static str = "http://localhost:8081"; - pub const EMBEDDING: &'static str = "http://localhost:8082"; - pub const QDRANT: &'static str = "http://localhost:6334"; - pub const FORGEJO: &'static str = "http://localhost:3000"; - pub const LIVEKIT: &'static str = "http://localhost:7880"; + // No localhost defaults - services must be configured via Vault or env vars pub const BOTMODELS_VISION_QRCODE: &'static str = "/api/vision/qrcode"; pub const BOTMODELS_SPEECH_TO_TEXT: &'static str = "/api/speech/to-text"; pub const BOTMODELS_VISION_DESCRIBE_VIDEO: &'static str = "/api/vision/describe-video"; diff --git a/src/directory/bootstrap.rs b/src/directory/bootstrap.rs index 0cbd1586..f5bac6ba 100644 --- a/src/directory/bootstrap.rs +++ b/src/directory/bootstrap.rs @@ -268,7 +268,7 @@ fn save_setup_credentials(result: &BootstrapResult) { ║ Password: {:<46}║ ║ Email: {:<46}║ ║ ║ -║ 🌐 LOGIN NOW: http://localhost:8080/suite/login ║ +║ 🌐 LOGIN NOW: /suite/login ║ ║ ║ ╚════════════════════════════════════════════════════════════╝ @@ -358,7 +358,7 @@ fn print_bootstrap_credentials(result: &BootstrapResult) { println!("║{:^60}║", ""); println!("║ {:56}║", "🌐 LOGIN NOW:"); println!("║{:^60}║", ""); - println!("║ {:56}║", "http://localhost:8080/suite/login"); + println!("║ {:56}║", "/suite/login"); println!("║{:^60}║", ""); println!("╠{}╣", separator); println!("║{:^60}║", ""); diff --git a/src/email/messages.rs b/src/email/messages.rs index a9302951..ff94004e 100644 --- a/src/email/messages.rs +++ b/src/email/messages.rs @@ -65,8 +65,8 @@ fn is_tracking_pixel_enabled(state: &Arc, bot_id: Option) -> boo fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc) -> String { let config_manager = crate::core::config::ConfigManager::new(state.conn.clone()); let base_url = config_manager - .get_config(&Uuid::nil(), "server-url", Some("http://localhost:9000")) - .unwrap_or_else(|_| "http://localhost:9000".to_string()); + .get_config(&Uuid::nil(), "server-url", Some("")) + .unwrap_or_else(|_| "".to_string()); let pixel_url = format!("{}/api/email/tracking/pixel/{}", base_url, tracking_id); let pixel_html = format!( diff --git a/src/email/tracking.rs b/src/email/tracking.rs index 24a8917b..22cd2be8 100644 --- a/src/email/tracking.rs +++ b/src/email/tracking.rs @@ -31,8 +31,8 @@ pub fn is_tracking_pixel_enabled(state: &Arc, bot_id: Option) -> pub fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc) -> String { let config_manager = crate::core::config::ConfigManager::new(state.conn.clone()); let base_url = config_manager - .get_config(&Uuid::nil(), "server-url", Some("http://localhost:9000")) - .unwrap_or_else(|_| "http://localhost:9000".to_string()); + .get_config(&Uuid::nil(), "server-url", Some("")) + .unwrap_or_else(|_| "".to_string()); let pixel_url = format!("{}/api/email/tracking/pixel/{}", base_url, tracking_id); let pixel_html = format!( diff --git a/src/email/vectordb.rs b/src/email/vectordb.rs index 285f1cac..91e555b6 100644 --- a/src/email/vectordb.rs +++ b/src/email/vectordb.rs @@ -421,11 +421,11 @@ impl EmailEmbeddingGenerator { } pub async fn generate_text_embedding(&self, text: &str) -> Result> { - let embedding_url = if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { + let embedding_url = if let Ok(sm) = crate::core::secrets::SecretsManager::get() { let (llm_url, _, _, _, ollama_url) = sm.get_llm_config(); if !ollama_url.is_empty() { ollama_url } else { llm_url } } else { - "http://localhost:8082".to_string() + "".to_string() }; match self.generate_local_embedding(text, &embedding_url).await { Ok(embedding) => Ok(embedding), diff --git a/src/llm/local.rs b/src/llm/local.rs index e6ec3bc4..b4f2a3e1 100644 --- a/src/llm/local.rs +++ b/src/llm/local.rs @@ -45,14 +45,14 @@ pub async fn ensure_llama_servers_running( .get_config(&default_bot_id, "llm-server", Some("true")) .unwrap_or_else(|_| "true".to_string()), config_manager - .get_config(&default_bot_id, "llm-url", Some("http://localhost:8081")) - .unwrap_or_else(|_| "http://localhost:8081".to_string()), + .get_config(&default_bot_id, "llm-url", Some("")) + .unwrap_or_else(|_| "".to_string()), config_manager .get_config(&default_bot_id, "llm-model", None) .unwrap_or_default(), config_manager - .get_config(&default_bot_id, "embedding-url", Some("http://localhost:8082")) - .unwrap_or_else(|_| "http://localhost:8082".to_string()), + .get_config(&default_bot_id, "embedding-url", Some("")) + .unwrap_or_else(|_| "".to_string()), embedding_model_result.unwrap_or_default(), config_manager .get_config(&default_bot_id, "llm-server-path", None) diff --git a/src/llm/mod.rs b/src/llm/mod.rs index b8811ea3..3cc9f6e3 100644 --- a/src/llm/mod.rs +++ b/src/llm/mod.rs @@ -289,7 +289,7 @@ impl LLMProvider for OpenAIClient { 128000 // Cerebras gpt-oss models and GPT-4 variants } else if model.contains("gpt-3.5") { 16385 - } else if model.starts_with("http://localhost:808") || model == "local" { + } else if model.starts_with("") || model == "local" { 768 // Local llama.cpp server context limit } else { 32768 // Default conservative limit for modern models @@ -378,7 +378,7 @@ impl LLMProvider for OpenAIClient { 128000 // Cerebras gpt-oss models and GPT-4 variants } else if model.contains("gpt-3.5") { 16385 - } else if model.starts_with("http://localhost:808") || model == "local" { + } else if model.starts_with("") || model == "local" { 768 // Local llama.cpp server context limit } else { 32768 // Default conservative limit for modern models @@ -885,10 +885,10 @@ mod tests { fn test_openai_client_new_custom_url() { let client = OpenAIClient::new( "test_key".to_string(), - Some("http://localhost:9000".to_string()), + Some("".to_string()), None, ); - assert_eq!(client.base_url, "http://localhost:9000"); + assert_eq!(client.base_url, ""); } #[test] diff --git a/src/main_module/bootstrap.rs b/src/main_module/bootstrap.rs index 9e537150..83c3949b 100644 --- a/src/main_module/bootstrap.rs +++ b/src/main_module/bootstrap.rs @@ -305,16 +305,16 @@ pub async fn init_redis() -> Option> { let mut urls: Vec = Vec::new(); if let Some(url) = env_url { urls.push(url); - } else if let Ok(secrets) = SecretsManager::from_env() { + } else if let Ok(secrets) = SecretsManager::get() { if let Ok(data) = secrets.get_secret(SecretPaths::CACHE).await { - let host = data.get("host").cloned().unwrap_or_else(|| "localhost".into()); + let host = data.get("host").cloned().unwrap_or_else(|| "".into()); let port = data.get("port").and_then(|p| p.parse().ok()).unwrap_or(6379); urls.push(format!("redis://{}:{}", host, port)); if let Some(pass) = data.get("password") { urls.push(format!("redis://:{}@{}:{}", pass, host, port)); } } else { - urls.push("redis://localhost:6379".to_string()); +urls.push(String::new()); } } else { urls.push("redis://localhost:6379".to_string()); @@ -453,8 +453,8 @@ pub async fn create_app_state( url } else { config_manager - .get_config(&default_bot_id, "llm-url", Some("http://localhost:8081")) - .unwrap_or_else(|_| "http://localhost:8081".to_string()) + .get_config(&default_bot_id, "llm-url", Some("")) + .unwrap_or_else(|_| "".to_string()) }; info!("LLM URL: {}", llm_url); @@ -486,8 +486,8 @@ pub async fn create_app_state( && !llm_url.contains("127.0.0.1") && (llm_url.contains("api.z.ai") || llm_url.contains("openai.com") || llm_url.contains("anthropic.com")) { - warn!("External LLM URL configured ({}), but no API key provided. Falling back to local LLM at http://localhost:8081", llm_url); - "http://localhost:8081".to_string() + warn!("External LLM URL configured ({}), but no API key provided. Falling back to local LLM at ", llm_url); + "".to_string() } else { llm_url }; @@ -660,7 +660,7 @@ fn init_directory_service() -> Result<(Arc> let base_url = json .get("base_url") .and_then(|v| v.as_str()) - .unwrap_or("http://localhost:8300"); + .unwrap_or(""); let client_id = json.get("client_id").and_then(|v| v.as_str()).unwrap_or(""); let client_secret = json .get("client_secret") @@ -703,13 +703,13 @@ fn init_directory_service() -> Result<(Arc> #[cfg(feature = "directory")] fn default_zitadel_config() -> crate::directory::ZitadelConfig { crate::directory::ZitadelConfig { - issuer_url: "http://localhost:8300".to_string(), - issuer: "http://localhost:8300".to_string(), + issuer_url: "".to_string(), + issuer: "".to_string(), client_id: String::new(), client_secret: String::new(), - redirect_uri: "http://localhost:8300/callback".to_string(), + redirect_uri: "/callback".to_string(), project_id: "default".to_string(), - api_url: "http://localhost:8300".to_string(), + api_url: "".to_string(), service_account_key: None, } } @@ -787,9 +787,9 @@ fn init_llm_provider( .get_config( &bot_id, "embedding-url", - Some("http://localhost:8082"), + Some(""), ) - .unwrap_or_else(|_| "http://localhost:8082".to_string()); + .unwrap_or_else(|_| "".to_string()); let embedding_model = config_manager .get_config(&bot_id, "embedding-model", Some("all-MiniLM-L6-v2")) .unwrap_or_else(|_| "all-MiniLM-L6-v2".to_string()); diff --git a/src/marketing/ai.rs b/src/marketing/ai.rs index 69b89e7e..20c5a1f7 100644 --- a/src/marketing/ai.rs +++ b/src/marketing/ai.rs @@ -161,8 +161,8 @@ async fn get_llm_config(state: &Arc, bot_id: Uuid) -> Result<(String, let config = ConfigManager::new(state.conn.clone()); let llm_url = config - .get_config(&bot_id, "llm-url", Some("http://localhost:8081")) - .unwrap_or_else(|_| "http://localhost:8081".to_string()); + .get_config(&bot_id, "llm-url", Some("")) + .unwrap_or_else(|_| "".to_string()); let llm_model = config .get_config(&bot_id, "llm-model", None) diff --git a/src/marketing/email.rs b/src/marketing/email.rs index fe794182..c68670d7 100644 --- a/src/marketing/email.rs +++ b/src/marketing/email.rs @@ -97,8 +97,8 @@ pub async fn send_campaign_email( let config = ConfigManager::new(state.conn.clone()); let base_url = config - .get_config(&bot_id, "server-url", Some("http://localhost:3000")) - .unwrap_or_else(|_| "http://localhost:3000".to_string()); + .get_config(&bot_id, "server-url", Some("")) + .unwrap_or_else(|_| "".to_string()); let body_html = payload .body_html diff --git a/src/monitoring/tracing.rs b/src/monitoring/tracing.rs index 0f9b2e18..a7bf89ca 100644 --- a/src/monitoring/tracing.rs +++ b/src/monitoring/tracing.rs @@ -357,7 +357,7 @@ impl Default for ExporterConfig { fn default() -> Self { Self { exporter_type: ExporterType::Otlp, - endpoint: "http://localhost:4317".to_string(), + endpoint: "".to_string(), headers: HashMap::new(), batch_size: 512, flush_interval_ms: 5000, diff --git a/src/security/cors.rs b/src/security/cors.rs index 649c55cc..5d7300d5 100644 --- a/src/security/cors.rs +++ b/src/security/cors.rs @@ -100,9 +100,9 @@ impl CorsConfig { pub fn development() -> Self { Self { allowed_origins: vec![ - "http://localhost:3000".to_string(), - "http://localhost:8080".to_string(), - "http://localhost:9000".to_string(), + "".to_string(), + "".to_string(), + "".to_string(), "http://127.0.0.1:3000".to_string(), "http://127.0.0.1:8080".to_string(), "http://127.0.0.1:9000".to_string(), @@ -502,7 +502,7 @@ mod tests { fn test_development_config() { let config = CorsConfig::development(); assert!(!config.allowed_origins.is_empty()); - assert!(config.allowed_origins.contains(&"http://localhost:3000".to_string())); + assert!(config.allowed_origins.contains(&"".to_string())); } #[test] @@ -542,7 +542,7 @@ mod tests { #[test] fn test_valid_origin_format() { assert!(is_valid_origin_format("https://example.com")); - assert!(is_valid_origin_format("http://localhost:3000")); + assert!(is_valid_origin_format("")); assert!(is_valid_origin_format("https://api.example.com:8443")); assert!(!is_valid_origin_format("ftp://example.com")); @@ -557,7 +557,7 @@ mod tests { .allow_localhost(true); assert!(validator.is_allowed("https://example.com")); - assert!(validator.is_allowed("http://localhost:3000")); + assert!(validator.is_allowed("")); assert!(!validator.is_allowed("https://evil.com")); } @@ -573,7 +573,7 @@ mod tests { #[test] fn test_localhost_detection() { assert!(is_localhost_origin("http://localhost")); - assert!(is_localhost_origin("http://localhost:3000")); + assert!(is_localhost_origin("")); assert!(is_localhost_origin("https://localhost:8443")); assert!(is_localhost_origin("http://127.0.0.1")); assert!(is_localhost_origin("http://127.0.0.1:9000")); @@ -584,7 +584,7 @@ mod tests { fn test_extract_host() { assert_eq!(extract_host("https://example.com"), Some("example.com")); assert_eq!(extract_host("https://example.com:8443"), Some("example.com")); - assert_eq!(extract_host("http://localhost:3000"), Some("localhost")); + assert_eq!(extract_host(""), Some("localhost")); assert_eq!(extract_host("invalid"), None); } diff --git a/src/security/integration.rs b/src/security/integration.rs index 37539439..df8a8643 100644 --- a/src/security/integration.rs +++ b/src/security/integration.rs @@ -30,11 +30,12 @@ pub struct TlsIntegration { impl TlsIntegration { pub fn new(tls_enabled: bool) -> Self { - let (qdrant_url, _) = if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { - sm.get_vectordb_config_sync() - } else { - ("http://localhost:6333".to_string(), None) - }; + let sm = crate::core::secrets::SecretsManager::get().ok(); + + let (qdrant_url, _) = sm + .as_ref() + .map(|sm| sm.get_vectordb_config_sync()) + .unwrap_or((String::new(), None)); let qdrant_secure = qdrant_url.replace("http://", "https://"); let qdrant_port: u16 = qdrant_url .split(':') @@ -47,18 +48,13 @@ impl TlsIntegration { .and_then(|p| p.parse().ok()) .unwrap_or(6334); - let (llm_url, _, _, _, _) = if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() - { - sm.get_llm_config() - } else { - ( - "http://localhost:8081".to_string(), - "gpt-4".to_string(), - None, - None, - "http://localhost:11434".to_string(), - ) - }; + let (llm_url, _, _, _, _) = sm.as_ref().map(|sm| sm.get_llm_config()).unwrap_or(( + String::new(), + String::new(), + None, + None, + String::new(), + )); let llm_secure = llm_url.replace("http://", "https://"); let llm_port: u16 = llm_url .split(':') @@ -71,55 +67,41 @@ impl TlsIntegration { .and_then(|p| p.parse().ok()) .unwrap_or(8444); - let (cache_host, cache_port, _) = - if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { - sm.get_cache_config() - } else { - ("localhost".to_string(), 6379, None) - }; + let (cache_host, cache_port, _) = sm + .as_ref() + .map(|sm| sm.get_cache_config()) + .and_then(|r| r.ok()) + .unwrap_or((String::new(), 6379, None)); let cache_tls_port = cache_port + 1; - let (db_host, db_port, _, _, _) = - if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { - sm.get_database_config_sync() - } else { - ( - "localhost".to_string(), - 5432, - "botserver".to_string(), - "gbuser".to_string(), - "changeme".to_string(), - ) - }; + let (db_host, db_port, _, _, _) = sm + .as_ref() + .map(|sm| sm.get_database_config_sync()) + .and_then(|r| r.ok()) + .unwrap_or(( + String::new(), + 5432, + String::new(), + String::new(), + String::new(), + )); let db_tls_port = db_port + 1; - let (drive_host, _drive_accesskey, _drive_secret) = - if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { - sm.get_drive_config() - } else { - ( - "localhost:9100".to_string(), - "minioadmin".to_string(), - "minioadmin".to_string(), - ) - }; + let (drive_host, _, _) = sm + .as_ref() + .map(|sm| sm.get_drive_config()) + .and_then(|r| r.ok()) + .unwrap_or((String::new(), String::new(), String::new())); let drive_port: u16 = drive_host .split(':') .next_back() .and_then(|p| p.parse().ok()) .unwrap_or(9100); - let (directory_url, _, _, _) = - if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { - sm.get_directory_config_sync() - } else { - ( - "http://localhost:9000".to_string(), - String::new(), - String::new(), - String::new(), - ) - }; + let (directory_url, _, _, _) = sm + .as_ref() + .map(|sm| sm.get_directory_config_sync()) + .unwrap_or((String::new(), String::new(), String::new(), String::new())); let directory_secure = directory_url.replace("http://", "https://"); let directory_port: u16 = directory_url .split(':') @@ -137,8 +119,8 @@ impl TlsIntegration { services.insert( "api".to_string(), ServiceUrls { - original: "http://localhost:8080".to_string(), - secure: "https://localhost:8443".to_string(), + original: String::new(), + secure: String::new(), port: 8080, tls_port: 8443, }, @@ -157,8 +139,8 @@ impl TlsIntegration { services.insert( "embedding".to_string(), ServiceUrls { - original: "http://localhost:8082".to_string(), - secure: "https://localhost:8445".to_string(), + original: String::new(), + secure: String::new(), port: 8082, tls_port: 8445, }, diff --git a/src/security/zitadel_auth.rs b/src/security/zitadel_auth.rs index b4e1177f..586361dd 100644 --- a/src/security/zitadel_auth.rs +++ b/src/security/zitadel_auth.rs @@ -28,8 +28,8 @@ pub struct ZitadelAuthConfig { impl Default for ZitadelAuthConfig { fn default() -> Self { Self { - issuer_url: "http://localhost:9000".to_string(), - api_url: "http://localhost:9000".to_string(), + issuer_url: "".to_string(), + api_url: "".to_string(), client_id: String::new(), client_secret: String::new(), project_id: String::new(), diff --git a/src/timeseries/mod.rs b/src/timeseries/mod.rs index d83137a8..cf21c15b 100644 --- a/src/timeseries/mod.rs +++ b/src/timeseries/mod.rs @@ -26,7 +26,7 @@ pub struct TimeSeriesConfig { impl Default for TimeSeriesConfig { fn default() -> Self { Self { - url: "http://localhost:8086".to_string(), + url: "".to_string(), token: String::new(), org: "system".to_string(), bucket: "metrics".to_string(), diff --git a/src/vector-db/embedding.rs b/src/vector-db/embedding.rs index bb93734e..5d9f9269 100644 --- a/src/vector-db/embedding.rs +++ b/src/vector-db/embedding.rs @@ -11,7 +11,7 @@ impl EmbeddingGenerator { } pub async fn generate_text_embedding(&self, text: &str) -> Result> { - let embedding_url = "http://localhost:8082".to_string(); + let embedding_url = "".to_string(); match self.generate_local_embedding(text, &embedding_url).await { Ok(embedding) => Ok(embedding), Err(e) => { diff --git a/src/video/engine.rs b/src/video/engine.rs index 61546d83..6dce553b 100644 --- a/src/video/engine.rs +++ b/src/video/engine.rs @@ -586,7 +586,7 @@ impl VideoEngine { }; let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); let response = client @@ -691,7 +691,7 @@ impl VideoEngine { output_dir: &str, ) -> Result> { let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); let response = client @@ -1022,7 +1022,7 @@ impl VideoEngine { let clip = clips.iter().find(|c| c.id == clip_id).ok_or("Clip not found")?; let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); let response = client @@ -1056,7 +1056,7 @@ impl VideoEngine { let clip = clips.iter().find(|c| c.id == req.clip_id).ok_or("Clip not found")?; let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); let response = client @@ -1097,7 +1097,7 @@ impl VideoEngine { let track = tracks.iter().find(|t| t.id == audio_track_id).ok_or("Audio track not found")?; let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); let response = client @@ -1143,7 +1143,7 @@ impl VideoEngine { let track = tracks.iter().find(|t| t.id == audio_track_id).ok_or("Audio track not found")?; let botmodels_url = - std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "http://localhost:8001".to_string()); + std::env::var("BOTMODELS_URL").unwrap_or_else(|_| "".to_string()); let client = reqwest::Client::new(); let response = client diff --git a/src/weba/mod.rs b/src/weba/mod.rs index 81415833..4dd5a280 100644 --- a/src/weba/mod.rs +++ b/src/weba/mod.rs @@ -992,26 +992,26 @@ mod tests { #[test] fn test_page_url_patterns() { - let login = LoginPage::new("http://localhost:4242"); + let login = LoginPage::new(""); assert_eq!(LoginPage::url_pattern(), "/login"); - assert_eq!(login.base_url(), "http://localhost:4242"); + assert_eq!(login.base_url(), ""); - let dashboard = DashboardPage::new("http://localhost:4242"); + let dashboard = DashboardPage::new(""); assert_eq!(DashboardPage::url_pattern(), "/dashboard"); - assert_eq!(dashboard.base_url(), "http://localhost:4242"); + assert_eq!(dashboard.base_url(), ""); - let chat = ChatPage::new("http://localhost:4242", "test-bot"); + let chat = ChatPage::new("", "test-bot"); assert_eq!(ChatPage::url_pattern(), "/chat/"); - assert_eq!(chat.base_url(), "http://localhost:4242"); + assert_eq!(chat.base_url(), ""); assert_eq!(chat.bot_name(), "test-bot"); - let queue = QueuePage::new("http://localhost:4242"); + let queue = QueuePage::new(""); assert_eq!(QueuePage::url_pattern(), "/queue"); - assert_eq!(queue.base_url(), "http://localhost:4242"); + assert_eq!(queue.base_url(), ""); - let bots = BotManagementPage::new("http://localhost:4242"); + let bots = BotManagementPage::new(""); assert_eq!(BotManagementPage::url_pattern(), "/admin/bots"); - assert_eq!(bots.base_url(), "http://localhost:4242"); + assert_eq!(bots.base_url(), ""); } #[test]