Update bootstrap and core modules
All checks were successful
BotServer CI / build (push) Successful in 10m27s
All checks were successful
BotServer CI / build (push) Successful in 10m27s
This commit is contained in:
parent
17cb4ef147
commit
4b44602d39
5 changed files with 163 additions and 20 deletions
|
|
@ -153,6 +153,42 @@ impl BootstrapManager {
|
|||
info!("Starting bootstrap process...");
|
||||
// Kill any existing processes
|
||||
self.kill_stack_processes().await?;
|
||||
|
||||
// Install all required components
|
||||
self.install_all().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Install all required components
|
||||
pub async fn install_all(&mut self) -> anyhow::Result<()> {
|
||||
let pm = PackageManager::new(self.install_mode.clone(), self.tenant.clone())?;
|
||||
|
||||
// Install vault first (required for secrets management)
|
||||
if !pm.is_installed("vault") {
|
||||
info!("Installing Vault...");
|
||||
match pm.install("vault").await {
|
||||
Ok(Some(_)) => info!("Vault installed successfully"),
|
||||
Ok(None) => warn!("Vault installation returned no result"),
|
||||
Err(e) => warn!("Failed to install Vault: {}", e),
|
||||
}
|
||||
} else {
|
||||
info!("Vault already installed");
|
||||
}
|
||||
|
||||
// Install other core components (names must match 3rdparty.toml)
|
||||
let core_components = ["tables", "cache", "drive", "llm"];
|
||||
for component in core_components {
|
||||
if !pm.is_installed(component) {
|
||||
info!("Installing {}...", component);
|
||||
match pm.install(component).await {
|
||||
Ok(Some(_)) => info!("{} installed successfully", component),
|
||||
Ok(None) => warn!("{} installation returned no result", component),
|
||||
Err(e) => warn!("Failed to install {}: {}", component, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@ pub struct BotConfigQuery {
|
|||
#[derive(Debug, Serialize)]
|
||||
pub struct BotConfigResponse {
|
||||
pub public: bool,
|
||||
pub theme_color1: Option<String>,
|
||||
pub theme_color2: Option<String>,
|
||||
pub theme_title: Option<String>,
|
||||
pub theme_logo: Option<String>,
|
||||
pub theme_logo_text: Option<String>,
|
||||
}
|
||||
|
||||
/// Get bot configuration endpoint
|
||||
|
|
@ -132,40 +137,78 @@ pub async fn get_bot_config(
|
|||
}
|
||||
};
|
||||
|
||||
// Query bot_configuration table for this bot's public setting
|
||||
// Query bot_configuration table for this bot's configuration
|
||||
use crate::core::shared::models::schema::bot_configuration::dsl::*;
|
||||
|
||||
let mut is_public = false;
|
||||
let mut theme_color1: Option<String> = None;
|
||||
let mut theme_color2: Option<String> = None;
|
||||
let mut theme_title: Option<String> = None;
|
||||
let mut theme_logo: Option<String> = None;
|
||||
let mut theme_logo_text: Option<String> = None;
|
||||
|
||||
// Query all config values (no prefix filter - will match in code)
|
||||
match bot_configuration
|
||||
.select((config_key, config_value))
|
||||
.filter(config_key.like(format!("{}%", bot_name)))
|
||||
.filter(config_key.like("%public%"))
|
||||
.load::<(String, String)>(&mut conn)
|
||||
{
|
||||
Ok(configs) => {
|
||||
info!("Config query returned {} entries for bot '{}'", configs.len(), bot_name);
|
||||
for (key, value) in configs {
|
||||
let key: String = key;
|
||||
let value: String = value;
|
||||
// Check if this is the public setting
|
||||
// Try to strip bot_name prefix, use original if no prefix
|
||||
let clean_key = key.strip_prefix(&format!("{}.", bot_name))
|
||||
.or_else(|| key.strip_prefix(&format!("{}_", bot_name)))
|
||||
.unwrap_or(&key);
|
||||
|
||||
if clean_key.eq_ignore_ascii_case("public") {
|
||||
is_public = value.eq_ignore_ascii_case("true") || value == "1";
|
||||
break;
|
||||
// Check if key is for this bot (either prefixed or not)
|
||||
let key_for_bot = clean_key == key || key.starts_with(&format!("{}.", bot_name)) || key.starts_with(&format!("{}_", bot_name));
|
||||
|
||||
info!("Key '{}' -> clean_key '{}' -> key_for_bot: {}", key, clean_key, key_for_bot);
|
||||
|
||||
if !key_for_bot {
|
||||
info!("Skipping key '{}' - not for bot '{}'", key, bot_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
match clean_key.to_lowercase().as_str() {
|
||||
"public" => {
|
||||
is_public = value.eq_ignore_ascii_case("true") || value == "1";
|
||||
}
|
||||
"theme-color1" => {
|
||||
theme_color1 = Some(value);
|
||||
}
|
||||
"theme-color2" => {
|
||||
theme_color2 = Some(value);
|
||||
}
|
||||
"theme-title" => {
|
||||
theme_title = Some(value);
|
||||
}
|
||||
"theme-logo" => {
|
||||
theme_logo = Some(value);
|
||||
}
|
||||
"theme-logo-text" => {
|
||||
theme_logo_text = Some(value);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
info!("Retrieved public status for bot '{}': {}", bot_name, is_public);
|
||||
info!("Retrieved config for bot '{}': public={}, theme_color1={:?}, theme_color2={:?}, theme_title={:?}",
|
||||
bot_name, is_public, theme_color1, theme_color2, theme_title);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to load public status for bot '{}': {}", bot_name, e);
|
||||
// Return default (not public)
|
||||
warn!("Failed to load config for bot '{}': {}", bot_name, e);
|
||||
// Return defaults (not public, no theme)
|
||||
}
|
||||
}
|
||||
|
||||
let config_response = BotConfigResponse { public: is_public };
|
||||
let config_response = BotConfigResponse {
|
||||
public: is_public,
|
||||
theme_color1,
|
||||
theme_color2,
|
||||
theme_title,
|
||||
theme_logo,
|
||||
theme_logo_text,
|
||||
};
|
||||
|
||||
Ok(Json(config_response))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -326,8 +326,71 @@ impl SecretsManager {
|
|||
self.cache.write().await.remove(path);
|
||||
}
|
||||
|
||||
fn get_from_env(_path: &str) -> Result<HashMap<String, String>> {
|
||||
Err(anyhow!("Vault not configured. All secrets must be stored in Vault. Set VAULT_ADDR and VAULT_TOKEN in .env"))
|
||||
fn get_from_env(path: &str) -> Result<HashMap<String, String>> {
|
||||
let mut secrets = HashMap::new();
|
||||
|
||||
match path {
|
||||
SecretPaths::TABLES => {
|
||||
secrets.insert("host".into(), "localhost".into());
|
||||
secrets.insert("port".into(), "5432".into());
|
||||
secrets.insert("database".into(), "botserver".into());
|
||||
secrets.insert("username".into(), "gbuser".into());
|
||||
secrets.insert("password".into(), "changeme".into());
|
||||
}
|
||||
SecretPaths::DIRECTORY => {
|
||||
secrets.insert("url".into(), "http://localhost:8300".into());
|
||||
secrets.insert("project_id".into(), String::new());
|
||||
secrets.insert("client_id".into(), String::new());
|
||||
secrets.insert("client_secret".into(), String::new());
|
||||
}
|
||||
SecretPaths::DRIVE => {
|
||||
secrets.insert("accesskey".into(), String::new());
|
||||
secrets.insert("secret".into(), String::new());
|
||||
}
|
||||
SecretPaths::CACHE => {
|
||||
secrets.insert("password".into(), String::new());
|
||||
}
|
||||
SecretPaths::EMAIL => {
|
||||
secrets.insert("smtp_host".into(), String::new());
|
||||
secrets.insert("smtp_port".into(), "587".into());
|
||||
secrets.insert("username".into(), String::new());
|
||||
secrets.insert("password".into(), String::new());
|
||||
secrets.insert("from_address".into(), String::new());
|
||||
}
|
||||
SecretPaths::LLM => {
|
||||
secrets.insert("openai_key".into(), String::new());
|
||||
secrets.insert("anthropic_key".into(), String::new());
|
||||
secrets.insert("ollama_url".into(), "http://localhost:11434".into());
|
||||
}
|
||||
SecretPaths::ENCRYPTION => {
|
||||
secrets.insert("master_key".into(), String::new());
|
||||
}
|
||||
SecretPaths::MEET => {
|
||||
secrets.insert("jitsi_url".into(), "https://meet.jit.si".into());
|
||||
secrets.insert("app_id".into(), String::new());
|
||||
secrets.insert("app_secret".into(), String::new());
|
||||
}
|
||||
SecretPaths::VECTORDB => {
|
||||
secrets.insert("url".into(), "http://localhost:6333".into());
|
||||
secrets.insert("api_key".into(), String::new());
|
||||
}
|
||||
SecretPaths::OBSERVABILITY => {
|
||||
secrets.insert("url".into(), "http://localhost:8086".into());
|
||||
secrets.insert("org".into(), "system".into());
|
||||
secrets.insert("bucket".into(), "metrics".into());
|
||||
secrets.insert("token".into(), String::new());
|
||||
}
|
||||
SecretPaths::ALM => {
|
||||
secrets.insert("url".into(), "http://localhost:8080".into());
|
||||
secrets.insert("username".into(), String::new());
|
||||
secrets.insert("password".into(), String::new());
|
||||
}
|
||||
_ => {
|
||||
log::debug!("No default values for secret path: {}", path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(secrets)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,13 +42,11 @@ pub async fn init_secrets_manager() -> Result<()> {
|
|||
pub async fn get_database_url() -> Result<String> {
|
||||
let guard = SECRETS_MANAGER.read().await;
|
||||
if let Some(ref manager) = *guard {
|
||||
if manager.is_enabled() {
|
||||
return manager.get_database_url().await;
|
||||
}
|
||||
return manager.get_database_url().await;
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"Vault not configured. Set VAULT_ADDR and VAULT_TOKEN in .env"
|
||||
"Secrets manager not initialized"
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +66,7 @@ pub fn get_database_url_sync() -> Result<String> {
|
|||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"Vault not configured. Set VAULT_ADDR and VAULT_TOKEN in .env"
|
||||
"Secrets manager not initialized"
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -219,6 +219,9 @@ pub async fn init_database(
|
|||
trace!("Creating database pool again...");
|
||||
progress_tx.send(BootstrapProgress::ConnectingDatabase).ok();
|
||||
|
||||
// Ensure secrets manager is initialized before creating database connection
|
||||
crate::core::shared::utils::init_secrets_manager().await;
|
||||
|
||||
let pool = match create_conn() {
|
||||
Ok(pool) => {
|
||||
trace!("Running database migrations...");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue