merge: Unify master into main - all commits unified
Some checks failed
BotServer CI / build (push) Failing after 6m9s

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-03-01 07:43:07 -03:00
commit 2c92a81302
16 changed files with 167 additions and 102 deletions

View file

@ -620,7 +620,7 @@ pub async fn create_objective(
let record = new_objective.clone();
let _result = tokio::task::spawn_blocking(move || {
tokio::task::spawn_blocking(move || {
let mut conn = pool.get().map_err(|e| GoalsError::Database(e.to_string()))?;
diesel::insert_into(okr_objectives::table)
.values(&new_objective)
@ -718,7 +718,7 @@ pub async fn delete_objective(
) -> Result<Json<serde_json::Value>, GoalsError> {
let pool = state.conn.clone();
let _result = tokio::task::spawn_blocking(move || {
tokio::task::spawn_blocking(move || {
let mut conn = pool.get().map_err(|e| GoalsError::Database(e.to_string()))?;
let deleted = diesel::delete(okr_objectives::table.find(objective_id))
.execute(&mut conn)
@ -793,7 +793,7 @@ pub async fn create_key_result(
let record = new_kr.clone();
let _result = tokio::task::spawn_blocking(move || {
tokio::task::spawn_blocking(move || {
let mut conn = pool.get().map_err(|e| GoalsError::Database(e.to_string()))?;
diesel::insert_into(okr_key_results::table)
.values(&new_kr)

View file

@ -884,8 +884,6 @@ END SCHEDULE
.clone()
.unwrap_or_else(|| "unspecified".to_string());
let _goal_id = Uuid::new_v4();
// Goals are more complex - they create a monitoring + action loop
let basic_code = format!(
r#"' Goal: {goal_name}

View file

@ -16,11 +16,6 @@ use std::path::PathBuf;
#[cfg(feature = "llm")]
use std::sync::Arc;
// When llm feature is disabled, create a dummy trait for type compatibility
#[cfg(not(feature = "llm"))]
#[allow(dead_code)]
trait LLMProvider: Send + Sync {}
pub fn create_site_keyword(state: &AppState, user: UserSession, engine: &mut Engine) {
let state_clone = state.clone();
let user_clone = user;

View file

@ -1,7 +1,6 @@
use log::info;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::path::Path;
use uuid::Uuid;
use crate::core::shared::state::AppState;
@ -174,9 +173,12 @@ pub fn fetch_folder_changes(
Ok(events)
}
#[allow(dead_code)]
#[cfg(test)]
mod tests {
use super::*;
fn apply_filters(events: Vec<FolderChangeEvent>, filters: &Option<FileFilters>) -> Vec<FolderChangeEvent> {
let Some(filters) = filters else {
let Some(ref filters) = filters else {
return events;
};
@ -219,9 +221,6 @@ fn apply_filters(events: Vec<FolderChangeEvent>, filters: &Option<FileFilters>)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]

View file

@ -139,18 +139,18 @@ pub struct CodeScanner {
}
impl CodeScanner {
pub fn new(base_path: impl AsRef<Path>) -> Self {
let patterns = Self::build_patterns();
Self {
pub fn new(base_path: impl AsRef<Path>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let patterns = Self::build_patterns()?;
Ok(Self {
patterns,
base_path: base_path.as_ref().to_path_buf(),
}
})
}
fn build_patterns() -> Vec<ScanPattern> {
vec![
fn build_patterns() -> Result<Vec<ScanPattern>, Box<dyn std::error::Error + Send + Sync>> {
Ok(vec![
ScanPattern {
regex: Regex::new(r#"(?i)password\s*=\s*["'][^"']+["']"#).expect("valid regex"),
regex: Regex::new(r#"(?i)password\s*=\s*["'][^"']+["']"#)?,
issue_type: IssueType::PasswordInConfig,
severity: IssueSeverity::Critical,
title: "Hardcoded Password".to_string(),
@ -159,7 +159,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r#"(?i)(api[_-]?key|apikey|secret[_-]?key|client[_-]?secret)\s*=\s*["'][^"']{8,}["']"#).expect("valid regex"),
regex: Regex::new(r#"(?i)(api[_-]?key|apikey|secret[_-]?key|client[_-]?secret)\s*=\s*["'][^"']{8,}["']"#)?,
issue_type: IssueType::HardcodedSecret,
severity: IssueSeverity::Critical,
title: "Hardcoded API Key/Secret".to_string(),
@ -168,7 +168,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r#"(?i)token\s*=\s*["'][a-zA-Z0-9_\-]{20,}["']"#).expect("valid regex"),
regex: Regex::new(r#"(?i)token\s*=\s*["'][a-zA-Z0-9_\-]{20,}["']"#)?,
issue_type: IssueType::HardcodedSecret,
severity: IssueSeverity::High,
title: "Hardcoded Token".to_string(),
@ -177,7 +177,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r"(?i)IF\s+.*\binput\b").expect("valid regex"),
regex: Regex::new(r"(?i)IF\s+.*\binput\b")?,
issue_type: IssueType::DeprecatedIfInput,
severity: IssueSeverity::Medium,
title: "Deprecated IF...input Pattern".to_string(),
@ -189,7 +189,7 @@ impl CodeScanner {
category: "Code Quality".to_string(),
},
ScanPattern {
regex: Regex::new(r"(?i)\b(GET_BOT_MEMORY|SET_BOT_MEMORY|GET_USER_MEMORY|SET_USER_MEMORY|USE_KB|USE_TOOL|SEND_MAIL|CREATE_TASK)\b").expect("valid regex"),
regex: Regex::new(r"(?i)\b(GET_BOT_MEMORY|SET_BOT_MEMORY|GET_USER_MEMORY|SET_USER_MEMORY|USE_KB|USE_TOOL|SEND_MAIL|CREATE_TASK)\b")?,
issue_type: IssueType::UnderscoreInKeyword,
severity: IssueSeverity::Low,
title: "Underscore in Keyword".to_string(),
@ -198,7 +198,7 @@ impl CodeScanner {
category: "Naming Convention".to_string(),
},
ScanPattern {
regex: Regex::new(r"(?i)POST\s+TO\s+INSTAGRAM\s+\w+\s*,\s*\w+").expect("valid regex"),
regex: Regex::new(r"(?i)POST\s+TO\s+INSTAGRAM\s+\w+\s*,\s*\w+")?,
issue_type: IssueType::InsecurePattern,
severity: IssueSeverity::High,
title: "Instagram Credentials in Code".to_string(),
@ -209,7 +209,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r"(?i)(SELECT|INSERT|UPDATE|DELETE)\s+.*(FROM|INTO|SET)\s+").expect("valid regex"),
regex: Regex::new(r"(?i)(SELECT|INSERT|UPDATE|DELETE)\s+.*(FROM|INTO|SET)\s+")?,
issue_type: IssueType::FragileCode,
severity: IssueSeverity::Medium,
title: "Raw SQL Query".to_string(),
@ -221,7 +221,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r"(?i)\bEVAL\s*\(").expect("valid regex"),
regex: Regex::new(r"(?i)\bEVAL\s*\(")?,
issue_type: IssueType::FragileCode,
severity: IssueSeverity::High,
title: "Dynamic Code Execution".to_string(),
@ -233,7 +233,7 @@ impl CodeScanner {
regex: Regex::new(
r#"(?i)(password|secret|key|token)\s*=\s*["'][A-Za-z0-9+/=]{40,}["']"#,
)
.expect("valid regex"),
?,
issue_type: IssueType::HardcodedSecret,
severity: IssueSeverity::High,
title: "Potential Encoded Secret".to_string(),
@ -243,7 +243,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r"(?i)(AKIA[0-9A-Z]{16})").expect("valid regex"),
regex: Regex::new(r"(?i)(AKIA[0-9A-Z]{16})")?,
issue_type: IssueType::HardcodedSecret,
severity: IssueSeverity::Critical,
title: "AWS Access Key".to_string(),
@ -253,7 +253,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r"-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----").expect("valid regex"),
regex: Regex::new(r"-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----")?,
issue_type: IssueType::HardcodedSecret,
severity: IssueSeverity::Critical,
title: "Private Key in Code".to_string(),
@ -263,7 +263,7 @@ impl CodeScanner {
category: "Security".to_string(),
},
ScanPattern {
regex: Regex::new(r"(?i)(postgres|mysql|mongodb|redis)://[^:]+:[^@]+@").expect("valid regex"),
regex: Regex::new(r"(?i)(postgres|mysql|mongodb|redis)://[^:]+:[^@]+@")?,
issue_type: IssueType::HardcodedSecret,
severity: IssueSeverity::Critical,
title: "Database Credentials in Connection String".to_string(),
@ -450,12 +450,12 @@ impl CodeScanner {
fn redact_sensitive(line: &str) -> String {
let mut result = line.to_string();
let secret_pattern = Regex::new(r#"(["'])[^"']{8,}(["'])"#).expect("valid regex");
let secret_pattern = Regex::new(r#"(["'])[^"']{8,}(["'])"#)?;
result = secret_pattern
.replace_all(&result, "$1***REDACTED***$2")
.to_string();
let aws_pattern = Regex::new(r"AKIA[0-9A-Z]{16}").expect("valid regex");
let aws_pattern = Regex::new(r"AKIA[0-9A-Z]{16}")?;
result = aws_pattern
.replace_all(&result, "AKIA***REDACTED***")
.to_string();

View file

@ -24,7 +24,9 @@ struct ThirdPartyConfig {
static THIRDPARTY_CONFIG: Lazy<ThirdPartyConfig> = Lazy::new(|| {
let toml_str = include_str!("../../../3rdparty.toml");
toml::from_str(toml_str).expect("Failed to parse embedded 3rdparty.toml")
toml::from_str(toml_str).unwrap_or_else(|e| {
panic!("Failed to parse embedded 3rdparty.toml: {e}")
})
});
fn get_component_url(name: &str) -> Option<String> {

View file

@ -474,12 +474,12 @@ impl DirectorySetup {
.bearer_auth(&access_token)
.json(&json!({
"name": app_name,
"redirectUris": [redirect_uri, "http://localhost:3000/auth/callback", "http://localhost:8080/auth/callback"],
"redirectUris": [redirect_uri, "http://localhost:3000/auth/callback", "http://localhost:8080/auth/callback", "http://localhost:9000/auth/callback"],
"responseTypes": ["OIDC_RESPONSE_TYPE_CODE"],
"grantTypes": ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN", "OIDC_GRANT_TYPE_PASSWORD"],
"appType": "OIDC_APP_TYPE_WEB",
"authMethodType": "OIDC_AUTH_METHOD_TYPE_POST",
"postLogoutRedirectUris": ["http://localhost:8080", "http://localhost:3000", "http://localhost:8080"],
"postLogoutRedirectUris": ["http://localhost:8080", "http://localhost:3000", "http://localhost:9000"],
"accessTokenType": "OIDC_TOKEN_TYPE_BEARER",
"devMode": true,
}))

View file

@ -20,6 +20,7 @@ impl SecretPaths {
pub const EMAIL: &'static str = "gbo/email";
pub const LLM: &'static str = "gbo/llm";
pub const ENCRYPTION: &'static str = "gbo/encryption";
pub const JWT: &'static str = "gbo/jwt";
pub const MEET: &'static str = "gbo/meet";
pub const ALM: &'static str = "gbo/alm";
pub const VECTORDB: &'static str = "gbo/vectordb";
@ -270,6 +271,10 @@ impl SecretsManager {
self.get_value(SecretPaths::ENCRYPTION, "master_key").await
}
pub async fn get_jwt_secret(&self) -> Result<String> {
self.get_value(SecretPaths::JWT, "secret").await
}
pub async fn put_secret(&self, path: &str, data: HashMap<String, String>) -> Result<()> {
let client = self
.client

View file

@ -1,3 +1,4 @@
use serde::Serialize;
use std::path::Path;
use std::fs;
@ -83,7 +84,7 @@ impl BasFileAnalyzer {
}
}
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, Serialize)]
pub struct WorkflowMetadata {
pub name: String,
pub step_count: usize,

View file

@ -406,7 +406,7 @@ pub async fn get_current_user(
is_anonymous: true,
})
}
Some(token) if token.is_empty() => {
Some("") => {
info!("get_current_user: empty authorization token - returning anonymous user");
Json(CurrentUserResponse {
id: None,

View file

@ -23,7 +23,6 @@ use serde::{Deserialize, Serialize};
use tokio::fs as tokio_fs;
#[cfg(any(feature = "research", feature = "llm"))]
#[allow(dead_code)]
const KB_INDEXING_TIMEOUT_SECS: u64 = 60;
const MAX_BACKOFF_SECS: u64 = 300;
const INITIAL_BACKOFF_SECS: u64 = 30;
@ -43,7 +42,6 @@ pub struct DriveMonitor {
is_processing: Arc<AtomicBool>,
consecutive_failures: Arc<AtomicU32>,
#[cfg(any(feature = "research", feature = "llm"))]
#[allow(dead_code)]
kb_indexing_in_progress: Arc<TokioRwLock<HashSet<String>>>,
}
impl DriveMonitor {

View file

@ -263,7 +263,23 @@ impl SafeCommand {
}
cmd.env_clear();
cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin");
// Build PATH with standard locations plus botserver-stack/bin/shared
let mut path_entries = vec![
"/usr/local/bin".to_string(),
"/usr/bin".to_string(),
"/bin".to_string(),
];
// Add botserver-stack/bin/shared to PATH if it exists
let stack_path = std::env::var("BOTSERVER_STACK_PATH")
.unwrap_or_else(|_| "./botserver-stack".to_string());
let shared_bin = format!("{}/bin/shared", stack_path);
if std::path::Path::new(&shared_bin).exists() {
path_entries.insert(0, shared_bin);
}
cmd.env("PATH", path_entries.join(":"));
cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp")));
cmd.env("LANG", "C.UTF-8");
@ -284,7 +300,23 @@ impl SafeCommand {
}
cmd.env_clear();
cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin");
// Build PATH with standard locations plus botserver-stack/bin/shared
let mut path_entries = vec![
"/usr/local/bin".to_string(),
"/usr/bin".to_string(),
"/bin".to_string(),
];
// Add botserver-stack/bin/shared to PATH if it exists
let stack_path = std::env::var("BOTSERVER_STACK_PATH")
.unwrap_or_else(|_| "./botserver-stack".to_string());
let shared_bin = format!("{}/bin/shared", stack_path);
if std::path::Path::new(&shared_bin).exists() {
path_entries.insert(0, shared_bin);
}
cmd.env("PATH", path_entries.join(":"));
cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp")));
cmd.env("LANG", "C.UTF-8");
@ -305,7 +337,23 @@ impl SafeCommand {
}
cmd.env_clear();
cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin");
// Build PATH with standard locations plus botserver-stack/bin/shared
let mut path_entries = vec![
"/usr/local/bin".to_string(),
"/usr/bin".to_string(),
"/bin".to_string(),
];
// Add botserver-stack/bin/shared to PATH if it exists
let stack_path = std::env::var("BOTSERVER_STACK_PATH")
.unwrap_or_else(|_| "./botserver-stack".to_string());
let shared_bin = format!("{}/bin/shared", stack_path);
if std::path::Path::new(&shared_bin).exists() {
path_entries.insert(0, shared_bin);
}
cmd.env("PATH", path_entries.join(":"));
cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp")));
cmd.env("LANG", "C.UTF-8");
@ -326,7 +374,23 @@ impl SafeCommand {
}
cmd.env_clear();
cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin");
// Build PATH with standard locations plus botserver-stack/bin/shared
let mut path_entries = vec![
"/usr/local/bin".to_string(),
"/usr/bin".to_string(),
"/bin".to_string(),
];
// Add botserver-stack/bin/shared to PATH if it exists
let stack_path = std::env::var("BOTSERVER_STACK_PATH")
.unwrap_or_else(|_| "./botserver-stack".to_string());
let shared_bin = format!("{}/bin/shared", stack_path);
if std::path::Path::new(&shared_bin).exists() {
path_entries.insert(0, shared_bin);
}
cmd.env("PATH", path_entries.join(":"));
cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp")));
cmd.env("LANG", "C.UTF-8");

View file

@ -337,8 +337,6 @@ mod tests {
fn test_catch_panic_failure() {
let result = catch_panic(|| {
panic!("test panic");
#[allow(unreachable_code)]
42
});
assert!(result.is_err());
assert!(result.unwrap_err().message.contains("test panic"));

View file

@ -1,4 +1,3 @@
#![cfg_attr(feature = "mail", allow(unused_imports))]
pub mod audit_log;
pub mod menu_config;
pub mod permission_inheritance;
@ -147,13 +146,10 @@ r##"<div class="connections-empty">
</div>"## .to_string(), ) }
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct SearchSettingsRequest {
enable_fuzzy_search: Option<bool>,
search_result_limit: Option<i32>,
enable_ai_suggestions: Option<bool>,
index_attachments: Option<bool>,
search_sources: Option<Vec<String>>,
}
#[derive(Debug, Serialize)]
@ -183,8 +179,15 @@ Json(SearchSettingsResponse {
}
#[derive(Debug, Serialize)]
struct SmtpTestResponse {
success: bool,
message: Option<String>,
error: Option<String>,
}
#[cfg(feature = "mail")]
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct SmtpTestRequest {
host: String,
port: i32,
@ -193,11 +196,14 @@ password: Option<String>,
use_tls: Option<bool>,
}
#[derive(Debug, Serialize)]
struct SmtpTestResponse {
success: bool,
message: Option<String>,
error: Option<String>,
#[cfg(not(feature = "mail"))]
#[derive(Debug, Deserialize)]
struct SmtpTestRequest {
_host: String,
_port: i32,
_username: Option<String>,
_password: Option<String>,
_use_tls: Option<bool>,
}
#[cfg(feature = "mail")]

View file

@ -344,7 +344,6 @@ async fn process_attendant_command(
}
async fn check_is_attendant(state: &Arc<AppState>, phone: &str) -> bool {
let _conn = state.conn.clone();
let phone_clone = phone.to_string();
tokio::task::spawn_blocking(move || {