- Tables is installing.

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-10-19 14:02:47 -03:00
parent 88ca214366
commit 6f30517526
8 changed files with 61 additions and 60 deletions

View file

@ -2,11 +2,13 @@
{ {
"label": "Debug BotServer", "label": "Debug BotServer",
"build": { "build": {
"command": "cargo", "command": "rm -rf ./botserver-stack && cargo",
"args": ["run"] "args": ["build"]
}, },
"program": "$ZED_WORKTREE_ROOT/target/debug/botserver", "program": "$ZED_WORKTREE_ROOT/target/debug/botserver",
"env": {
"RUST_LOG": "trace"
},
"sourceLanguages": ["rust"], "sourceLanguages": ["rust"],
"request": "launch", "request": "launch",
"adapter": "CodeLLDB" "adapter": "CodeLLDB"

View file

@ -1,3 +1,2 @@
clear && \ clear && \
cargo build && \ RUST_LOG=trace cargo run install tables
sudo RUST_BACKTRACE=1 ./target/debug/botserver install tables

View file

@ -11,7 +11,8 @@ MOST IMPORTANT CODE GENERATION RULES:
- Every part must be executable and self-contained, with real implementations - only. - Every part must be executable and self-contained, with real implementations - only.
- DO NOT WRITE ANY ERROR HANDLING CODE LET IT CRASH. - DO NOT WRITE ANY ERROR HANDLING CODE LET IT CRASH.
- Never generate two ore more trace mensages that are equal! - Never generate two ore more trace mensages that are equal!
- Return *only the modified* files as a single `.sh` script using `cat`, so the code can be restored directly. - Return *only the modified* files as a single `.sh` script using `cat`, so the code can be - restored directly.
- Pay attention to shared::utils and shared::models to reuse shared things.
- NEVER return a untouched file in output. Just files that need to be updated. - NEVER return a untouched file in output. Just files that need to be updated.
- You MUST return exactly this example format: - You MUST return exactly this example format:

View file

@ -25,9 +25,10 @@ impl BootstrapManager {
let pm = PackageManager::new(self.install_mode.clone(), self.tenant.clone())?; let pm = PackageManager::new(self.install_mode.clone(), self.tenant.clone())?;
let required_components = vec!["drive", "cache", "tables", "llm"]; let required_components = vec!["tables", "cache", "drive", "llm"];
for component in required_components { for component in required_components {
info!("Checking component: {}", component);
if !pm.is_installed(component) { if !pm.is_installed(component) {
info!("Installing required component: {}", component); info!("Installing required component: {}", component);
futures::executor::block_on(pm.install(component))?; futures::executor::block_on(pm.install(component))?;

View file

@ -56,7 +56,7 @@ use crate::web_server::{index, static_files};
use crate::whatsapp::whatsapp_webhook_verify; use crate::whatsapp::whatsapp_webhook_verify;
use crate::whatsapp::WhatsAppAdapter; use crate::whatsapp::WhatsAppAdapter;
#[actix_web::main] #[tokio::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();

View file

@ -2,10 +2,11 @@ use anyhow::{Context, Result};
use log::{debug, info, trace, warn}; use log::{debug, info, trace, warn};
use reqwest::Client; use reqwest::Client;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use crate::shared::utils; // Adjust based on your actual module structure
use crate::package_manager::component::ComponentConfig; use crate::package_manager::component::ComponentConfig;
use crate::package_manager::installer::PackageManager; use crate::package_manager::installer::PackageManager;
use crate::package_manager::OsType; use crate::package_manager::OsType;
@ -79,11 +80,6 @@ impl PackageManager {
.await?; .await?;
self.run_commands(post_cmds, "local", &component.name)?; self.run_commands(post_cmds, "local", &component.name)?;
} }
if self.os_type == OsType::Linux && !component.exec_cmd.is_empty() {
self.create_service_file(&component.name, &component.exec_cmd, &component.env_vars)?;
}
info!("Local installation of '{}' completed", component.name); info!("Local installation of '{}' completed", component.name);
Ok(()) Ok(())
} }
@ -342,7 +338,11 @@ impl PackageManager {
std::fs::create_dir_all(&bin_path)?; std::fs::create_dir_all(&bin_path)?;
let filename = url.split('/').last().unwrap_or("download.tmp"); let filename = url.split('/').last().unwrap_or("download.tmp");
let temp_file = bin_path.join(filename); let temp_file = if filename.starts_with('/') {
PathBuf::from(filename)
} else {
bin_path.join(filename)
};
info!("Downloading from: {} to {:?}", url, temp_file); info!("Downloading from: {} to {:?}", url, temp_file);
@ -406,24 +406,23 @@ impl PackageManager {
pub async fn attempt_reqwest_download( pub async fn attempt_reqwest_download(
&self, &self,
client: &Client, _client: &Client, // We won't use this if using shared utils
url: &str, url: &str,
temp_file: &PathBuf, temp_file: &PathBuf,
) -> Result<u64> { ) -> Result<u64> {
let response = client info!("Downloading from: {} to {:?}", url, temp_file);
.get(url)
.send()
.await
.context("Failed to send request")?;
let mut file = std::fs::File::create(temp_file).context("Failed to create output file")?; // Convert PathBuf to string for the shared function
let bytes = response let output_path = temp_file.to_str().context("Invalid temp file path")?;
.bytes()
// Use the shared download_file utility
utils::download_file(url, output_path)
.await .await
.context("Failed to read response bytes")?; .map_err(|e| anyhow::anyhow!("Failed to download file using shared utility: {}", e))?;
let size = bytes.len() as u64;
file.write_all(&bytes) // Get file size to return
.context("Failed to write response to file")?; let metadata = std::fs::metadata(temp_file).context("Failed to get file metadata")?;
let size = metadata.len();
info!("Downloaded {} bytes", size); info!("Downloaded {} bytes", size);
Ok(size) Ok(size)
@ -470,10 +469,9 @@ impl PackageManager {
pub fn extract_tar_gz(&self, temp_file: &PathBuf, bin_path: &PathBuf) -> Result<()> { pub fn extract_tar_gz(&self, temp_file: &PathBuf, bin_path: &PathBuf) -> Result<()> {
info!("Extracting tar.gz archive to {:?}", bin_path); info!("Extracting tar.gz archive to {:?}", bin_path);
let output = Command::new("tar") let output = Command::new("tar")
.current_dir(bin_path) .current_dir(bin_path)
.args(&["-xzf", temp_file.to_str().unwrap()]) .args(&["-xzf", temp_file.to_str().unwrap(), "--strip-components=1"])
.output()?; .output()?;
if !output.status.success() { if !output.status.success() {

View file

@ -21,7 +21,7 @@ impl PackageManager {
let base_path = if mode == InstallMode::Container { let base_path = if mode == InstallMode::Container {
PathBuf::from("/opt/gbo") PathBuf::from("/opt/gbo")
} else { } else {
PathBuf::from("./botserver-stack") std::env::current_dir()?.join("botserver-stack")
}; };
let tenant = tenant.unwrap_or_else(|| "default".to_string()); let tenant = tenant.unwrap_or_else(|| "default".to_string());
@ -45,9 +45,9 @@ impl PackageManager {
} }
fn register_components(&mut self) { fn register_components(&mut self) {
self.register_drive();
self.register_cache();
self.register_tables(); self.register_tables();
self.register_cache();
self.register_drive();
self.register_llm(); self.register_llm();
self.register_email(); self.register_email();
self.register_proxy(); self.register_proxy();
@ -110,8 +110,8 @@ impl PackageManager {
download_url: None, download_url: None,
binary_name: Some("valkey-server".to_string()), binary_name: Some("valkey-server".to_string()),
pre_install_cmds_linux: vec![ pre_install_cmds_linux: vec![
"curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/valkey.gpg".to_string(), "if [ ! -f /usr/share/keyrings/valkey.gpg ]; then curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/valkey.gpg; fi".to_string(),
"echo 'deb [signed-by=/usr/share/keyrings/valkey.gpg] https://packages.redis.io/deb $(lsb_release -cs) main' | tee /etc/apt/sources.list.d/valkey.list".to_string(), "if [ ! -f /etc/apt/sources.list.d/valkey.list ]; then echo 'deb [signed-by=/usr/share/keyrings/valkey.gpg] https://packages.redis.io/deb $(lsb_release -cs) main' | tee /etc/apt/sources.list.d/valkey.list; fi".to_string(),
"apt-get update && apt-get install -y valkey".to_string() "apt-get update && apt-get install -y valkey".to_string()
], ],
post_install_cmds_linux: vec![], post_install_cmds_linux: vec![],
@ -137,9 +137,7 @@ impl PackageManager {
binary_name: Some("postgres".to_string()), binary_name: Some("postgres".to_string()),
pre_install_cmds_linux: vec![], pre_install_cmds_linux: vec![],
post_install_cmds_linux: vec![ post_install_cmds_linux: vec![
"tar -xzf postgresql-18.0.0-x86_64-unknown-linux-gnu.tar.gz".to_string(), "if [ ! -d \"{{DATA_PATH}}/pgdata\" ]; then ./bin/initdb -D {{DATA_PATH}}/pgdata -U postgres; fi".to_string(),
"mv pgsql/* . && rm -rf pgsql".to_string(),
"if [ ! -d \"{{DATA_PATH}}/pgdata\" ]; then ./initdb -D {{DATA_PATH}}/pgdata -U postgres; fi".to_string(),
"if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"data_directory = '{{DATA_PATH}}/pgdata'\" > {{CONF_PATH}}/postgresql.conf; fi".to_string(), "if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"data_directory = '{{DATA_PATH}}/pgdata'\" > {{CONF_PATH}}/postgresql.conf; fi".to_string(),
"if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"hba_file = '{{CONF_PATH}}/pg_hba.conf'\" >> {{CONF_PATH}}/postgresql.conf; fi".to_string(), "if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"hba_file = '{{CONF_PATH}}/pg_hba.conf'\" >> {{CONF_PATH}}/postgresql.conf; fi".to_string(),
"if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"ident_file = '{{CONF_PATH}}/pg_ident.conf'\" >> {{CONF_PATH}}/postgresql.conf; fi".to_string(), "if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"ident_file = '{{CONF_PATH}}/pg_ident.conf'\" >> {{CONF_PATH}}/postgresql.conf; fi".to_string(),
@ -149,18 +147,16 @@ impl PackageManager {
"if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"logging_collector = on\" >> {{CONF_PATH}}/postgresql.conf; fi".to_string(), "if [ ! -f \"{{CONF_PATH}}/postgresql.conf\" ]; then echo \"logging_collector = on\" >> {{CONF_PATH}}/postgresql.conf; fi".to_string(),
"if [ ! -f \"{{CONF_PATH}}/pg_hba.conf\" ]; then echo \"host all all all md5\" > {{CONF_PATH}}/pg_hba.conf; fi".to_string(), "if [ ! -f \"{{CONF_PATH}}/pg_hba.conf\" ]; then echo \"host all all all md5\" > {{CONF_PATH}}/pg_hba.conf; fi".to_string(),
"if [ ! -f \"{{CONF_PATH}}/pg_ident.conf\" ]; then touch {{CONF_PATH}}/pg_ident.conf; fi".to_string(), "if [ ! -f \"{{CONF_PATH}}/pg_ident.conf\" ]; then touch {{CONF_PATH}}/pg_ident.conf; fi".to_string(),
"if [ ! -d \"{{DATA_PATH}}/pgdata\" ]; then ./pg_ctl -D {{DATA_PATH}}/pgdata -l {{LOGS_PATH}}/postgres.log start; sleep 5; ./psql -p 5432 -d postgres -c \"CREATE USER default WITH PASSWORD 'defaultpass'\"; ./psql -p 5432 -d postgres -c \"CREATE DATABASE default_db OWNER default\"; ./psql -p 5432 -d postgres -c \"GRANT ALL PRIVILEGES ON DATABASE default_db TO default\"; ./pg_ctl -D {{DATA_PATH}}/pgdata stop; fi".to_string() "if [ ! -d \"{{DATA_PATH}}/pgdata\" ]; then ./bin/pg_ctl -D {{DATA_PATH}}/pgdata -l {{LOGS_PATH}}/postgres.log start; sleep 5; ./bin/psql -p 5432 -d postgres -c \" CREATE USER default WITH PASSWORD 'defaultpass'\"; ./bin/psql -p 5432 -d postgres -c \"CREATE DATABASE default_db OWNER default\"; ./bin/psql -p 5432 -d postgres -c \"GRANT ALL PRIVILEGES ON DATABASE default_db TO default\"; pkill postgres; fi".to_string()
], ],
pre_install_cmds_macos: vec![], pre_install_cmds_macos: vec![],
post_install_cmds_macos: vec![ post_install_cmds_macos: vec![
"tar -xzf postgresql-18.0-1-linux-x64-binaries.tar.gz".to_string(), "if [ ! -d \"{{DATA_PATH}}/pgdata\" ]; then ./bin/initdb -D {{DATA_PATH}}/pgdata -U postgres; fi".to_string(),
"mv pgsql/* . && rm -rf pgsql".to_string(),
"if [ ! -d \"{{DATA_PATH}}/pgdata\" ]; then ./initdb -D {{DATA_PATH}}/pgdata -U postgres; fi".to_string(),
], ],
pre_install_cmds_windows: vec![], pre_install_cmds_windows: vec![],
post_install_cmds_windows: vec![], post_install_cmds_windows: vec![],
env_vars: HashMap::new(), env_vars: HashMap::new(),
exec_cmd: "./pg_ctl -D {{DATA_PATH}}/pgdata -l {{LOGS_PATH}}/postgres.log start".to_string(), exec_cmd: "./bin/pg_ctl -D {{DATA_PATH}}/pgdata -l {{LOGS_PATH}}/postgres.log start".to_string(),
}); });
} }

View file

@ -7,7 +7,7 @@ use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
use std::path::Path; use std::path::Path;
use tokio::fs::File as TokioFile; use tokio::fs::File as TokioFile;
use tokio_stream::StreamExt;
use zip::ZipArchive; use zip::ZipArchive;
use crate::config::AIConfig; use crate::config::AIConfig;
@ -79,24 +79,28 @@ pub fn to_array(value: Dynamic) -> Array {
} }
} }
pub async fn download_file(url: &str, output_path: &str) -> Result<(), Box<dyn std::error::Error>> { pub async fn download_file(
url: &str,
output_path: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let url = url.to_string();
let output_path = output_path.to_string();
let download_handle = tokio::spawn(async move {
let client = Client::new(); let client = Client::new();
let response = client.get(url).send().await?; let response = client.get(&url).send().await?;
if response.status().is_success() { if response.status().is_success() {
let mut file = TokioFile::create(output_path).await?; let mut file = TokioFile::create(&output_path).await?;
let bytes = response.bytes().await?;
let mut stream = response.bytes_stream(); file.write_all(&bytes).await?;
while let Some(chunk) = stream.next().await {
file.write_all(&chunk?).await?;
}
debug!("File downloaded successfully to {}", output_path); debug!("File downloaded successfully to {}", output_path);
} else {
return Err("Failed to download file".into());
}
Ok(()) Ok(())
} else {
Err(format!("HTTP {}: {}", response.status(), url).into())
}
});
download_handle.await?
} }
pub fn parse_filter(filter_str: &str) -> Result<(String, Vec<String>), Box<dyn Error>> { pub fn parse_filter(filter_str: &str) -> Result<(String, Vec<String>), Box<dyn Error>> {