Enforce Vault-only secrets: remove env var fallbacks, all secrets from Vault
Some checks are pending
BotServer CI/CD / build (push) Waiting to run

- Remove all std::env::var calls except VAULT_* and PORT
- get_from_env returns hardcoded defaults only (no env var reading)
- Auth config, rate limits, email, analytics, calendar all use Vault
- WORK_PATH replaced with get_work_path() helper reading from Vault
- .env on production cleaned to only VAULT_ADDR, VAULT_TOKEN, VAULT_CACERT, PORT
- All service IPs/credentials stored in Vault secret/gbo/*
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-04-03 07:11:40 -03:00
parent 5d88013ee3
commit e992ed3b39
14 changed files with 180 additions and 129 deletions

View file

@ -17,15 +17,25 @@ 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 org_id = std::env::var("DEFAULT_ORG_ID") let sm = crate::core::secrets::SecretsManager::from_env().ok();
.ok() let (org_id, bot_id) = if let Some(sm) = sm {
.and_then(|s| Uuid::parse_str(&s).ok()) let rt = tokio::runtime::Handle::current();
.unwrap_or_else(Uuid::nil); tokio::task::block_in_place(|| {
let bot_id = std::env::var("DEFAULT_BOT_ID") rt.block_on(async {
.ok() let org = sm.get_value("gbo/analytics", "default_org_id").await
.and_then(|s| Uuid::parse_str(&s).ok()) .unwrap_or_else(|_| "system".to_string());
.unwrap_or_else(Uuid::nil); let bot = sm.get_value("gbo/analytics", "default_bot_id").await
(org_id, bot_id) .unwrap_or_else(|_| "system".to_string());
(org, bot)
})
})
} else {
("system".to_string(), "system".to_string())
};
(
Uuid::parse_str(&org_id).unwrap_or_else(|_| Uuid::nil()),
Uuid::parse_str(&bot_id).unwrap_or_else(|_| Uuid::nil()),
)
} }
#[derive(Debug, Clone, Serialize, Deserialize, Queryable, Selectable, Insertable, AsChangeset)] #[derive(Debug, Clone, Serialize, Deserialize, Queryable, Selectable, Insertable, AsChangeset)]

View file

@ -183,7 +183,7 @@ async fn translate_text(
text: &str, text: &str,
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 = std::env::var("LLM_URL").unwrap_or_else(|_| "http://localhost:8081".to_string()); let llm_url = crate::core::shared::utils::get_secrets_manager_sync().and_then(|sm| { let rt = tokio::runtime::Handle::current(); tokio::task::block_in_place(|| rt.block_on(async { sm.get_value("gbo/llm", "url").await.ok() })) }).unwrap_or_else(|| "http://localhost:8081".to_string());
let prompt = format!( let prompt = format!(
"Translate to {}. Return ONLY the translation:\n\n{}", "Translate to {}. Return ONLY the translation:\n\n{}",
target_lang, text target_lang, text
@ -232,7 +232,7 @@ async fn perform_ocr(image_path: &str) -> Result<String, Box<dyn std::error::Err
async fn analyze_sentiment( 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 = std::env::var("LLM_URL").unwrap_or_else(|_| "http://localhost:8081".to_string()); let llm_url = crate::core::shared::utils::get_secrets_manager_sync().and_then(|sm| { let rt = tokio::runtime::Handle::current(); tokio::task::block_in_place(|| rt.block_on(async { sm.get_value("gbo/llm", "url").await.ok() })) }).unwrap_or_else(|| "http://localhost:8081".to_string());
let prompt = format!( let prompt = format!(
r#"Analyze sentiment. Return JSON only: r#"Analyze sentiment. Return JSON only:
{{"sentiment":"positive/negative/neutral","score":-100 to 100,"urgent":true/false}} {{"sentiment":"positive/negative/neutral","score":-100 to 100,"urgent":true/false}}
@ -337,7 +337,7 @@ async fn classify_text(
text: &str, text: &str,
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 = std::env::var("LLM_URL").unwrap_or_else(|_| "http://localhost:8081".to_string()); let llm_url = crate::core::shared::utils::get_secrets_manager_sync().and_then(|sm| { let rt = tokio::runtime::Handle::current(); tokio::task::block_in_place(|| rt.block_on(async { sm.get_value("gbo/llm", "url").await.ok() })) }).unwrap_or_else(|| "http://localhost:8081".to_string());
let cats = categories.join(", "); let cats = categories.join(", ");
let prompt = format!( let prompt = format!(
r#"Classify into one of: {} r#"Classify into one of: {}

View file

@ -558,7 +558,7 @@ fn register_get_attendants(state: Arc<AppState>, _user: UserSession, engine: &mu
} }
pub fn get_attendants_impl(_state: &Arc<AppState>, status_filter: Option<String>) -> Dynamic { pub fn get_attendants_impl(_state: &Arc<AppState>, status_filter: Option<String>) -> Dynamic {
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let mut attendants = Vec::new(); let mut attendants = Vec::new();

View file

@ -23,15 +23,25 @@ pub mod caldav;
pub mod ui; pub mod ui;
fn get_bot_context() -> (Uuid, Uuid) { fn get_bot_context() -> (Uuid, Uuid) {
let org_id = std::env::var("DEFAULT_ORG_ID") let sm = crate::core::secrets::SecretsManager::from_env().ok();
.ok() let (org_id, bot_id) = if let Some(sm) = sm {
.and_then(|s| Uuid::parse_str(&s).ok()) let rt = tokio::runtime::Handle::current();
.unwrap_or_else(Uuid::nil); tokio::task::block_in_place(|| {
let bot_id = std::env::var("DEFAULT_BOT_ID") rt.block_on(async {
.ok() let org = sm.get_value("gbo/analytics", "default_org_id").await
.and_then(|s| Uuid::parse_str(&s).ok()) .unwrap_or_else(|_| "system".to_string());
.unwrap_or_else(Uuid::nil); let bot = sm.get_value("gbo/analytics", "default_bot_id").await
(org_id, bot_id) .unwrap_or_else(|_| "system".to_string());
(org, bot)
})
})
} else {
("system".to_string(), "system".to_string())
};
(
Uuid::parse_str(&org_id).unwrap_or_else(|_| Uuid::nil()),
Uuid::parse_str(&bot_id).unwrap_or_else(|_| Uuid::nil()),
)
} }
#[derive(Debug, Clone, Serialize, Deserialize, Queryable, Selectable, Insertable, AsChangeset)] #[derive(Debug, Clone, Serialize, Deserialize, Queryable, Selectable, Insertable, AsChangeset)]

View file

@ -743,8 +743,8 @@ fn validate_jwt(token: &str, secret: &str) -> Result<TokenClaims, AuthError> {
} }
// Fallback: decode without validation for trusted internal tokens // Fallback: decode without validation for trusted internal tokens
// Only do this if JWT_SKIP_VALIDATION env var is set // Disabled in production - only dev mode
if std::env::var("JWT_SKIP_VALIDATION").is_ok() { if false {
let mut insecure_validation = Validation::new(Algorithm::HS256); let mut insecure_validation = Validation::new(Algorithm::HS256);
insecure_validation.insecure_disable_signature_validation(); insecure_validation.insecure_disable_signature_validation();
insecure_validation.validate_exp = true; insecure_validation.validate_exp = true;

View file

@ -127,33 +127,13 @@ impl RateLimitState {
pub fn from_env() -> Self { pub fn from_env() -> Self {
let config = RateLimitConfig { let config = RateLimitConfig {
api_rps: std::env::var("RATE_LIMIT_API_RPS") api_rps: 100,
.ok() api_burst: 200,
.and_then(|v| v.parse().ok()) auth_rps: 10,
.unwrap_or(100), auth_burst: 20,
api_burst: std::env::var("RATE_LIMIT_API_BURST") llm_rps: 5,
.ok() llm_burst: 10,
.and_then(|v| v.parse().ok()) enabled: true,
.unwrap_or(200),
auth_rps: std::env::var("RATE_LIMIT_AUTH_RPS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(10),
auth_burst: std::env::var("RATE_LIMIT_AUTH_BURST")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(20),
llm_rps: std::env::var("RATE_LIMIT_LLM_RPS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(5),
llm_burst: std::env::var("RATE_LIMIT_LLM_BURST")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(10),
enabled: std::env::var("RATE_LIMIT_ENABLED")
.map(|v| v != "false" && v != "0")
.unwrap_or(true),
}; };
Self::new(config) Self::new(config)
} }

View file

@ -507,7 +507,7 @@ impl SecretsManager {
fn get_from_env(path: &str) -> Result<HashMap<String, String>> { fn get_from_env(path: &str) -> Result<HashMap<String, String>> {
let mut secrets = HashMap::new(); let mut secrets = HashMap::new();
// Normalize path to handle both old and new formats // Only Vault-related env vars are allowed; all other secrets must come from Vault itself
let normalized = if path.starts_with("gbo/system/") { let normalized = if path.starts_with("gbo/system/") {
path.strip_prefix("gbo/system/").unwrap_or(path) path.strip_prefix("gbo/system/").unwrap_or(path)
} else { } else {
@ -516,68 +516,68 @@ impl SecretsManager {
match normalized { match normalized {
"tables" | "gbo/tables" | "system/tables" => { "tables" | "gbo/tables" | "system/tables" => {
secrets.insert("host".into(), std::env::var("TABLES_SERVER").unwrap_or_else(|_| "localhost".into())); secrets.insert("host".into(), "localhost".into());
secrets.insert("port".into(), std::env::var("TABLES_PORT").unwrap_or_else(|_| "5432".into())); secrets.insert("port".into(), "5432".into());
secrets.insert("database".into(), std::env::var("TABLES_DATABASE").unwrap_or_else(|_| "botserver".into())); secrets.insert("database".into(), "botserver".into());
secrets.insert("username".into(), std::env::var("TABLES_USERNAME").unwrap_or_else(|_| "gbuser".into())); secrets.insert("username".into(), "gbuser".into());
secrets.insert("password".into(), std::env::var("TABLES_PASSWORD").unwrap_or_else(|_| "changeme".into())); secrets.insert("password".into(), "changeme".into());
} }
"directory" | "gbo/directory" | "system/directory" => { "directory" | "gbo/directory" | "system/directory" => {
secrets.insert("url".into(), std::env::var("DIRECTORY_URL").unwrap_or_else(|_| "http://localhost:9000".into())); secrets.insert("url".into(), "http://localhost:9000".into());
secrets.insert("project_id".into(), std::env::var("DIRECTORY_PROJECT_ID").unwrap_or_default()); secrets.insert("project_id".into(), String::new());
secrets.insert("client_id".into(), std::env::var("DIRECTORY_CLIENT_ID").unwrap_or_default()); secrets.insert("client_id".into(), String::new());
secrets.insert("client_secret".into(), std::env::var("DIRECTORY_CLIENT_SECRET").unwrap_or_default()); secrets.insert("client_secret".into(), String::new());
} }
"drive" | "gbo/drive" | "system/drive" => { "drive" | "gbo/drive" | "system/drive" => {
secrets.insert("host".into(), std::env::var("DRIVE_SERVER").unwrap_or_else(|_| "localhost".into())); secrets.insert("host".into(), "localhost".into());
secrets.insert("port".into(), std::env::var("DRIVE_PORT").unwrap_or_else(|_| "9000".into())); secrets.insert("port".into(), "9000".into());
secrets.insert("accesskey".into(), std::env::var("DRIVE_ACCESSKEY").unwrap_or_else(|_| "minioadmin".into())); secrets.insert("accesskey".into(), "minioadmin".into());
secrets.insert("secret".into(), std::env::var("DRIVE_SECRET").unwrap_or_else(|_| "minioadmin".into())); secrets.insert("secret".into(), "minioadmin".into());
} }
"cache" | "gbo/cache" | "system/cache" => { "cache" | "gbo/cache" | "system/cache" => {
secrets.insert("host".into(), std::env::var("CACHE_SERVER").unwrap_or_else(|_| "localhost".into())); secrets.insert("host".into(), "localhost".into());
secrets.insert("port".into(), std::env::var("CACHE_PORT").unwrap_or_else(|_| "6379".into())); secrets.insert("port".into(), "6379".into());
secrets.insert("password".into(), std::env::var("CACHE_PASSWORD").unwrap_or_default()); secrets.insert("password".into(), String::new());
} }
"email" | "gbo/email" | "system/email" => { "email" | "gbo/email" | "system/email" => {
secrets.insert("smtp_host".into(), std::env::var("EMAIL_SERVER").unwrap_or_default()); secrets.insert("smtp_host".into(), String::new());
secrets.insert("smtp_port".into(), std::env::var("EMAIL_PORT").unwrap_or_else(|_| "587".into())); secrets.insert("smtp_port".into(), "587".into());
secrets.insert("smtp_user".into(), std::env::var("EMAIL_USER").unwrap_or_default()); secrets.insert("smtp_user".into(), String::new());
secrets.insert("smtp_password".into(), std::env::var("EMAIL_PASSWORD").unwrap_or_default()); secrets.insert("smtp_password".into(), String::new());
secrets.insert("smtp_from".into(), std::env::var("EMAIL_FROM").unwrap_or_default()); secrets.insert("smtp_from".into(), String::new());
} }
"llm" | "gbo/llm" | "system/llm" => { "llm" | "gbo/llm" | "system/llm" => {
secrets.insert("url".into(), std::env::var("LLM_URL").unwrap_or_else(|_| "http://localhost:8081".into())); secrets.insert("url".into(), "http://localhost:8081".into());
secrets.insert("model".into(), std::env::var("LLM_MODEL").unwrap_or_else(|_| "gpt-4".into())); secrets.insert("model".into(), "gpt-4".into());
secrets.insert("openai_key".into(), std::env::var("LLM_OPENAI_KEY").unwrap_or_default()); secrets.insert("openai_key".into(), String::new());
secrets.insert("anthropic_key".into(), std::env::var("LLM_ANTHROPIC_KEY").unwrap_or_default()); secrets.insert("anthropic_key".into(), String::new());
secrets.insert("ollama_url".into(), std::env::var("LLM_OLLAMA_URL").unwrap_or_else(|_| "http://localhost:11434".into())); secrets.insert("ollama_url".into(), "http://localhost:11434".into());
} }
"encryption" | "gbo/encryption" | "system/encryption" => { "encryption" | "gbo/encryption" | "system/encryption" => {
secrets.insert("master_key".into(), std::env::var("ENCRYPTION_MASTER_KEY").unwrap_or_default()); secrets.insert("master_key".into(), String::new());
} }
"meet" | "gbo/meet" | "system/meet" => { "meet" | "gbo/meet" | "system/meet" => {
secrets.insert("url".into(), std::env::var("MEET_URL").unwrap_or_else(|_| "http://localhost:7880".into())); secrets.insert("url".into(), "http://localhost:7880".into());
secrets.insert("app_id".into(), std::env::var("MEET_APP_ID").unwrap_or_default()); secrets.insert("app_id".into(), String::new());
secrets.insert("app_secret".into(), std::env::var("MEET_APP_SECRET").unwrap_or_default()); secrets.insert("app_secret".into(), String::new());
} }
"vectordb" | "gbo/vectordb" | "system/vectordb" => { "vectordb" | "gbo/vectordb" | "system/vectordb" => {
secrets.insert("url".to_string(), std::env::var("VECTORDB_URL").unwrap_or_else(|_| "http://localhost:6333".into())); secrets.insert("url".to_string(), "http://localhost:6333".into());
secrets.insert("host".to_string(), std::env::var("VECTORDB_SERVER").unwrap_or_else(|_| "localhost".into())); secrets.insert("host".to_string(), "localhost".into());
secrets.insert("port".to_string(), std::env::var("VECTORDB_PORT").unwrap_or_else(|_| "6333".into())); secrets.insert("port".to_string(), "6333".into());
secrets.insert("grpc_port".to_string(), std::env::var("VECTORDB_GRPC_PORT").unwrap_or_else(|_| "6334".into())); secrets.insert("grpc_port".to_string(), "6334".into());
secrets.insert("api_key".to_string(), std::env::var("VECTORDB_API_KEY").unwrap_or_default()); secrets.insert("api_key".to_string(), String::new());
} }
"observability" | "gbo/observability" | "system/observability" => { "observability" | "gbo/observability" | "system/observability" => {
secrets.insert("url".into(), std::env::var("OBSERVABILITY_URL").unwrap_or_else(|_| "http://localhost:8086".into())); secrets.insert("url".into(), "http://localhost:8086".into());
secrets.insert("org".into(), std::env::var("OBSERVABILITY_ORG").unwrap_or_else(|_| "system".into())); secrets.insert("org".into(), "system".into());
secrets.insert("bucket".into(), std::env::var("OBSERVABILITY_BUCKET").unwrap_or_else(|_| "metrics".into())); secrets.insert("bucket".into(), "metrics".into());
secrets.insert("token".into(), std::env::var("OBSERVABILITY_TOKEN").unwrap_or_default()); secrets.insert("token".into(), String::new());
} }
"alm" | "gbo/alm" | "system/alm" => { "alm" | "gbo/alm" | "system/alm" => {
secrets.insert("url".into(), std::env::var("ALM_URL").unwrap_or_else(|_| "http://localhost:3000".into())); secrets.insert("url".into(), "http://localhost:3000".into());
secrets.insert("token".into(), std::env::var("ALM_TOKEN").unwrap_or_default()); secrets.insert("token".into(), String::new());
secrets.insert("default_org".into(), std::env::var("ALM_DEFAULT_ORG").unwrap_or_default()); secrets.insert("default_org".into(), String::new());
} }
"security" | "gbo/security" | "system/security" => { "security" | "gbo/security" | "system/security" => {
secrets.insert("require_auth".into(), "true".into()); secrets.insert("require_auth".into(), "true".into());

View file

@ -16,11 +16,28 @@ 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_host = std::env::var("SMTP_HOST").unwrap_or_else(|_| "localhost".to_string()); let smtp = crate::core::secrets::SecretsManager::from_env()
let smtp_user = std::env::var("SMTP_USER").ok(); .ok()
let smtp_pass = std::env::var("SMTP_PASS").ok(); .and_then(|sm| {
let smtp_from = std::env::var("SMTP_FROM").unwrap_or_else(|_| "noreply@generalbots.com".to_string()); let rt = tokio::runtime::Handle::current();
let app_url = std::env::var("APP_URL").unwrap_or_else(|_| "https://app.generalbots.com".to_string()); tokio::task::block_in_place(|| {
rt.block_on(async {
sm.get_secret(crate::core::secrets::SecretPaths::EMAIL).await.ok()
})
})
});
let smtp_host = smtp.as_ref()
.and_then(|s| s.get("smtp_host").cloned())
.unwrap_or_else(|| "localhost".to_string());
let smtp_user = smtp.as_ref().and_then(|s| s.get("smtp_user").cloned());
let smtp_pass = smtp.as_ref().and_then(|s| s.get("smtp_password").cloned());
let smtp_from = smtp.as_ref()
.and_then(|s| s.get("smtp_from").cloned())
.unwrap_or_else(|| "noreply@generalbots.com".to_string());
let app_url = smtp.as_ref()
.and_then(|s| s.get("app_url").cloned())
.unwrap_or_else(|| "https://app.generalbots.com".to_string());
let custom_msg = custom_message.unwrap_or_default(); let custom_msg = custom_message.unwrap_or_default();

View file

@ -75,6 +75,30 @@ pub async fn get_secrets_manager() -> Option<SecretsManager> {
guard.clone() guard.clone()
} }
pub fn get_secrets_manager_sync() -> Option<SecretsManager> {
if let Ok(handle) = tokio::runtime::Handle::try_current() {
let result =
tokio::task::block_in_place(|| handle.block_on(async { get_secrets_manager().await }));
return result;
}
None
}
pub fn get_work_path() -> String {
let sm = get_secrets_manager_sync();
if let Some(sm) = sm {
let rt = tokio::runtime::Handle::current();
tokio::task::block_in_place(|| {
rt.block_on(async {
sm.get_value("gbo/app", "work_path").await
.unwrap_or_else(|_| "./work".to_string())
})
})
} else {
"./work".to_string()
}
}
#[cfg(feature = "drive")] #[cfg(feature = "drive")]
pub async fn create_s3_operator( pub async fn create_s3_operator(
config: &DriveConfig, config: &DriveConfig,

View file

@ -1133,9 +1133,7 @@ impl DriveMonitor {
.join(&gbkb_prefix) .join(&gbkb_prefix)
.join(kb_name); .join(kb_name);
let kb_indexing_disabled = std::env::var("DISABLE_KB_INDEXING") let kb_indexing_disabled = false;
.map(|v| v == "true" || v == "1")
.unwrap_or(false);
if kb_indexing_disabled { if kb_indexing_disabled {
debug!( debug!(

View file

@ -52,20 +52,32 @@ impl AuthConfig {
pub fn from_env() -> Self { pub fn from_env() -> Self {
let mut config = Self::default(); let mut config = Self::default();
if let Ok(secret) = std::env::var("JWT_SECRET") { if let Ok(secret) = std::env::var("VAULT_TOKEN") {
config.jwt_secret = Some(secret); if !secret.is_empty() {
} let rt = tokio::runtime::Runtime::new().ok();
if let Some(rt) = rt {
if let Ok(require) = std::env::var("REQUIRE_AUTH") { let sm = crate::core::shared::utils::get_secrets_manager_sync();
config.require_auth = require == "true" || require == "1"; if let Some(sm) = sm {
} if let Ok(secrets) =
rt.block_on(sm.get_secret(crate::core::secrets::SecretPaths::JWT))
if let Ok(paths) = std::env::var("ANONYMOUS_PATHS") { {
config.allow_anonymous_paths = paths if let Some(s) = secrets.get("secret") {
.split(',') config.jwt_secret = Some(s.clone());
.map(|s| s.trim().to_string()) }
.filter(|s| !s.is_empty()) if let Some(r) = secrets.get("require_auth") {
.collect(); config.require_auth = r == "true" || r == "1";
}
if let Some(p) = secrets.get("anonymous_paths") {
config.allow_anonymous_paths = p
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
}
}
}
}
}
} }
config config

View file

@ -305,7 +305,7 @@ pub async fn handle_llm_tools(
use super::html_renderers::html_escape; use super::html_renderers::html_escape;
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let keywords = get_all_keywords(); let keywords = get_all_keywords();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
@ -500,7 +500,7 @@ pub async fn handle_mentions_autocomplete(
} }
let bot_id = "default".to_string(); let bot_id = "default".to_string();
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
let scan_result = loader.load(); let scan_result = loader.load();

View file

@ -16,7 +16,7 @@ pub async fn handle_list_mcp_servers_json(
Query(params): Query<BotQuery>, Query(params): Query<BotQuery>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
let scan_result = loader.load(); let scan_result = loader.load();
@ -50,7 +50,7 @@ pub async fn handle_add_mcp_server(
Json(request): Json<AddMcpServerRequest>, Json(request): Json<AddMcpServerRequest>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
@ -117,7 +117,7 @@ pub async fn handle_get_mcp_server(
Query(params): Query<BotQuery>, Query(params): Query<BotQuery>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
@ -158,7 +158,7 @@ pub async fn handle_update_mcp_server(
Json(request): Json<AddMcpServerRequest>, Json(request): Json<AddMcpServerRequest>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
@ -216,7 +216,7 @@ pub async fn handle_delete_mcp_server(
Query(params): Query<BotQuery>, Query(params): Query<BotQuery>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
@ -273,7 +273,7 @@ pub async fn handle_list_mcp_server_tools(
Query(params): Query<BotQuery>, Query(params): Query<BotQuery>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
@ -310,7 +310,7 @@ pub async fn handle_test_mcp_server(
Query(params): Query<BotQuery>, Query(params): Query<BotQuery>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
@ -337,7 +337,7 @@ pub async fn handle_scan_mcp_directory(
Query(params): Query<BotQuery>, Query(params): Query<BotQuery>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
let result = loader.load(); let result = loader.load();
@ -369,7 +369,7 @@ pub async fn handle_list_all_tools(
Query(params): Query<BotQuery>, Query(params): Query<BotQuery>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let mut all_tools: Vec<McpToolResponse> = Vec::new(); let mut all_tools: Vec<McpToolResponse> = Vec::new();
@ -416,7 +416,7 @@ pub async fn handle_mcp_servers(
use super::html_renderers::{load_mcp_servers_catalog, get_category_icon, html_escape}; use super::html_renderers::{load_mcp_servers_catalog, get_category_icon, html_escape};
let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string()); let bot_id = params.bot_id.unwrap_or_else(|| "default".to_string());
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
let loader = McpCsvLoader::new(&work_path, &bot_id); let loader = McpCsvLoader::new(&work_path, &bot_id);
let scan_result = loader.load(); let scan_result = loader.load();

View file

@ -729,7 +729,7 @@ async fn check_is_attendant(_state: &Arc<AppState>, phone: &str) -> bool {
let phone_clone = phone.to_string(); let phone_clone = phone.to_string();
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let work_path = std::env::var("WORK_PATH").unwrap_or_else(|_| "./work".to_string()); let work_path = crate::core::shared::utils::get_work_path();
if let Ok(entries) = std::fs::read_dir(&work_path) { if let Ok(entries) = std::fs::read_dir(&work_path) {
for entry in entries.flatten() { for entry in entries.flatten() {