Add container setup scripts for various services
Some checks failed
GBCI / build (push) Has been cancelled
Some checks failed
GBCI / build (push) Has been cancelled
- 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.
This commit is contained in:
parent
7131911313
commit
a4206ad63d
7 changed files with 264 additions and 27 deletions
|
@ -15,7 +15,7 @@ actix-ws="0.3.0"
|
||||||
bytes = "1.1"
|
bytes = "1.1"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
reqwest = { version = "0.11", features = ["json", "stream"] }
|
reqwest = { version = "0.11", features = ["json", "stream"] }
|
||||||
rhai = "1.0"
|
rhai = "1.22.2"
|
||||||
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
|
|
32
src/main.rs
32
src/main.rs
|
@ -10,7 +10,8 @@ use services::email::*;
|
||||||
use services::file::*;
|
use services::file::*;
|
||||||
use services::state::*;
|
use services::state::*;
|
||||||
use services::llm::*;
|
use services::llm::*;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
//use services:: find::*;
|
||||||
mod services;
|
mod services;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
|
@ -18,14 +19,23 @@ async fn main() -> std::io::Result<()> {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
let config = AppConfig::from_env();
|
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_service = ScriptService::new();
|
||||||
|
|
||||||
let script = r#"
|
let script = r#"
|
||||||
let json = FIND "users", "name=John"
|
|
||||||
let x=2
|
let list = FIND "rob", "ACTION=EMUL1"
|
||||||
let text = GET "example.com"
|
let text = GET "example.com"
|
||||||
let nome = "table"
|
|
||||||
let d = FIND nome, "car=2"
|
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
match script_service.compile(script) {
|
match script_service.compile(script) {
|
||||||
|
@ -40,16 +50,16 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
|
|
||||||
let db_url = config.database_url();
|
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)
|
let minio_client = init_minio(&config)
|
||||||
// .await
|
.await
|
||||||
// .expect("Failed to initialize Minio");
|
.expect("Failed to initialize Minio");
|
||||||
|
|
||||||
let app_state = web::Data::new(AppState {
|
let app_state = web::Data::new(AppState {
|
||||||
db: None,
|
db: db.into(),
|
||||||
config: Some(config.clone()),
|
config: Some(config.clone()),
|
||||||
minio_client: None,
|
minio_client: minio_client.into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start HTTP server
|
// Start HTTP server
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod file;
|
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod email;
|
pub mod email;
|
||||||
|
pub mod file;
|
||||||
pub mod llm;
|
pub mod llm;
|
||||||
pub mod script;
|
pub mod script;
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
use actix_web::{ web};
|
use actix_web::{ web};
|
||||||
|
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
|
|
232
src/services/find.md
Normal file
232
src/services/find.md
Normal file
|
@ -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<Value, Box<dyn Error>> {
|
||||||
|
// 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<Value, Box<dyn Error>> {
|
||||||
|
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::<i64, _>(i) {
|
||||||
|
Ok(v) => json!(v),
|
||||||
|
Err(_) => Value::Null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"float4" | "float8" => {
|
||||||
|
match row.try_get::<f64, _>(i) {
|
||||||
|
Ok(v) => json!(v),
|
||||||
|
Err(_) => Value::Null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text" | "varchar" => {
|
||||||
|
match row.try_get::<String, _>(i) {
|
||||||
|
Ok(v) => json!(v),
|
||||||
|
Err(_) => Value::Null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bool" => {
|
||||||
|
match row.try_get::<bool, _>(i) {
|
||||||
|
Ok(v) => json!(v),
|
||||||
|
Err(_) => Value::Null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json" | "jsonb" => {
|
||||||
|
match row.try_get::<Value, _>(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<String>), Box<dyn Error>> {
|
||||||
|
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<PgPool, Box<dyn Error>> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
use actix_web::http::Error;
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
get, post,
|
|
||||||
web::{self, Bytes},
|
web::{self, Bytes},
|
||||||
App, HttpResponse, HttpServer, Responder,
|
HttpResponse, Responder,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
@ -17,7 +15,7 @@ use langchain_rust::{
|
||||||
schemas::messages::Message,
|
schemas::messages::Message,
|
||||||
template_fstring,
|
template_fstring,
|
||||||
};
|
};
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use crate::services::{config::AIConfig, state::AppState};
|
use crate::services::{config::AIConfig, state::AppState};
|
||||||
|
|
||||||
|
@ -29,7 +27,6 @@ pub fn from_config(config: &AIConfig) -> AzureConfig {
|
||||||
.with_deployment_id(&config.instance)
|
.with_deployment_id(&config.instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
use serde_json::json;
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
struct ChatRequest {
|
struct ChatRequest {
|
||||||
input: String,
|
input: String,
|
||||||
|
@ -112,15 +109,6 @@ pub async fn chat_stream(
|
||||||
let azure_config = from_config(&state.config.clone().unwrap().ai);
|
let azure_config = from_config(&state.config.clone().unwrap().ai);
|
||||||
let open_ai = OpenAI::new(azure_config);
|
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![
|
let prompt = message_formatter![
|
||||||
fmt_message!(Message::new_system_message(
|
fmt_message!(Message::new_system_message(
|
||||||
|
|
|
@ -10,6 +10,10 @@ pub struct ScriptService {
|
||||||
module_resolver: StaticModuleResolver,
|
module_resolver: StaticModuleResolver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl ScriptService {
|
impl ScriptService {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
@ -91,6 +95,7 @@ impl ScriptService {
|
||||||
let url = context.eval_expression_tree(&inputs[0])?;
|
let url = context.eval_expression_tree(&inputs[0])?;
|
||||||
let url_str = url.to_string();
|
let url_str = url.to_string();
|
||||||
|
|
||||||
|
println!("GET executed: {}", url_str.to_string());
|
||||||
Ok(format!("Content from {}", url_str).into())
|
Ok(format!("Content from {}", url_str).into())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue