Update botserver: Multiple improvements across core modules
All checks were successful
BotServer CI/CD / build (push) Successful in 10m41s

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-04-11 07:33:32 -03:00
parent 4cc1e3aa4b
commit 5576378b3f
44 changed files with 320 additions and 334 deletions

View file

@ -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" 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 # 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" 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 - name: Build BotServer
working-directory: /opt/gbo/data/botserver working-directory: /opt/gbo/data/botserver
run: | run: |
sccache --start-server 2>/dev/null || true 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 sccache --show-stats
ls -lh target/debug/botserver ls -lh target/debug/botserver

View file

@ -17,7 +17,7 @@ use crate::core::shared::schema::{okr_checkins, okr_key_results, okr_objectives,
use crate::core::shared::state::AppState; use crate::core::shared::state::AppState;
fn get_bot_context() -> (Uuid, Uuid) { 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 (org_id, bot_id) = if let Some(sm) = sm {
let sm_owned = sm.clone(); let sm_owned = sm.clone();
let (tx, rx) = std::sync::mpsc::channel(); let (tx, rx) = std::sync::mpsc::channel();

View file

@ -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); self.broadcast_step("Agent Ready", 2, 10);
Ok(()) Ok(())

View file

@ -634,19 +634,23 @@ impl ReflectionEngine {
.load(&mut conn) .load(&mut conn)
.unwrap_or_default(); .unwrap_or_default();
let mut llm_url = "http://localhost:8081".to_string(); let mut llm_url: Option<String> = None;
let mut llm_model = "default".to_string(); let mut llm_model: Option<String> = None;
let mut llm_key = "none".to_string(); let mut llm_key: Option<String> = None;
for config in configs { for config in configs {
match config.config_key.as_str() { match config.config_key.as_str() {
"llm-url" => llm_url = config.config_value, "llm-url" => llm_url = Some(config.config_value),
"llm-model" => llm_model = config.config_value, "llm-model" => llm_model = Some(config.config_value),
"llm-key" => llm_key = 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)) Ok((llm_url, llm_model, llm_key))
} }

View file

@ -184,9 +184,9 @@ async fn translate_text(
target_lang: &str, target_lang: &str,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let llm_url = if let Some(sm) = crate::core::shared::utils::get_secrets_manager().await { 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 { } else {
"http://localhost:8081".to_string() "".to_string()
}; };
let prompt = format!( let prompt = format!(
"Translate to {}. Return ONLY the translation:\n\n{}", "Translate to {}. Return ONLY the translation:\n\n{}",
@ -212,7 +212,7 @@ async fn translate_text(
async fn perform_ocr(image_path: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync>> { async fn perform_ocr(image_path: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let botmodels_url = 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 client = reqwest::Client::new();
let image_data = if image_path.starts_with("http") { let image_data = if image_path.starts_with("http") {
client.get(image_path).send().await?.bytes().await?.to_vec() client.get(image_path).send().await?.bytes().await?.to_vec()
@ -237,9 +237,9 @@ async fn analyze_sentiment(
text: &str, text: &str,
) -> Result<Dynamic, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<Dynamic, Box<dyn std::error::Error + Send + Sync>> {
let llm_url = if let Some(sm) = crate::core::shared::utils::get_secrets_manager().await { 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 { } else {
"http://localhost:8081".to_string() "".to_string()
}; };
let prompt = format!( let prompt = format!(
r#"Analyze sentiment. Return JSON only: r#"Analyze sentiment. Return JSON only:
@ -346,9 +346,9 @@ async fn classify_text(
categories: &[String], categories: &[String],
) -> Result<Dynamic, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<Dynamic, Box<dyn std::error::Error + Send + Sync>> {
let llm_url = if let Some(sm) = crate::core::shared::utils::get_secrets_manager().await { 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 { } else {
"http://localhost:8081".to_string() "".to_string()
}; };
let cats = categories.join(", "); let cats = categories.join(", ");
let prompt = format!( let prompt = format!(

View file

@ -109,7 +109,7 @@ async fn detect_anomalies_in_table(
} }
let botmodels_host = 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 = let botmodels_key =
std::env::var("BOTMODELS_API_KEY").unwrap_or_else(|_| "starter".to_string()); std::env::var("BOTMODELS_API_KEY").unwrap_or_else(|_| "starter".to_string());

View file

@ -138,7 +138,7 @@ pub async fn process_qrcode(
.ok() .ok()
}); });
config_url.unwrap_or_else(|| { 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, audio_url: &str,
) -> Result<(String, Option<serde_json::Value>), String> { ) -> Result<(String, Option<serde_json::Value>), String> {
let botmodels_url = 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 client = reqwest::Client::new();
@ -237,7 +237,7 @@ pub async fn process_video_description(
video_url: &str, video_url: &str,
) -> Result<(String, Option<serde_json::Value>), String> { ) -> Result<(String, Option<serde_json::Value>), String> {
let botmodels_url = 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 client = reqwest::Client::new();

View file

@ -120,7 +120,7 @@ impl Default for McpConnection {
fn default() -> Self { fn default() -> Self {
Self { Self {
connection_type: ConnectionType::Http, connection_type: ConnectionType::Http,
url: "http://localhost:8080".to_string(), url: "".to_string(),
port: None, port: None,
timeout_seconds: 30, timeout_seconds: 30,
max_retries: 3, max_retries: 3,

View file

@ -76,7 +76,7 @@ pub async fn export_test(
let script = r#" let script = r#"
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
test('Recorded test', async ({ page }) => { test('Recorded test', async ({ page }) => {
await page.goto('http://localhost:3000'); await page.goto('');
// Add actions // Add actions
}); });
"#; "#;

View file

@ -23,7 +23,7 @@ pub mod caldav;
pub mod ui; pub mod ui;
fn get_bot_context() -> (Uuid, Uuid) { 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 (org_id, bot_id) = if let Some(sm) = sm {
let sm_owned = sm.clone(); let sm_owned = sm.clone();
let (tx, rx) = std::sync::mpsc::channel(); let (tx, rx) = std::sync::mpsc::channel();

View file

@ -894,7 +894,7 @@ pub fn apply_wizard_config(config: &WizardConfig) -> io::Result<()> {
println!(" Data directory: {}", config.data_dir.display()); println!(" Data directory: {}", config.data_dir.display());
println!("\n Next steps:"); println!("\n Next steps:");
println!(" 1. Run: botserver start"); println!(" 1. Run: botserver start");
println!(" 2. Open: http://localhost:4242"); println!(" 2. Open: ");
println!(" 3. Login with: {}", config.admin.username); println!(" 3. Login with: {}", config.admin.username);
Ok(()) Ok(())

View file

@ -162,10 +162,10 @@ pub fn cache_health_check() -> bool {
/// Check if Qdrant vector database is healthy /// Check if Qdrant vector database is healthy
pub fn vector_db_health_check() -> bool { 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 sm.get_vectordb_config_sync().0
} else { } else {
"http://localhost:6333".to_string() "".to_string()
}; };
let urls = [ let urls = [
@ -226,7 +226,7 @@ pub fn zitadel_health_check() -> bool {
"2", "2",
"-m", "-m",
"3", "3",
"http://localhost:8300/debug/healthz", "/debug/healthz",
]) ])
}) })
.and_then(|c| c.execute()); .and_then(|c| c.execute());
@ -327,7 +327,7 @@ pub fn drive_health_check() -> bool {
/// Check if ALM (Forgejo) is healthy /// Check if ALM (Forgejo) is healthy
pub fn alm_health_check() -> bool { pub fn alm_health_check() -> bool {
let urls = ["http://localhost:3000", "https://localhost:3000"]; let urls = ["", "https://localhost:3000"];
for url in &urls { for url in &urls {
if let Ok(output) = SafeCommand::new("curl") if let Ok(output) = SafeCommand::new("curl")

View file

@ -91,7 +91,7 @@ impl CustomDatabaseConfig {
.first::<String>(&mut conn) .first::<String>(&mut conn)
.ok() .ok()
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
.unwrap_or_else(|| "localhost".to_string()); .unwrap_or_else(|| "".to_string());
let port: u16 = bot_configuration let port: u16 = bot_configuration
.filter(bot_id.eq(target_bot_id)) .filter(bot_id.eq(target_bot_id))
@ -272,27 +272,21 @@ impl AppConfig {
.unwrap_or_else(|| default.to_string()) .unwrap_or_else(|| default.to_string())
}; };
let get_u16 = |key: &str, default: u16| -> u16 { let get_u16 = |key: &str, default: u16| -> u16 {
config_map config_map
.get(key) .get(key)
.and_then(|v| v.parse().ok()) .and_then(|v| v.parse().ok())
.unwrap_or(default) .unwrap_or(default)
}; };
// Read from Vault with fallback to defaults // 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 let (drive_server, drive_access, drive_secret) = secrets
.as_ref() .as_ref()
.map(|s| s.get_drive_config()) .and_then(|s| s.get_drive_config().ok())
.unwrap_or_else(|| { .unwrap_or((String::new(), String::new(), String::new()));
(
crate::core::urls::InternalUrls::DRIVE.to_string(),
"minioadmin".to_string(),
"minioadmin".to_string(),
)
});
let drive = DriveConfig { let drive = DriveConfig {
server: drive_server, server: drive_server,
access_key: drive_access, access_key: drive_access,
secret_key: drive_secret, secret_key: drive_secret,
@ -317,7 +311,7 @@ impl AppConfig {
server: ServerConfig { server: ServerConfig {
host: get_str("server_host", "0.0.0.0"), host: get_str("server_host", "0.0.0.0"),
port, 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: { site_path: {
ConfigManager::new(pool.clone()).get_config( ConfigManager::new(pool.clone()).get_config(
@ -331,17 +325,11 @@ impl AppConfig {
} }
pub fn from_env() -> Result<Self, anyhow::Error> { pub fn from_env() -> Result<Self, anyhow::Error> {
// Read from Vault with fallback to defaults // 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 let (drive_server, drive_access, drive_secret) = secrets
.as_ref() .as_ref()
.map(|s| s.get_drive_config()) .and_then(|s| s.get_drive_config().ok())
.unwrap_or_else(|| { .unwrap_or((String::new(), String::new(), String::new()));
(
crate::core::urls::InternalUrls::DRIVE.to_string(),
"minioadmin".to_string(),
"minioadmin".to_string(),
)
});
let minio = DriveConfig { let minio = DriveConfig {
server: drive_server, server: drive_server,
@ -368,7 +356,7 @@ impl AppConfig {
server: ServerConfig { server: ServerConfig {
host: "0.0.0.0".to_string(), host: "0.0.0.0".to_string(),
port, port,
base_url: "http://localhost:8080".to_string(), base_url: "".to_string(),
}, },
site_path: format!("{}/sites", crate::core::shared::utils::get_stack_path()), site_path: format!("{}/sites", crate::core::shared::utils::get_stack_path()),

View file

@ -24,8 +24,8 @@ pub async fn reload_config(
// Get LLM config // Get LLM config
let llm_url = config_manager let llm_url = config_manager
.get_config(&default_bot_id, "llm-url", Some("http://localhost:8081")) .get_config(&default_bot_id, "llm-url", Some(""))
.unwrap_or_else(|_| "http://localhost:8081".to_string()); .unwrap_or_else(|_| "".to_string());
let llm_model = config_manager let llm_model = config_manager
.get_config(&default_bot_id, "llm-model", Some("local")) .get_config(&default_bot_id, "llm-model", Some("local"))

View file

@ -81,7 +81,7 @@ pub async fn provision_user_handler(
.config .config
.as_ref() .as_ref()
.map(|c| c.server.base_url.clone()) .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); let provisioning = UserProvisioningService::new(state.conn.clone(), s3_client, base_url);
@ -114,7 +114,7 @@ pub async fn deprovision_user_handler(
.config .config
.as_ref() .as_ref()
.map(|c| c.server.base_url.clone()) .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); let provisioning = UserProvisioningService::new(state.conn.clone(), s3_client, base_url);
@ -257,7 +257,7 @@ pub async fn check_services_status(State(state): State<Arc<AppState>>) -> impl I
let client = create_tls_client(Some(2)); 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(); status.directory = response.status().is_success();
} }

View file

@ -39,7 +39,7 @@ pub struct EmbeddingConfig {
impl Default for EmbeddingConfig { impl Default for EmbeddingConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
embedding_url: "http://localhost:8082".to_string(), embedding_url: "".to_string(),
embedding_model: "BAAI/bge-multilingual-gemma2".to_string(), embedding_model: "BAAI/bge-multilingual-gemma2".to_string(),
embedding_key: None, embedding_key: None,
dimensions: 2048, dimensions: 2048,
@ -58,7 +58,7 @@ impl EmbeddingConfig {
/// Load embedding config from bot's config.csv (similar to llm-url, llm-model) /// Load embedding config from bot's config.csv (similar to llm-url, llm-model)
/// This allows configuring embedding server per-bot in config.csv: /// 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-model,bge-small-en-v1.5
/// embedding-dimensions,384 /// embedding-dimensions,384
/// embedding-batch-size,16 /// embedding-batch-size,16
@ -77,7 +77,7 @@ impl EmbeddingConfig {
.ok() .ok()
.filter(|s| !s.is_empty()), .filter(|s| !s.is_empty()),
Err(_) => None, Err(_) => None,
}.unwrap_or_else(|| "http://localhost:8082".to_string()); }.unwrap_or_else(|| "".to_string());
let embedding_model = match pool.get() { let embedding_model = match pool.get() {
Ok(mut conn) => bot_configuration Ok(mut conn) => bot_configuration

View file

@ -22,7 +22,7 @@ pub struct QdrantConfig {
impl Default for QdrantConfig { impl Default for QdrantConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
url: "http://localhost:6333".to_string(), url: "".to_string(),
api_key: None, api_key: None,
timeout_secs: 30, timeout_secs: 30,
} }
@ -36,8 +36,8 @@ impl QdrantConfig {
} else { } else {
let config_manager = ConfigManager::new(pool); let config_manager = ConfigManager::new(pool);
let url = config_manager let url = config_manager
.get_config(bot_id, "vectordb-url", Some("http://localhost:6333")) .get_config(bot_id, "vectordb-url", Some(""))
.unwrap_or_else(|_| "http://localhost:6333".to_string()); .unwrap_or_else(|_| "".to_string());
(url, None) (url, None)
}; };
Self { Self {

View file

@ -439,7 +439,7 @@ async fn get_bot_config(state: &AppState) -> HashMap<String, String> {
fn get_base_url(state: &AppState) -> String { fn get_base_url(state: &AppState) -> String {
let _ = state; let _ = state;
"http://localhost:9000".to_string() "".to_string()
} }
async fn create_or_get_oauth_user( async fn create_or_get_oauth_user(

View file

@ -15,7 +15,7 @@ pub async fn setup_alm() -> anyhow::Result<()> {
let config_path = stack_path.join("conf/alm-ci/config.yaml"); let config_path = stack_path.join("conf/alm-ci/config.yaml");
// Check Vault if already set up // 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 secrets_manager.is_enabled() {
if let Ok(secrets) = secrets_manager.get_secret(crate::core::secrets::SecretPaths::ALM).await { 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")) { if let (Some(user), Some(token)) = (secrets.get("username"), secrets.get("runner_token")) {
@ -52,7 +52,7 @@ PATH = {}/data/alm/gitea.db
[server] [server]
HTTP_PORT = 3000 HTTP_PORT = 3000
DOMAIN = localhost DOMAIN = localhost
ROOT_URL = http://localhost:3000 ROOT_URL =
[security] [security]
INSTALL_LOCK = true INSTALL_LOCK = true
@ -67,7 +67,7 @@ INSTALL_LOCK = true
// Generate credentials and attempt to configure via HTTP API // Generate credentials and attempt to configure via HTTP API
let username = "botserver"; let username = "botserver";
let password = generate_random_string(32); 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 // 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 // Note: Forgejo CLI binary may segfault on some systems, so we use curl
@ -95,7 +95,7 @@ INSTALL_LOCK = true
} }
// Store in Vault // 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() { if secrets_manager.is_enabled() {
let mut secrets = HashMap::new(); let mut secrets = HashMap::new();
secrets.insert("url".to_string(), alm_url.to_string()); secrets.insert("url".to_string(), alm_url.to_string());

View file

@ -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() { if !manager.is_enabled() {
return Err(anyhow::anyhow!( return Err(anyhow::anyhow!(
"Vault not configured. Set VAULT_ADDR and VAULT_TOKEN" "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<()> { async fn vault_put(path: &str, kvs: &[&str]) -> Result<()> {
let manager = SecretsManager::from_env()?; let manager = SecretsManager::get()?.clone();
if !manager.is_enabled() { if !manager.is_enabled() {
return Err(anyhow::anyhow!("Vault not configured")); 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<()> { async fn vault_get(path: &str, key: Option<&str>) -> Result<()> {
let manager = SecretsManager::from_env()?; let manager = SecretsManager::get()?.clone();
if !manager.is_enabled() { if !manager.is_enabled() {
return Err(anyhow::anyhow!("Vault not configured")); return Err(anyhow::anyhow!("Vault not configured"));
} }
@ -751,7 +751,7 @@ async fn print_version(show_all: bool) -> Result<()> {
println!(); println!();
println!("Secrets:"); println!("Secrets:");
if let Ok(manager) = SecretsManager::from_env() { if let Ok(manager) = SecretsManager::get() {
if manager.is_enabled() { if manager.is_enabled() {
match manager.health_check().await { match manager.health_check().await {
Ok(true) => println!(" Vault: connected"), Ok(true) => println!(" Vault: connected"),
@ -805,7 +805,7 @@ fn generate_secret_key() -> String {
} }
async fn rotate_secret(component: &str) -> Result<()> { async fn rotate_secret(component: &str) -> Result<()> {
let manager = SecretsManager::from_env()?; let manager = SecretsManager::get()?.clone();
if !manager.is_enabled() { if !manager.is_enabled() {
return Err(anyhow::anyhow!( return Err(anyhow::anyhow!(
"Vault not configured. Set VAULT_ADDR and VAULT_TOKEN" "Vault not configured. Set VAULT_ADDR and VAULT_TOKEN"
@ -1096,7 +1096,7 @@ async fn rotate_all_secrets() -> Result<()> {
return Ok(()); return Ok(());
} }
let manager = SecretsManager::from_env()?; let manager = SecretsManager::get()?.clone();
if !manager.is_enabled() { if !manager.is_enabled() {
return Err(anyhow::anyhow!("Vault not configured")); return Err(anyhow::anyhow!("Vault not configured"));
} }
@ -1183,7 +1183,7 @@ async fn verify_rotation(component: &str) -> Result<()> {
match component { match component {
"tables" => { "tables" => {
let manager = SecretsManager::from_env()?; let manager = SecretsManager::get()?.clone();
let secrets = manager.get_secret(SecretPaths::TABLES).await?; let secrets = manager.get_secret(SecretPaths::TABLES).await?;
let host = secrets.get("host").cloned().unwrap_or_else(|| "localhost".to_string()); 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 // Try to determine the health endpoint
let health_urls = vec![ let health_urls = vec![
"http://localhost:8080/health", "/health",
"http://localhost:5858/health", "/health",
"http://localhost:3000/health", "/health",
]; ];
let mut success = false; let mut success = false;
@ -1268,7 +1268,7 @@ async fn verify_rotation(component: &str) -> Result<()> {
} }
async fn vault_health() -> Result<()> { async fn vault_health() -> Result<()> {
let manager = SecretsManager::from_env()?; let manager = SecretsManager::get()?.clone();
if !manager.is_enabled() { if !manager.is_enabled() {
println!("x Vault not configured"); println!("x Vault not configured");

View file

@ -74,11 +74,11 @@ pub async fn setup_directory() -> anyhow::Result<crate::core::package_manager::s
let stack_path = get_stack_path(); let stack_path = get_stack_path();
let base_url = "http://localhost:8300".to_string(); let base_url = "".to_string();
let config_path = PathBuf::from(&stack_path).join("conf/system/directory_config.json"); let config_path = PathBuf::from(&stack_path).join("conf/system/directory_config.json");
// Check if config already exists in Vault first // Check if config already exists in Vault first
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 secrets_manager.is_enabled() {
if let Ok(secrets) = secrets_manager.get_secret(crate::core::secrets::SecretPaths::DIRECTORY).await { if let Ok(secrets) = secrets_manager.get_secret(crate::core::secrets::SecretPaths::DIRECTORY).await {
if let (Some(client_id), Some(client_secret)) = (secrets.get("client_id"), secrets.get("client_secret")) { if let (Some(client_id), Some(client_secret)) = (secrets.get("client_id"), secrets.get("client_secret")) {
@ -98,7 +98,7 @@ pub async fn setup_directory() -> anyhow::Result<crate::core::package_manager::s
issuer: secrets.get("issuer").cloned().unwrap_or_else(|| base_url.clone()), issuer: secrets.get("issuer").cloned().unwrap_or_else(|| base_url.clone()),
client_id: client_id.clone(), client_id: client_id.clone(),
client_secret: client_secret.clone(), client_secret: client_secret.clone(),
redirect_uri: secrets.get("redirect_uri").cloned().unwrap_or_else(|| "http://localhost:3000/auth/callback".to_string()), redirect_uri: secrets.get("redirect_uri").cloned().unwrap_or_else(|| "/auth/callback".to_string()),
project_id: secrets.get("project_id").cloned().unwrap_or_default(), project_id: secrets.get("project_id").cloned().unwrap_or_default(),
api_url: secrets.get("api_url").cloned().unwrap_or_else(|| base_url.clone()), api_url: secrets.get("api_url").cloned().unwrap_or_else(|| base_url.clone()),
service_account_key: secrets.get("service_account_key").cloned(), service_account_key: secrets.get("service_account_key").cloned(),
@ -135,7 +135,7 @@ pub async fn setup_directory() -> anyhow::Result<crate::core::package_manager::s
.map_err(|e| anyhow::anyhow!("Failed to initialize directory: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to initialize directory: {}", e))?;
// Store credentials in Vault // Store credentials 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() { if secrets_manager.is_enabled() {
let mut secrets = HashMap::new(); let mut secrets = HashMap::new();
secrets.insert("url".to_string(), config.base_url.clone()); secrets.insert("url".to_string(), config.base_url.clone());

View file

@ -74,7 +74,7 @@ impl DirectorySetup {
issuer: self.base_url.clone(), issuer: self.base_url.clone(),
client_id, client_id,
client_secret, client_secret,
redirect_uri: "http://localhost:3000/auth/callback".to_string(), redirect_uri: "/auth/callback".to_string(),
project_id, project_id,
api_url: self.base_url.clone(), api_url: self.base_url.clone(),
service_account_key: None, service_account_key: None,
@ -227,8 +227,8 @@ impl DirectorySetup {
let app_body = serde_json::json!({ let app_body = serde_json::json!({
"name": "BotServer", "name": "BotServer",
"redirectUris": [ "redirectUris": [
"http://localhost:3000/auth/callback", "/auth/callback",
"http://localhost:8080/auth/callback" "/auth/callback"
], ],
"responseTypes": ["OIDC_RESPONSE_TYPE_CODE"], "responseTypes": ["OIDC_RESPONSE_TYPE_CODE"],
"grantTypes": [ "grantTypes": [
@ -237,7 +237,7 @@ impl DirectorySetup {
], ],
"appType": "OIDC_APP_TYPE_WEB", "appType": "OIDC_APP_TYPE_WEB",
"authMethodType": "OIDC_AUTH_METHOD_TYPE_POST", "authMethodType": "OIDC_AUTH_METHOD_TYPE_POST",
"postLogoutRedirectUris": ["http://localhost:3000"], "postLogoutRedirectUris": [""],
"devMode": true "devMode": true
}); });
@ -320,14 +320,14 @@ mod tests {
#[test] #[test]
fn test_directory_config_serialization() { fn test_directory_config_serialization() {
let config = DirectoryConfig { let config = DirectoryConfig {
base_url: "http://localhost:8300".to_string(), base_url: "".to_string(),
issuer_url: "http://localhost:8300".to_string(), issuer_url: "".to_string(),
issuer: "http://localhost:8300".to_string(), issuer: "".to_string(),
client_id: "test_client".to_string(), client_id: "test_client".to_string(),
client_secret: "test_secret".to_string(), client_secret: "test_secret".to_string(),
redirect_uri: "http://localhost:3000/callback".to_string(), redirect_uri: "/callback".to_string(),
project_id: "12345".to_string(), project_id: "12345".to_string(),
api_url: "http://localhost:8300".to_string(), api_url: "".to_string(),
service_account_key: None, service_account_key: None,
}; };

View file

@ -7,6 +7,7 @@ use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Arc as StdArc; use std::sync::Arc as StdArc;
use std::sync::OnceLock;
use std::sync::RwLock as StdRwLock; use std::sync::RwLock as StdRwLock;
use uuid::Uuid; use uuid::Uuid;
use vaultrs::client::{VaultClient, VaultClientSettingsBuilder}; use vaultrs::client::{VaultClient, VaultClientSettingsBuilder};
@ -130,19 +131,19 @@ impl SecretsManager {
settings_builder.verify(true); settings_builder.verify(true);
if ca_path.exists() { if ca_path.exists() {
info!("Using CA certificate for Vault: {}", ca_cert); debug!("Using CA certificate for Vault: {}", ca_cert);
settings_builder.ca_certs(vec![ca_cert]); settings_builder.ca_certs(vec![ca_cert]);
} }
} }
if cert_path.exists() && key_path.exists() && !skip_verify { if cert_path.exists() && key_path.exists() && !skip_verify {
info!("Using mTLS client certificate for Vault: {}", client_cert); debug!("Using mTLS client certificate for Vault: {}", client_cert);
} }
let settings = settings_builder.build()?; let settings = settings_builder.build()?;
let client = VaultClient::new(settings)?; let client = VaultClient::new(settings)?;
info!("Vault client initialized with TLS: {}", addr); debug!("Vault client initialized with TLS: {}", addr);
Ok(Self { Ok(Self {
client: Some(StdArc::new(client)), client: Some(StdArc::new(client)),
@ -152,6 +153,26 @@ impl SecretsManager {
}) })
} }
pub fn get() -> Result<&'static SecretsManager> {
static INIT: OnceLock<Result<SecretsManager>> = 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<SecretsManager> {
Self::get().map(|sm| sm.clone())
}
pub fn is_enabled(&self) -> bool { pub fn is_enabled(&self) -> bool {
self.enabled self.enabled
} }
@ -208,7 +229,7 @@ impl SecretsManager {
default.to_string() 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) // Try to read from Vault using std process (curl)
if let Ok(vault_addr) = std::env::var("VAULT_ADDR") { if let Ok(vault_addr) = std::env::var("VAULT_ADDR") {
if let Ok(vault_token) = std::env::var("VAULT_TOKEN") { if let Ok(vault_token) = std::env::var("VAULT_TOKEN") {
@ -227,11 +248,14 @@ impl SecretsManager {
Ok(output) if output.status.success() => { Ok(output) if output.status.success() => {
if let Ok(data) = serde_json::from_slice::<serde_json::Value>(&output.stdout) { if let Ok(data) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
if let Some(secret_data) = data.get("data").and_then(|d| d.get("data")) { 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 host = secret_data.get("host").and_then(|v| v.as_str())
let accesskey = secret_data.get("accesskey").and_then(|v| v.as_str()).unwrap_or("minioadmin"); .ok_or_else(|| anyhow!("drive host not configured in Vault"))?;
let secret = secret_data.get("secret").and_then(|v| v.as_str()).unwrap_or("minioadmin"); 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); 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,11 +269,10 @@ impl SecretsManager {
} }
} }
log::warn!("get_drive_config: Falling back to defaults - Vault not available"); Err(anyhow!("Drive configuration not available in Vault and VAULT_ADDR/VAULT_TOKEN not set"))
("localhost:9100".to_string(), "minioadmin".to_string(), "minioadmin".to_string())
} }
pub fn get_cache_config(&self) -> (String, u16, Option<String>) { pub fn get_cache_config(&self) -> Result<(String, u16, Option<String>)> {
if let Ok(vault_addr) = std::env::var("VAULT_ADDR") { if let Ok(vault_addr) = std::env::var("VAULT_ADDR") {
if let Ok(vault_token) = std::env::var("VAULT_TOKEN") { if let Ok(vault_token) = std::env::var("VAULT_TOKEN") {
let ca_cert = std::env::var("VAULT_CACERT").unwrap_or_default(); let ca_cert = std::env::var("VAULT_CACERT").unwrap_or_default();
@ -265,22 +288,24 @@ impl SecretsManager {
if output.status.success() { if output.status.success() {
if let Ok(data) = serde_json::from_slice::<serde_json::Value>(&output.stdout) { if let Ok(data) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
if let Some(secret_data) = data.get("data").and_then(|d| d.get("data")) { 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 host = secret_data.get("host").and_then(|v| v.as_str())
let port = secret_data.get("port").and_then(|v| v.as_str()).unwrap_or("6379").parse().unwrap_or(6379); .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()); 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); 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"); Err(anyhow!("Cache configuration not available in Vault and VAULT_ADDR/VAULT_TOKEN not set"))
("localhost".to_string(), 6379, None)
} }
pub fn get_qdrant_config(&self) -> (String, Option<String>) { pub fn get_qdrant_config(&self) -> Result<(String, Option<String>)> {
if let Ok(vault_addr) = std::env::var("VAULT_ADDR") { if let Ok(vault_addr) = std::env::var("VAULT_ADDR") {
if let Ok(vault_token) = std::env::var("VAULT_TOKEN") { if let Ok(vault_token) = std::env::var("VAULT_TOKEN") {
let ca_cert = std::env::var("VAULT_CACERT").unwrap_or_default(); let ca_cert = std::env::var("VAULT_CACERT").unwrap_or_default();
@ -296,31 +321,34 @@ impl SecretsManager {
if output.status.success() { if output.status.success() {
if let Ok(data) = serde_json::from_slice::<serde_json::Value>(&output.stdout) { if let Ok(data) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
if let Some(secret_data) = data.get("data").and_then(|d| d.get("data")) { 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()); 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); 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"); Err(anyhow!("VectorDB configuration not available in Vault and VAULT_ADDR/VAULT_TOKEN not set"))
("http://localhost:6333".to_string(), None)
} }
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) { if let Ok(secrets) = Self::get_from_env(SecretPaths::TABLES) {
return ( let host = secrets.get("host").cloned()
secrets.get("host").cloned().unwrap_or_else(|| "localhost".to_string()), .ok_or_else(|| anyhow!("database host not configured"))?;
secrets.get("port").and_then(|p| p.parse().ok()).unwrap_or(5432), let port = secrets.get("port").and_then(|p| p.parse().ok())
secrets.get("database").cloned().unwrap_or_else(|| "botserver".to_string()), .ok_or_else(|| anyhow!("database port not configured"))?;
secrets.get("username").cloned().unwrap_or_else(|| "gbuser".to_string()), let database = secrets.get("database").cloned()
secrets.get("password").cloned().unwrap_or_default(), .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)> { 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)> { pub async fn get_database_config(&self) -> Result<(String, u16, String, String, String)> {
let s = self.get_secret(SecretPaths::TABLES).await?; let s = self.get_secret(SecretPaths::TABLES).await?;
Ok(( let host = s.get("host").cloned()
s.get("host").cloned().unwrap_or_else(|| "localhost".into()), .ok_or_else(|| anyhow!("database host not configured in Vault"))?;
s.get("port").and_then(|p| p.parse().ok()).unwrap_or(5432), let port = s.get("port").and_then(|p| p.parse().ok())
s.get("database") .ok_or_else(|| anyhow!("database port not configured in Vault"))?;
.cloned() let database = s.get("database").cloned()
.unwrap_or_else(|| "botserver".into()), .ok_or_else(|| anyhow!("database name not configured in Vault"))?;
s.get("username") let username = s.get("username").cloned()
.cloned() .ok_or_else(|| anyhow!("database username not configured in Vault"))?;
.unwrap_or_else(|| "gbuser".into()), let password = s.get("password").cloned().unwrap_or_default();
s.get("password").cloned().unwrap_or_default(), Ok((host, port, database, username, password))
))
} }
pub async fn get_database_url(&self) -> Result<String> { pub async fn get_database_url(&self) -> Result<String> {
@ -374,10 +401,10 @@ impl SecretsManager {
pub async fn get_directory_config(&self) -> Result<(String, String, String, String)> { pub async fn get_directory_config(&self) -> Result<(String, String, String, String)> {
let s = self.get_secret(SecretPaths::DIRECTORY).await?; 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(( Ok((
s.get("url") url,
.cloned()
.unwrap_or_else(|| "http://localhost:9000".into()),
s.get("project_id").cloned().unwrap_or_default(), s.get("project_id").cloned().unwrap_or_default(),
s.get("client_id").cloned().unwrap_or_default(), s.get("client_id").cloned().unwrap_or_default(),
s.get("client_secret").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<String>)> { pub async fn get_vectordb_config(&self) -> Result<(String, Option<String>)> {
let s = self.get_secret(SecretPaths::VECTORDB).await?; let s = self.get_secret(SecretPaths::VECTORDB).await?;
Ok(( let url = s.get("url").cloned()
s.get("url") .ok_or_else(|| anyhow!("vectordb url not configured in Vault"))?;
.cloned() Ok((url, s.get("api_key").cloned()))
.unwrap_or_else(|| "http://localhost:6333".into()),
s.get("api_key").cloned(),
))
} }
pub async fn get_observability_config(&self) -> Result<(String, String, String, String)> { pub async fn get_observability_config(&self) -> Result<(String, String, String, String)> {
let s = self.get_secret(SecretPaths::OBSERVABILITY).await?; 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(( Ok((
s.get("url") url,
.cloned()
.unwrap_or_else(|| "http://localhost:8086".into()),
s.get("org").cloned().unwrap_or_else(|| "system".into()), s.get("org").cloned().unwrap_or_else(|| "system".into()),
s.get("bucket").cloned().unwrap_or_else(|| "metrics".into()), s.get("bucket").cloned().unwrap_or_else(|| "metrics".into()),
s.get("token").cloned().unwrap_or_default(), s.get("token").cloned().unwrap_or_default(),
@ -441,13 +465,13 @@ impl SecretsManager {
}); });
if let Ok(Some(secrets)) = rx.recv() { if let Ok(Some(secrets)) = rx.recv() {
return ( 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("project_id").cloned().unwrap_or_default(),
secrets.get("client_id").cloned().unwrap_or_default(), secrets.get("client_id").cloned().unwrap_or_default(),
secrets.get("client_secret").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) { pub fn get_email_config(&self) -> (String, u16, String, String, String) {
@ -496,14 +520,14 @@ impl SecretsManager {
}); });
if let Ok(Some(secrets)) = rx.recv() { if let Ok(Some(secrets)) = rx.recv() {
return ( 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("model").cloned().unwrap_or_else(|| "gpt-4".into()),
secrets.get("openai_key").cloned(), secrets.get("openai_key").cloned(),
secrets.get("anthropic_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) { pub fn get_meet_config(&self) -> (String, String, String) {
@ -524,12 +548,12 @@ impl SecretsManager {
}); });
if let Ok(Some(secrets)) = rx.recv() { if let Ok(Some(secrets)) = rx.recv() {
return ( 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_id").cloned().unwrap_or_default(),
secrets.get("app_secret").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<String>) { pub fn get_vectordb_config_sync(&self) -> (String, Option<String>) {
@ -550,11 +574,11 @@ impl SecretsManager {
}); });
if let Ok(Some(secrets)) = rx.recv() { if let Ok(Some(secrets)) = rx.recv() {
return ( 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(), 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) { pub fn get_observability_config_sync(&self) -> (String, String, String, String) {
@ -575,13 +599,13 @@ impl SecretsManager {
}); });
if let Ok(Some(secrets)) = rx.recv() { if let Ok(Some(secrets)) = rx.recv() {
return ( 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("org").cloned().unwrap_or_else(|| "system".into()),
secrets.get("bucket").cloned().unwrap_or_else(|| "metrics".into()), secrets.get("bucket").cloned().unwrap_or_else(|| "metrics".into()),
secrets.get("token").cloned().unwrap_or_default(), 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) { pub fn get_alm_config(&self) -> (String, String, String) {
@ -602,12 +626,12 @@ impl SecretsManager {
}); });
if let Ok(Some(secrets)) = rx.recv() { if let Ok(Some(secrets)) = rx.recv() {
return ( 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("token").cloned().unwrap_or_default(),
secrets.get("default_org").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 { pub fn get_jwt_secret_sync(&self) -> String {
@ -712,12 +736,14 @@ impl SecretsManager {
secrets.insert("username".into(), "gbuser".into()); secrets.insert("username".into(), "gbuser".into());
secrets.insert("password".into(), "changeme".into()); secrets.insert("password".into(), "changeme".into());
} }
"directory" | "gbo/directory" | "system/directory" => { "directory" | "gbo/directory" | "system/directory" => {
secrets.insert("url".into(), "http://localhost:9000".into()); secrets.insert("url".into(), "".into());
secrets.insert("project_id".into(), String::new()); secrets.insert("host".into(), "localhost".into());
secrets.insert("client_id".into(), String::new()); secrets.insert("port".into(), "9000".into());
secrets.insert("client_secret".into(), String::new()); 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" => { "drive" | "gbo/drive" | "system/drive" => {
secrets.insert("host".into(), "localhost".into()); secrets.insert("host".into(), "localhost".into());
secrets.insert("port".into(), "9000".into()); secrets.insert("port".into(), "9000".into());
@ -737,35 +763,35 @@ impl SecretsManager {
secrets.insert("smtp_from".into(), String::new()); secrets.insert("smtp_from".into(), String::new());
} }
"llm" | "gbo/llm" | "system/llm" => { "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("model".into(), "gpt-4".into());
secrets.insert("openai_key".into(), String::new()); secrets.insert("openai_key".into(), String::new());
secrets.insert("anthropic_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" => { "encryption" | "gbo/encryption" | "system/encryption" => {
secrets.insert("master_key".into(), String::new()); secrets.insert("master_key".into(), String::new());
} }
"meet" | "gbo/meet" | "system/meet" => { "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_id".into(), String::new());
secrets.insert("app_secret".into(), String::new()); secrets.insert("app_secret".into(), String::new());
} }
"vectordb" | "gbo/vectordb" | "system/vectordb" => { "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("host".to_string(), "localhost".into());
secrets.insert("port".to_string(), "6333".into()); secrets.insert("port".to_string(), "6333".into());
secrets.insert("grpc_port".to_string(), "6334".into()); secrets.insert("grpc_port".to_string(), "6334".into());
secrets.insert("api_key".to_string(), String::new()); secrets.insert("api_key".to_string(), String::new());
} }
"observability" | "gbo/observability" | "system/observability" => { "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("org".into(), "system".into());
secrets.insert("bucket".into(), "metrics".into()); secrets.insert("bucket".into(), "metrics".into());
secrets.insert("token".into(), String::new()); secrets.insert("token".into(), String::new());
} }
"alm" | "gbo/alm" | "system/alm" => { "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("token".into(), String::new());
secrets.insert("default_org".into(), String::new()); secrets.insert("default_org".into(), String::new());
} }
@ -779,14 +805,14 @@ impl SecretsManager {
secrets.insert("secret_key".into(), String::new()); secrets.insert("secret_key".into(), String::new());
} }
"app" | "gbo/app" | "system/app" => { "app" | "gbo/app" | "system/app" => {
secrets.insert("url".into(), "http://localhost:8080".into()); secrets.insert("url".into(), "".into());
secrets.insert("environment".into(), "development".into()); secrets.insert("environment".into(), "development".into());
} }
"jwt" | "gbo/jwt" | "system/jwt" => { "jwt" | "gbo/jwt" | "system/jwt" => {
secrets.insert("secret".into(), String::new()); secrets.insert("secret".into(), String::new());
} }
"models" | "gbo/models" | "system/models" => { "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); log::debug!("No default values for secret path: {}", path);

View file

@ -18,7 +18,7 @@ pub async fn send_invitation_email(
custom_message: Option<String>, custom_message: Option<String>,
invitation_id: Uuid, invitation_id: Uuid,
) -> Result<(), String> { ) -> Result<(), String> {
let smtp = crate::core::secrets::SecretsManager::from_env() let smtp = crate::core::secrets::SecretsManager::get()
.ok() .ok()
.and_then(|sm| { .and_then(|sm| {
let sm_owned = sm.clone(); let sm_owned = sm.clone();
@ -28,9 +28,7 @@ pub async fn send_invitation_email(
.enable_all() .enable_all()
.build(); .build();
let result = if let Ok(rt) = rt { let result = if let Ok(rt) = rt {
rt.block_on(async move { rt.block_on(async move { sm_owned.get_secret(crate::core::secrets::SecretPaths::EMAIL).await.ok() })
sm_owned.get_secret(crate::core::secrets::SecretPaths::EMAIL).await.ok()
})
} else { } else {
None None
}; };

View file

@ -250,13 +250,13 @@ impl Default for TestAppStateBuilder {
#[cfg(feature = "directory")] #[cfg(feature = "directory")]
pub fn create_mock_auth_service() -> AuthService { pub fn create_mock_auth_service() -> AuthService {
let config = ZitadelConfig { let config = ZitadelConfig {
issuer_url: "http://localhost:9000".to_string(), issuer_url: "".to_string(),
issuer: "http://localhost:9000".to_string(), issuer: "".to_string(),
client_id: "mock_client_id".to_string(), client_id: "mock_client_id".to_string(),
client_secret: "mock_client_secret".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(), project_id: "mock_project_id".to_string(),
api_url: "http://localhost:9000".to_string(), api_url: "".to_string(),
service_account_key: None, service_account_key: None,
}; };

View file

@ -34,7 +34,7 @@ static SECRETS_MANAGER: std::sync::LazyLock<Arc<RwLock<Option<SecretsManager>>>>
std::sync::LazyLock::new(|| Arc::new(RwLock::new(None))); std::sync::LazyLock::new(|| Arc::new(RwLock::new(None)));
pub async fn init_secrets_manager() -> Result<()> { 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))?; let mut guard = SECRETS_MANAGER.write().map_err(|e| anyhow::anyhow!("Lock poisoned: {}", e))?;
*guard = Some(manager); *guard = Some(manager);
Ok(()) Ok(())

View file

@ -490,16 +490,7 @@ impl ApiUrls {
pub struct InternalUrls; pub struct InternalUrls;
impl InternalUrls { impl InternalUrls {
pub const DIRECTORY_BASE: &'static str = "http://localhost:8300"; // No localhost defaults - services must be configured via Vault or env vars
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";
pub const BOTMODELS_VISION_QRCODE: &'static str = "/api/vision/qrcode"; 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_SPEECH_TO_TEXT: &'static str = "/api/speech/to-text";
pub const BOTMODELS_VISION_DESCRIBE_VIDEO: &'static str = "/api/vision/describe-video"; pub const BOTMODELS_VISION_DESCRIBE_VIDEO: &'static str = "/api/vision/describe-video";

View file

@ -268,7 +268,7 @@ fn save_setup_credentials(result: &BootstrapResult) {
Password: {:<46} Password: {:<46}
Email: {:<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!("{:^60}", "");
println!("{:56}", "🌐 LOGIN NOW:"); println!("{:56}", "🌐 LOGIN NOW:");
println!("{:^60}", ""); println!("{:^60}", "");
println!("{:56}", "http://localhost:8080/suite/login"); println!("{:56}", "/suite/login");
println!("{:^60}", ""); println!("{:^60}", "");
println!("{}", separator); println!("{}", separator);
println!("{:^60}", ""); println!("{:^60}", "");

View file

@ -65,8 +65,8 @@ fn is_tracking_pixel_enabled(state: &Arc<AppState>, bot_id: Option<Uuid>) -> boo
fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc<AppState>) -> String { fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc<AppState>) -> String {
let config_manager = crate::core::config::ConfigManager::new(state.conn.clone()); let config_manager = crate::core::config::ConfigManager::new(state.conn.clone());
let base_url = config_manager let base_url = config_manager
.get_config(&Uuid::nil(), "server-url", Some("http://localhost:9000")) .get_config(&Uuid::nil(), "server-url", Some(""))
.unwrap_or_else(|_| "http://localhost:9000".to_string()); .unwrap_or_else(|_| "".to_string());
let pixel_url = format!("{}/api/email/tracking/pixel/{}", base_url, tracking_id); let pixel_url = format!("{}/api/email/tracking/pixel/{}", base_url, tracking_id);
let pixel_html = format!( let pixel_html = format!(

View file

@ -31,8 +31,8 @@ pub fn is_tracking_pixel_enabled(state: &Arc<AppState>, bot_id: Option<Uuid>) ->
pub fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc<AppState>) -> String { pub fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc<AppState>) -> String {
let config_manager = crate::core::config::ConfigManager::new(state.conn.clone()); let config_manager = crate::core::config::ConfigManager::new(state.conn.clone());
let base_url = config_manager let base_url = config_manager
.get_config(&Uuid::nil(), "server-url", Some("http://localhost:9000")) .get_config(&Uuid::nil(), "server-url", Some(""))
.unwrap_or_else(|_| "http://localhost:9000".to_string()); .unwrap_or_else(|_| "".to_string());
let pixel_url = format!("{}/api/email/tracking/pixel/{}", base_url, tracking_id); let pixel_url = format!("{}/api/email/tracking/pixel/{}", base_url, tracking_id);
let pixel_html = format!( let pixel_html = format!(

View file

@ -421,11 +421,11 @@ impl EmailEmbeddingGenerator {
} }
pub async fn generate_text_embedding(&self, text: &str) -> Result<Vec<f32>> { pub async fn generate_text_embedding(&self, text: &str) -> Result<Vec<f32>> {
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(); let (llm_url, _, _, _, ollama_url) = sm.get_llm_config();
if !ollama_url.is_empty() { ollama_url } else { llm_url } if !ollama_url.is_empty() { ollama_url } else { llm_url }
} else { } else {
"http://localhost:8082".to_string() "".to_string()
}; };
match self.generate_local_embedding(text, &embedding_url).await { match self.generate_local_embedding(text, &embedding_url).await {
Ok(embedding) => Ok(embedding), Ok(embedding) => Ok(embedding),

View file

@ -45,14 +45,14 @@ pub async fn ensure_llama_servers_running(
.get_config(&default_bot_id, "llm-server", Some("true")) .get_config(&default_bot_id, "llm-server", Some("true"))
.unwrap_or_else(|_| "true".to_string()), .unwrap_or_else(|_| "true".to_string()),
config_manager config_manager
.get_config(&default_bot_id, "llm-url", Some("http://localhost:8081")) .get_config(&default_bot_id, "llm-url", Some(""))
.unwrap_or_else(|_| "http://localhost:8081".to_string()), .unwrap_or_else(|_| "".to_string()),
config_manager config_manager
.get_config(&default_bot_id, "llm-model", None) .get_config(&default_bot_id, "llm-model", None)
.unwrap_or_default(), .unwrap_or_default(),
config_manager config_manager
.get_config(&default_bot_id, "embedding-url", Some("http://localhost:8082")) .get_config(&default_bot_id, "embedding-url", Some(""))
.unwrap_or_else(|_| "http://localhost:8082".to_string()), .unwrap_or_else(|_| "".to_string()),
embedding_model_result.unwrap_or_default(), embedding_model_result.unwrap_or_default(),
config_manager config_manager
.get_config(&default_bot_id, "llm-server-path", None) .get_config(&default_bot_id, "llm-server-path", None)

View file

@ -289,7 +289,7 @@ impl LLMProvider for OpenAIClient {
128000 // Cerebras gpt-oss models and GPT-4 variants 128000 // Cerebras gpt-oss models and GPT-4 variants
} else if model.contains("gpt-3.5") { } else if model.contains("gpt-3.5") {
16385 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 768 // Local llama.cpp server context limit
} else { } else {
32768 // Default conservative limit for modern models 32768 // Default conservative limit for modern models
@ -378,7 +378,7 @@ impl LLMProvider for OpenAIClient {
128000 // Cerebras gpt-oss models and GPT-4 variants 128000 // Cerebras gpt-oss models and GPT-4 variants
} else if model.contains("gpt-3.5") { } else if model.contains("gpt-3.5") {
16385 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 768 // Local llama.cpp server context limit
} else { } else {
32768 // Default conservative limit for modern models 32768 // Default conservative limit for modern models
@ -885,10 +885,10 @@ mod tests {
fn test_openai_client_new_custom_url() { fn test_openai_client_new_custom_url() {
let client = OpenAIClient::new( let client = OpenAIClient::new(
"test_key".to_string(), "test_key".to_string(),
Some("http://localhost:9000".to_string()), Some("".to_string()),
None, None,
); );
assert_eq!(client.base_url, "http://localhost:9000"); assert_eq!(client.base_url, "");
} }
#[test] #[test]

View file

@ -305,16 +305,16 @@ pub async fn init_redis() -> Option<Arc<redis::Client>> {
let mut urls: Vec<String> = Vec::new(); let mut urls: Vec<String> = Vec::new();
if let Some(url) = env_url { if let Some(url) = env_url {
urls.push(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 { 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); let port = data.get("port").and_then(|p| p.parse().ok()).unwrap_or(6379);
urls.push(format!("redis://{}:{}", host, port)); urls.push(format!("redis://{}:{}", host, port));
if let Some(pass) = data.get("password") { if let Some(pass) = data.get("password") {
urls.push(format!("redis://:{}@{}:{}", pass, host, port)); urls.push(format!("redis://:{}@{}:{}", pass, host, port));
} }
} else { } else {
urls.push("redis://localhost:6379".to_string()); urls.push(String::new());
} }
} else { } else {
urls.push("redis://localhost:6379".to_string()); urls.push("redis://localhost:6379".to_string());
@ -453,8 +453,8 @@ pub async fn create_app_state(
url url
} else { } else {
config_manager config_manager
.get_config(&default_bot_id, "llm-url", Some("http://localhost:8081")) .get_config(&default_bot_id, "llm-url", Some(""))
.unwrap_or_else(|_| "http://localhost:8081".to_string()) .unwrap_or_else(|_| "".to_string())
}; };
info!("LLM URL: {}", llm_url); 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("127.0.0.1")
&& (llm_url.contains("api.z.ai") || llm_url.contains("openai.com") || llm_url.contains("anthropic.com")) && (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); warn!("External LLM URL configured ({}), but no API key provided. Falling back to local LLM at ", llm_url);
"http://localhost:8081".to_string() "".to_string()
} else { } else {
llm_url llm_url
}; };
@ -660,7 +660,7 @@ fn init_directory_service() -> Result<(Arc<Mutex<crate::directory::AuthService>>
let base_url = json let base_url = json
.get("base_url") .get("base_url")
.and_then(|v| v.as_str()) .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_id = json.get("client_id").and_then(|v| v.as_str()).unwrap_or("");
let client_secret = json let client_secret = json
.get("client_secret") .get("client_secret")
@ -703,13 +703,13 @@ fn init_directory_service() -> Result<(Arc<Mutex<crate::directory::AuthService>>
#[cfg(feature = "directory")] #[cfg(feature = "directory")]
fn default_zitadel_config() -> crate::directory::ZitadelConfig { fn default_zitadel_config() -> crate::directory::ZitadelConfig {
crate::directory::ZitadelConfig { crate::directory::ZitadelConfig {
issuer_url: "http://localhost:8300".to_string(), issuer_url: "".to_string(),
issuer: "http://localhost:8300".to_string(), issuer: "".to_string(),
client_id: String::new(), client_id: String::new(),
client_secret: String::new(), client_secret: String::new(),
redirect_uri: "http://localhost:8300/callback".to_string(), redirect_uri: "/callback".to_string(),
project_id: "default".to_string(), project_id: "default".to_string(),
api_url: "http://localhost:8300".to_string(), api_url: "".to_string(),
service_account_key: None, service_account_key: None,
} }
} }
@ -787,9 +787,9 @@ fn init_llm_provider(
.get_config( .get_config(
&bot_id, &bot_id,
"embedding-url", "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 let embedding_model = config_manager
.get_config(&bot_id, "embedding-model", Some("all-MiniLM-L6-v2")) .get_config(&bot_id, "embedding-model", Some("all-MiniLM-L6-v2"))
.unwrap_or_else(|_| "all-MiniLM-L6-v2".to_string()); .unwrap_or_else(|_| "all-MiniLM-L6-v2".to_string());

View file

@ -161,8 +161,8 @@ async fn get_llm_config(state: &Arc<AppState>, bot_id: Uuid) -> Result<(String,
let config = ConfigManager::new(state.conn.clone()); let config = ConfigManager::new(state.conn.clone());
let llm_url = config let llm_url = config
.get_config(&bot_id, "llm-url", Some("http://localhost:8081")) .get_config(&bot_id, "llm-url", Some(""))
.unwrap_or_else(|_| "http://localhost:8081".to_string()); .unwrap_or_else(|_| "".to_string());
let llm_model = config let llm_model = config
.get_config(&bot_id, "llm-model", None) .get_config(&bot_id, "llm-model", None)

View file

@ -97,8 +97,8 @@ pub async fn send_campaign_email(
let config = ConfigManager::new(state.conn.clone()); let config = ConfigManager::new(state.conn.clone());
let base_url = config let base_url = config
.get_config(&bot_id, "server-url", Some("http://localhost:3000")) .get_config(&bot_id, "server-url", Some(""))
.unwrap_or_else(|_| "http://localhost:3000".to_string()); .unwrap_or_else(|_| "".to_string());
let body_html = payload let body_html = payload
.body_html .body_html

View file

@ -357,7 +357,7 @@ impl Default for ExporterConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
exporter_type: ExporterType::Otlp, exporter_type: ExporterType::Otlp,
endpoint: "http://localhost:4317".to_string(), endpoint: "".to_string(),
headers: HashMap::new(), headers: HashMap::new(),
batch_size: 512, batch_size: 512,
flush_interval_ms: 5000, flush_interval_ms: 5000,

View file

@ -100,9 +100,9 @@ impl CorsConfig {
pub fn development() -> Self { pub fn development() -> Self {
Self { Self {
allowed_origins: vec![ allowed_origins: vec![
"http://localhost:3000".to_string(), "".to_string(),
"http://localhost:8080".to_string(), "".to_string(),
"http://localhost:9000".to_string(), "".to_string(),
"http://127.0.0.1:3000".to_string(), "http://127.0.0.1:3000".to_string(),
"http://127.0.0.1:8080".to_string(), "http://127.0.0.1:8080".to_string(),
"http://127.0.0.1:9000".to_string(), "http://127.0.0.1:9000".to_string(),
@ -502,7 +502,7 @@ mod tests {
fn test_development_config() { fn test_development_config() {
let config = CorsConfig::development(); let config = CorsConfig::development();
assert!(!config.allowed_origins.is_empty()); assert!(!config.allowed_origins.is_empty());
assert!(config.allowed_origins.contains(&"http://localhost:3000".to_string())); assert!(config.allowed_origins.contains(&"".to_string()));
} }
#[test] #[test]
@ -542,7 +542,7 @@ mod tests {
#[test] #[test]
fn test_valid_origin_format() { fn test_valid_origin_format() {
assert!(is_valid_origin_format("https://example.com")); 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("https://api.example.com:8443"));
assert!(!is_valid_origin_format("ftp://example.com")); assert!(!is_valid_origin_format("ftp://example.com"));
@ -557,7 +557,7 @@ mod tests {
.allow_localhost(true); .allow_localhost(true);
assert!(validator.is_allowed("https://example.com")); 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")); assert!(!validator.is_allowed("https://evil.com"));
} }
@ -573,7 +573,7 @@ mod tests {
#[test] #[test]
fn test_localhost_detection() { fn test_localhost_detection() {
assert!(is_localhost_origin("http://localhost")); 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("https://localhost:8443"));
assert!(is_localhost_origin("http://127.0.0.1")); assert!(is_localhost_origin("http://127.0.0.1"));
assert!(is_localhost_origin("http://127.0.0.1:9000")); assert!(is_localhost_origin("http://127.0.0.1:9000"));
@ -584,7 +584,7 @@ mod tests {
fn test_extract_host() { fn test_extract_host() {
assert_eq!(extract_host("https://example.com"), Some("example.com")); 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("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); assert_eq!(extract_host("invalid"), None);
} }

View file

@ -30,11 +30,12 @@ pub struct TlsIntegration {
impl TlsIntegration { impl TlsIntegration {
pub fn new(tls_enabled: bool) -> Self { pub fn new(tls_enabled: bool) -> Self {
let (qdrant_url, _) = if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { let sm = crate::core::secrets::SecretsManager::get().ok();
sm.get_vectordb_config_sync()
} else { let (qdrant_url, _) = sm
("http://localhost:6333".to_string(), None) .as_ref()
}; .map(|sm| sm.get_vectordb_config_sync())
.unwrap_or((String::new(), None));
let qdrant_secure = qdrant_url.replace("http://", "https://"); let qdrant_secure = qdrant_url.replace("http://", "https://");
let qdrant_port: u16 = qdrant_url let qdrant_port: u16 = qdrant_url
.split(':') .split(':')
@ -47,18 +48,13 @@ impl TlsIntegration {
.and_then(|p| p.parse().ok()) .and_then(|p| p.parse().ok())
.unwrap_or(6334); .unwrap_or(6334);
let (llm_url, _, _, _, _) = if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() let (llm_url, _, _, _, _) = sm.as_ref().map(|sm| sm.get_llm_config()).unwrap_or((
{ String::new(),
sm.get_llm_config() String::new(),
} else { None,
( None,
"http://localhost:8081".to_string(), String::new(),
"gpt-4".to_string(), ));
None,
None,
"http://localhost:11434".to_string(),
)
};
let llm_secure = llm_url.replace("http://", "https://"); let llm_secure = llm_url.replace("http://", "https://");
let llm_port: u16 = llm_url let llm_port: u16 = llm_url
.split(':') .split(':')
@ -71,55 +67,41 @@ impl TlsIntegration {
.and_then(|p| p.parse().ok()) .and_then(|p| p.parse().ok())
.unwrap_or(8444); .unwrap_or(8444);
let (cache_host, cache_port, _) = let (cache_host, cache_port, _) = sm
if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { .as_ref()
sm.get_cache_config() .map(|sm| sm.get_cache_config())
} else { .and_then(|r| r.ok())
("localhost".to_string(), 6379, None) .unwrap_or((String::new(), 6379, None));
};
let cache_tls_port = cache_port + 1; let cache_tls_port = cache_port + 1;
let (db_host, db_port, _, _, _) = let (db_host, db_port, _, _, _) = sm
if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { .as_ref()
sm.get_database_config_sync() .map(|sm| sm.get_database_config_sync())
} else { .and_then(|r| r.ok())
( .unwrap_or((
"localhost".to_string(), String::new(),
5432, 5432,
"botserver".to_string(), String::new(),
"gbuser".to_string(), String::new(),
"changeme".to_string(), String::new(),
) ));
};
let db_tls_port = db_port + 1; let db_tls_port = db_port + 1;
let (drive_host, _drive_accesskey, _drive_secret) = let (drive_host, _, _) = sm
if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { .as_ref()
sm.get_drive_config() .map(|sm| sm.get_drive_config())
} else { .and_then(|r| r.ok())
( .unwrap_or((String::new(), String::new(), String::new()));
"localhost:9100".to_string(),
"minioadmin".to_string(),
"minioadmin".to_string(),
)
};
let drive_port: u16 = drive_host let drive_port: u16 = drive_host
.split(':') .split(':')
.next_back() .next_back()
.and_then(|p| p.parse().ok()) .and_then(|p| p.parse().ok())
.unwrap_or(9100); .unwrap_or(9100);
let (directory_url, _, _, _) = let (directory_url, _, _, _) = sm
if let Ok(sm) = crate::core::secrets::SecretsManager::from_env() { .as_ref()
sm.get_directory_config_sync() .map(|sm| sm.get_directory_config_sync())
} else { .unwrap_or((String::new(), String::new(), String::new(), String::new()));
(
"http://localhost:9000".to_string(),
String::new(),
String::new(),
String::new(),
)
};
let directory_secure = directory_url.replace("http://", "https://"); let directory_secure = directory_url.replace("http://", "https://");
let directory_port: u16 = directory_url let directory_port: u16 = directory_url
.split(':') .split(':')
@ -137,8 +119,8 @@ impl TlsIntegration {
services.insert( services.insert(
"api".to_string(), "api".to_string(),
ServiceUrls { ServiceUrls {
original: "http://localhost:8080".to_string(), original: String::new(),
secure: "https://localhost:8443".to_string(), secure: String::new(),
port: 8080, port: 8080,
tls_port: 8443, tls_port: 8443,
}, },
@ -157,8 +139,8 @@ impl TlsIntegration {
services.insert( services.insert(
"embedding".to_string(), "embedding".to_string(),
ServiceUrls { ServiceUrls {
original: "http://localhost:8082".to_string(), original: String::new(),
secure: "https://localhost:8445".to_string(), secure: String::new(),
port: 8082, port: 8082,
tls_port: 8445, tls_port: 8445,
}, },

View file

@ -28,8 +28,8 @@ pub struct ZitadelAuthConfig {
impl Default for ZitadelAuthConfig { impl Default for ZitadelAuthConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
issuer_url: "http://localhost:9000".to_string(), issuer_url: "".to_string(),
api_url: "http://localhost:9000".to_string(), api_url: "".to_string(),
client_id: String::new(), client_id: String::new(),
client_secret: String::new(), client_secret: String::new(),
project_id: String::new(), project_id: String::new(),

View file

@ -26,7 +26,7 @@ pub struct TimeSeriesConfig {
impl Default for TimeSeriesConfig { impl Default for TimeSeriesConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
url: "http://localhost:8086".to_string(), url: "".to_string(),
token: String::new(), token: String::new(),
org: "system".to_string(), org: "system".to_string(),
bucket: "metrics".to_string(), bucket: "metrics".to_string(),

View file

@ -11,7 +11,7 @@ impl EmbeddingGenerator {
} }
pub async fn generate_text_embedding(&self, text: &str) -> Result<Vec<f32>> { pub async fn generate_text_embedding(&self, text: &str) -> Result<Vec<f32>> {
let embedding_url = "http://localhost:8082".to_string(); let embedding_url = "".to_string();
match self.generate_local_embedding(text, &embedding_url).await { match self.generate_local_embedding(text, &embedding_url).await {
Ok(embedding) => Ok(embedding), Ok(embedding) => Ok(embedding),
Err(e) => { Err(e) => {

View file

@ -586,7 +586,7 @@ impl VideoEngine {
}; };
let botmodels_url = 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 client = reqwest::Client::new();
let response = client let response = client
@ -691,7 +691,7 @@ impl VideoEngine {
output_dir: &str, output_dir: &str,
) -> Result<TTSResponse, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<TTSResponse, Box<dyn std::error::Error + Send + Sync>> {
let botmodels_url = 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 client = reqwest::Client::new();
let response = client 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 clip = clips.iter().find(|c| c.id == clip_id).ok_or("Clip not found")?;
let botmodels_url = 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 client = reqwest::Client::new();
let response = client 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 clip = clips.iter().find(|c| c.id == req.clip_id).ok_or("Clip not found")?;
let botmodels_url = 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 client = reqwest::Client::new();
let response = client 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 track = tracks.iter().find(|t| t.id == audio_track_id).ok_or("Audio track not found")?;
let botmodels_url = 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 client = reqwest::Client::new();
let response = client 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 track = tracks.iter().find(|t| t.id == audio_track_id).ok_or("Audio track not found")?;
let botmodels_url = 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 client = reqwest::Client::new();
let response = client let response = client

View file

@ -992,26 +992,26 @@ mod tests {
#[test] #[test]
fn test_page_url_patterns() { fn test_page_url_patterns() {
let login = LoginPage::new("http://localhost:4242"); let login = LoginPage::new("");
assert_eq!(LoginPage::url_pattern(), "/login"); 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!(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!(ChatPage::url_pattern(), "/chat/");
assert_eq!(chat.base_url(), "http://localhost:4242"); assert_eq!(chat.base_url(), "");
assert_eq!(chat.bot_name(), "test-bot"); 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!(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!(BotManagementPage::url_pattern(), "/admin/bots");
assert_eq!(bots.base_url(), "http://localhost:4242"); assert_eq!(bots.base_url(), "");
} }
#[test] #[test]