From a4206ad63de5249c46da04d8934f411c9626f56f Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Mon, 14 Jul 2025 21:05:55 -0300 Subject: [PATCH] Add container setup scripts for various services - Implemented ALM container setup with Forgejo installation and systemd service configuration. - Created Bot container setup with necessary dependencies and Node.js application installation. - Developed Desktop container setup with XRDP and Brave browser installation. - Established Directory container setup with Zitadel installation and service configuration. - Added Doc Editor container setup for Collabora Online integration. - Implemented Drive container setup with MinIO installation and service configuration. - Created Email container setup with Stalwart Mail installation and service configuration. - Developed Meeting container setup with LiveKit and TURN server configuration. - Added Proxy container setup with Caddy installation and service configuration. - Implemented System container setup for general bots with service configuration. - Created Table Editor container setup with NocoDB installation and service configuration. - Developed Tables container setup with PostgreSQL installation and configuration. - Added Webmail container setup with Roundcube installation and service configuration. - Included prompt guidelines for container setup scripts. --- Cargo.toml | 2 +- src/main.rs | 32 ++++-- src/services.rs | 3 +- src/services/file.rs | 1 + src/services/find.md | 232 +++++++++++++++++++++++++++++++++++++++++ src/services/llm.rs | 16 +-- src/services/script.rs | 5 + 7 files changed, 264 insertions(+), 27 deletions(-) create mode 100644 src/services/find.md diff --git a/Cargo.toml b/Cargo.toml index 61d19ac..50228a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ actix-ws="0.3.0" bytes = "1.1" futures-util = "0.3" reqwest = { version = "0.11", features = ["json", "stream"] } -rhai = "1.0" +rhai = "1.22.2" chrono = { version = "0.4", features = ["serde"] } dotenv = "0.15" diff --git a/src/main.rs b/src/main.rs index 4cde49a..0403df7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,8 @@ use services::email::*; use services::file::*; use services::state::*; use services::llm::*; - +use sqlx::PgPool; +//use services:: find::*; mod services; #[actix_web::main] @@ -18,14 +19,23 @@ async fn main() -> std::io::Result<()> { dotenv().ok(); let config = AppConfig::from_env(); + +// let table_str = "rob"; +// let filter_str = "ACTION=EMUL1"; + +// match execute_find(table_str, filter_str) { +// Ok(result) => println!("{}", result), +// Err(e) => eprintln!("Error: {}", e), +// } + + let script_service = ScriptService::new(); let script = r#" - let json = FIND "users", "name=John" - let x=2 + + let list = FIND "rob", "ACTION=EMUL1" let text = GET "example.com" - let nome = "table" - let d = FIND nome, "car=2" + "#; match script_service.compile(script) { @@ -40,16 +50,16 @@ async fn main() -> std::io::Result<()> { let db_url = config.database_url(); - //let db = PgPool::connect(&db_url).await.unwrap(); + let db = PgPool::connect(&db_url).await.unwrap(); - // let minio_client = init_minio(&config) - // .await - // .expect("Failed to initialize Minio"); + let minio_client = init_minio(&config) + .await + .expect("Failed to initialize Minio"); let app_state = web::Data::new(AppState { - db: None, + db: db.into(), config: Some(config.clone()), - minio_client: None, + minio_client: minio_client.into(), }); // Start HTTP server diff --git a/src/services.rs b/src/services.rs index 00342cf..7eb6360 100644 --- a/src/services.rs +++ b/src/services.rs @@ -1,6 +1,7 @@ pub mod config; -pub mod file; + pub mod state; pub mod email; +pub mod file; pub mod llm; pub mod script; \ No newline at end of file diff --git a/src/services/file.rs b/src/services/file.rs index c451dc2..e5c8f73 100644 --- a/src/services/file.rs +++ b/src/services/file.rs @@ -1,3 +1,4 @@ + use actix_web::{ web}; use actix_multipart::Multipart; diff --git a/src/services/find.md b/src/services/find.md new file mode 100644 index 0000000..cdc21cd --- /dev/null +++ b/src/services/find.md @@ -0,0 +1,232 @@ +- 100KB Email box + +i need a vb to russt watx +first wkeywords; +the keyword find for example will call the websiervics speciifid. +you now ? this should be compatible with languageserer so can be debuger +in code. user rust minamalistc to do this very inline and minimize code verbosity +it will be integatd in antoher proejt so i nee a sevicelayer +to call it from a schduler i need the compiler and the rnner of the ob +use localhost:5858 to call websiervis from the wasm executable +this is will attend + + FOR EACH item in ARRAY + next item + + FIND filter + + json = FIND "tablename", "field=value" + /tables/find + + SET "tablename", "key=value", "value" + /tables/set + + text = GET "domain.com" + /webauto/get_text + + text = GET WEBSITE "CASAS BAHIA" 'casasbahia.com via duckduckgo. + /webauto/get_website + + CREATE SITE "sitename",company, website, template, prompt + + /sites/create + copy template from temapltes folder + add llm outpt page.tsx + add listener + + CREATE DRAFT to, subject, body + /email/create_draft + + + + + + + + + + +use serde_json::{json, Value}; +use sqlx::{postgres::PgRow, PgPool, Row}; +use sqlx::Column; // Required for .name() method +use sqlx::TypeInfo; // Required for .type_info() method +use std::error::Error; +use sqlx::postgres::PgPoolOptions; + +// Main async function to execute the FIND command +pub async fn execute_find( + pool: &PgPool, + table_str: &str, + filter_str: &str, +) -> Result> { + // Parse the filter string into SQL WHERE clause and parameters + let (where_clause, params) = parse_filter(filter_str)?; + + // Build the SQL query with proper parameter binding + let query = format!("SELECT * FROM {} WHERE {}", table_str, where_clause); + + // Execute query and collect results + let rows = match params.len() { + 0 => sqlx::query(&query).fetch_all(pool).await?, + 1 => sqlx::query(&query).bind(¶ms[0]).fetch_all(pool).await?, + _ => return Err("Only single parameter filters supported in this example".into()), + }; + + // Convert rows to JSON values + let mut results = Vec::new(); + for row in rows { + results.push(row_to_json(row)?); + } + + // Return the structured result + Ok(json!({ + "command": "find", + "table": table_str, + "filter": filter_str, + "results": results + })) +} + +fn row_to_json(row: PgRow) -> Result> { + let mut result = serde_json::Map::new(); + for (i, column) in row.columns().iter().enumerate() { + let column_name = column.name(); + let value: Value = match column.type_info().name() { + "int4" | "int8" => { + match row.try_get::(i) { + Ok(v) => json!(v), + Err(_) => Value::Null, + } + }, + "float4" | "float8" => { + match row.try_get::(i) { + Ok(v) => json!(v), + Err(_) => Value::Null, + } + }, + "text" | "varchar" => { + match row.try_get::(i) { + Ok(v) => json!(v), + Err(_) => Value::Null, + } + }, + "bool" => { + match row.try_get::(i) { + Ok(v) => json!(v), + Err(_) => Value::Null, + } + }, + "json" | "jsonb" => { + match row.try_get::(i) { + Ok(v) => v, + Err(_) => Value::Null, + } + }, + _ => Value::Null, + }; + result.insert(column_name.to_string(), value); + } + Ok(Value::Object(result)) +} +// Helper function to parse the filter string into SQL WHERE clause and parameters +fn parse_filter(filter_str: &str) -> Result<(String, Vec), Box> { + let parts: Vec<&str> = filter_str.split('=').collect(); + if parts.len() != 2 { + return Err("Invalid filter format. Expected 'KEY=VALUE'".into()); + } + + let column = parts[0].trim(); + let value = parts[1].trim(); + + // Validate column name to prevent SQL injection + if !column.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err("Invalid column name in filter".into()); + } + + // Return the parameterized query part and the value separately + Ok((format!("{} = $1", column), vec![value.to_string()])) +} + +// Database connection setup +pub async fn create_pool(database_url: &str) -> Result> { + let pool = PgPoolOptions::new() + .max_connections(5) + .connect(database_url) + .await?; + + // Test the connection + sqlx::query("SELECT 1").execute(&pool).await?; + + Ok(pool) +} + +#[cfg(test)] +mod tests { + use super::*; + use sqlx::postgres::PgPoolOptions; + use dotenv::dotenv; + use std::env; + + async fn setup_test_db() -> PgPool { + dotenv().ok(); + let database_url = env::var("DATABASE_URL") + .expect("DATABASE_URL must be set in .env for tests"); + + let pool = PgPoolOptions::new() + .max_connections(1) + .connect(&database_url) + .await + .unwrap(); + + // Create a test table + sqlx::query( + r#" + DROP TABLE IF EXISTS rob; + CREATE TABLE rob ( + id SERIAL PRIMARY KEY, + action TEXT, + name TEXT, + is_active BOOLEAN, + metadata JSONB + ) + "# + ) + .execute(&pool) + .await + .unwrap(); + + // Insert test data + sqlx::query( + r#" + INSERT INTO rob (action, name, is_active, metadata) VALUES + ('EMUL1', 'Robot1', true, '{"version": 1}'), + ('EMUL2', 'Robot2', false, '{"version": 2}'), + ('EMUL1', 'Robot3', true, null) + "# + ) + .execute(&pool) + .await + .unwrap(); + + pool + } + + #[tokio::test] + async fn test_execute_find() { + let pool = setup_test_db().await; + let result = execute_find(&pool, "rob", "action=EMUL1") + .await + .unwrap(); + + let results = result["results"].as_array().unwrap(); + assert_eq!(results.len(), 2); + assert_eq!(results[0]["action"], "EMUL1"); + assert_eq!(results[1]["action"], "EMUL1"); + + // Test JSON field + assert_eq!(results[0]["metadata"]["version"], 1); + + // Test boolean field + assert_eq!(results[0]["is_active"], true); + } +} \ No newline at end of file diff --git a/src/services/llm.rs b/src/services/llm.rs index d4cafcd..d9db179 100644 --- a/src/services/llm.rs +++ b/src/services/llm.rs @@ -1,8 +1,6 @@ -use actix_web::http::Error; use actix_web::{ - get, post, web::{self, Bytes}, - App, HttpResponse, HttpServer, Responder, + HttpResponse, Responder, }; use anyhow::Result; use futures::StreamExt; @@ -17,7 +15,7 @@ use langchain_rust::{ schemas::messages::Message, template_fstring, }; -use std::env; + use crate::services::{config::AIConfig, state::AppState}; @@ -29,7 +27,6 @@ pub fn from_config(config: &AIConfig) -> AzureConfig { .with_deployment_id(&config.instance) } -use serde_json::json; #[derive(serde::Deserialize)] struct ChatRequest { input: String, @@ -112,15 +109,6 @@ pub async fn chat_stream( let azure_config = from_config(&state.config.clone().unwrap().ai); let open_ai = OpenAI::new(azure_config); - let response = match open_ai.invoke("Why is the sky blue?").await { - Ok(res) => res, - Err(err) => { - eprintln!("Error invoking API: {}", err); - return Err(actix_web::error::ErrorInternalServerError( - "Failed to invoke OpenAI API", - )); - } - }; let prompt = message_formatter![ fmt_message!(Message::new_system_message( diff --git a/src/services/script.rs b/src/services/script.rs index 183cb7a..0e146d6 100644 --- a/src/services/script.rs +++ b/src/services/script.rs @@ -10,6 +10,10 @@ pub struct ScriptService { module_resolver: StaticModuleResolver, } + + + + impl ScriptService { pub fn new() -> Self { let mut engine = Engine::new(); @@ -91,6 +95,7 @@ impl ScriptService { let url = context.eval_expression_tree(&inputs[0])?; let url_str = url.to_string(); + println!("GET executed: {}", url_str.to_string()); Ok(format!("Content from {}", url_str).into()) }, )