From d4db358850019b7d041ebc84459d3afa2eb466a4 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Thu, 30 Oct 2025 13:29:46 -0300 Subject: [PATCH] feat(bootstrap): add CSV config loading and improve legacy mode handling Added functionality to load configuration from CSV files in S3 when available, with fallback to existing methods. The changes include: 1. Added new `load_config_from_csv` method to handle CSV config loading 2. Improved legacy mode handling by attempting CSV load before falling back to database 3. Added new dependencies (uuid, Arc, Mutex) for config management 4. Enhanced error handling and logging for config loading scenarios The changes maintain backward compatibility while adding the ability to load configurations from CSV files stored in S3, which provides more flexibility in configuration management. --- src/bootstrap/mod.rs | 65 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/mod.rs b/src/bootstrap/mod.rs index 138c98ad7..c1393cbd0 100644 --- a/src/bootstrap/mod.rs +++ b/src/bootstrap/mod.rs @@ -1,6 +1,6 @@ use crate::config::AppConfig; use crate::package_manager::{InstallMode, PackageManager}; -use anyhow::Result; +use anyhow::{Result, Context}; use diesel::{connection::SimpleConnection, RunQueryDsl, Connection, QueryableByName}; use dotenvy::dotenv; use log::{debug, error, info, trace}; @@ -12,6 +12,8 @@ use sha2::{Digest, Sha256}; use std::io::{self, Write}; use std::path::Path; use std::process::Command; +use std::sync::{Arc, Mutex}; +use uuid::Uuid; #[derive(QueryableByName)] struct BotIdRow { @@ -156,6 +158,7 @@ impl BootstrapManager { } pub async fn bootstrap(&mut self) -> Result { + // First check for legacy mode if let Ok(tables_server) = std::env::var("TABLES_SERVER") { if !tables_server.is_empty() { info!( @@ -177,6 +180,11 @@ impl BootstrapManager { ) }); + // In legacy mode, still try to load config.csv if available + if let Ok(config) = self.load_config_from_csv().await { + return Ok(config); + } + match diesel::PgConnection::establish(&database_url) { Ok(mut conn) => { if let Err(e) = self.apply_migrations(&mut conn) { @@ -292,7 +300,13 @@ impl BootstrapManager { } self.s3_client = futures::executor::block_on(Self::create_s3_operator(&config)); - Ok(config) + + // Load config from CSV if available + if let Ok(csv_config) = self.load_config_from_csv().await { + Ok(csv_config) + } else { + Ok(config) + } } @@ -516,6 +530,53 @@ impl BootstrapManager { }) } + async fn load_config_from_csv(&self) -> Result { + use crate::config::ConfigManager; + use uuid::Uuid; + + let client = &self.s3_client; + let bucket = "templates/default.gbai"; + let config_key = "default.gbot/config.csv"; + + match client.get_object() + .bucket(bucket) + .key(config_key) + .send() + .await + { + Ok(response) => { + let bytes = response.body.collect().await?.into_bytes(); + let csv_content = String::from_utf8(bytes.to_vec())?; + + let database_url = std::env::var("DATABASE_URL") + .unwrap_or_else(|_| "postgres://gbuser:@localhost:5432/botserver".to_string()); + // Create new connection for config loading + let mut config_conn = diesel::PgConnection::establish(&database_url)?; + let config_manager = ConfigManager::new(Arc::new(Mutex::new(config_conn))); + + // Use default bot ID or create one if needed + let default_bot_id = Uuid::parse_str("00000000-0000-0000-0000-000000000000")?; + + // Write CSV to temp file for ConfigManager + let temp_path = std::env::temp_dir().join("config.csv"); + std::fs::write(&temp_path, csv_content)?; + + config_manager.sync_gbot_config(&default_bot_id, temp_path.to_str().unwrap()) + .map_err(|e| anyhow::anyhow!("Failed to sync gbot config: {}", e))?; + + // Load config from database which now has the CSV values + let mut config_conn = diesel::PgConnection::establish(&database_url)?; + let config = AppConfig::from_database(&mut config_conn); + info!("Successfully loaded config from CSV"); + Ok(config) + } + Err(e) => { + debug!("No config.csv found: {}", e); + Err(e.into()) + } + } + } + fn apply_migrations(&self, conn: &mut diesel::PgConnection) -> Result<()> { let migrations_dir = std::path::Path::new("migrations"); if !migrations_dir.exists() {