merge: Unify master into main - all commits unified
Some checks failed
BotServer CI / build (push) Failing after 6m9s
Some checks failed
BotServer CI / build (push) Failing after 6m9s
This commit is contained in:
commit
2c92a81302
16 changed files with 167 additions and 102 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,56 +173,56 @@ pub fn fetch_folder_changes(
|
|||
Ok(events)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn apply_filters(events: Vec<FolderChangeEvent>, filters: &Option<FileFilters>) -> Vec<FolderChangeEvent> {
|
||||
let Some(filters) = filters else {
|
||||
return events;
|
||||
};
|
||||
|
||||
events
|
||||
.into_iter()
|
||||
.filter(|event| {
|
||||
if let Some(ref extensions) = filters.extensions {
|
||||
let ext = Path::new(&event.path)
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("");
|
||||
if !extensions.iter().any(|e| e.eq_ignore_ascii_case(ext)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(min_size) = filters.min_size {
|
||||
if event.size.unwrap_or(0) < min_size {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(max_size) = filters.max_size {
|
||||
if event.size.unwrap_or(i64::MAX) > max_size {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref pattern) = filters.name_pattern {
|
||||
let file_name = Path::new(&event.path)
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("");
|
||||
if !file_name.contains(pattern) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn apply_filters(events: Vec<FolderChangeEvent>, filters: &Option<FileFilters>) -> Vec<FolderChangeEvent> {
|
||||
let Some(ref filters) = filters else {
|
||||
return events;
|
||||
};
|
||||
|
||||
events
|
||||
.into_iter()
|
||||
.filter(|event| {
|
||||
if let Some(ref extensions) = filters.extensions {
|
||||
let ext = Path::new(&event.path)
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("");
|
||||
if !extensions.iter().any(|e| e.eq_ignore_ascii_case(ext)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(min_size) = filters.min_size {
|
||||
if event.size.unwrap_or(0) < min_size {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(max_size) = filters.max_size {
|
||||
if event.size.unwrap_or(i64::MAX) > max_size {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref pattern) = filters.name_pattern {
|
||||
let file_name = Path::new(&event.path)
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("");
|
||||
if !file_name.contains(pattern) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_folder_provider_from_str() {
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ pub async fn workflow_designer_page(
|
|||
content = '<strong>Parallel</strong><br>Multiple branches';
|
||||
break;
|
||||
case 'event':
|
||||
content = '<strong>Event</strong><br><input type="text" placeholder="Event Name " style="width:100px;margin:2px;">';
|
||||
content = '<strong>Event</strong><br><input type="text" placeholder="Event Name" style="width:100px;margin:2px;">';
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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 || {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue