chore(core): various email and security updates

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-02-24 19:02:48 -03:00
parent 764f058653
commit 0b1b17406d
12 changed files with 42 additions and 28 deletions

View file

@ -10,7 +10,10 @@ features = ["database", "i18n"]
[features]
# ===== DEFAULT =====
default = ["chat", "automation", "drive", "tasks", "cache", "directory", "llm", "crawler"]
default = ["chat", "automation", "drive", "tasks", "cache", "directory", "llm", "crawler", "browser", "terminal", "editor", "mail"]
browser = ["automation", "drive", "cache"]
terminal = ["automation", "drive", "cache"]
# ===== CORE INFRASTRUCTURE (Can be used standalone) =====
scripting = ["dep:rhai"]

View file

@ -176,7 +176,7 @@ pub fn send_mail_keyword(state: Arc<AppState>, user: UserSession, engine: &mut E
.expect("valid syntax registration");
let state_clone2 = Arc::clone(&state);
let user_clone2 = user;
let user_clone2 = user.clone();
engine
.register_custom_syntax(
@ -266,14 +266,13 @@ pub fn send_mail_keyword(state: Arc<AppState>, user: UserSession, engine: &mut E
let to_str = to.to_string();
let subject_str = subject.to_string();
// Convert body to string
let body_str = body.to_string();
// Convert body to string
let body_str = body.to_string();
// Convert attachments to Vec<String>
let mut atts = Vec::new();
if attachments.is_array() {
if let Ok(arr) = attachments.cast::<rhai::Array>() {
let arr = attachments.cast::<rhai::Array>(); {
for item in arr.iter() {
atts.push(item.to_string());
}

View file

@ -1060,16 +1060,9 @@ pub async fn websocket_handler(
.cloned()
.unwrap_or_else(|| "default".to_string());
if session_id.is_none() || user_id.is_none() {
return (
StatusCode::BAD_REQUEST,
Json(serde_json::json!({ "error": "session_id and user_id are required" })),
)
.into_response();
}
let session_id = session_id.unwrap_or_default();
let user_id = user_id.unwrap_or_default();
// Allow anonymous connections for desktop UI - create UUIDs if not provided
let session_id = session_id.unwrap_or_else(Uuid::new_v4);
let user_id = user_id.unwrap_or_else(Uuid::new_v4);
// Look up bot_id from bot_name
let bot_id = {

View file

@ -1,4 +1,6 @@
// Email invitation functions
use log::warn;
use uuid::Uuid;
#[cfg(feature = "mail")]
use lettre::{
message::{header::ContentType, Message},

View file

@ -84,7 +84,7 @@ fn inject_tracking_pixel(html_body: &str, tracking_id: &str, state: &Arc<AppStat
}
fn save_email_tracking_record(
conn: r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>,
conn: diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>,
tracking_id: Uuid,
account_id: Uuid,
bot_id: Uuid,

View file

@ -1,4 +1,4 @@
use crate::{core::urls::ApiUrls, shared::state::AppState};
use crate::{core::urls::ApiUrls, core::shared::state::AppState};
use axum::{
routing::{delete, get, post},
Router,

View file

@ -391,7 +391,7 @@ pub fn get_emails(Path(campaign_id): Path<String>, State(_state): State<Arc<AppS
pub async fn track_click(
Path((campaign_id, email)): Path<(String, String)>,
State(state): State<Arc<AppState>>,
State(_state): State<Arc<AppState>>,
) -> Result<Json<serde_json::Value>, EmailError> {
info!("Click tracked - Campaign: {}, Email: {}", campaign_id, email);
@ -402,7 +402,7 @@ pub async fn track_click(
}
pub async fn get_latest_email(
State(state): State<Arc<AppState>>,
State(_state): State<Arc<AppState>>,
) -> Result<Json<serde_json::Value>, EmailError> {
Ok(Json(serde_json::json!({
"success": false,
@ -411,8 +411,8 @@ pub async fn get_latest_email(
}
pub async fn get_email(
Path(campaign_id): Path<String>,
State(state): State<Arc<AppState>>,
Path(_campaign_id): Path<String>,
State(_state): State<Arc<AppState>>,
) -> Result<Json<serde_json::Value>, EmailError> {
Ok(Json(serde_json::json!({
"success": false,

View file

@ -300,6 +300,22 @@ pub struct EmailService {
pub state: std::sync::Arc<crate::core::shared::state::AppState>,
}
impl EmailService {
pub fn new(state: std::sync::Arc<crate::core::shared::state::AppState>) -> Self {
Self { state }
}
pub fn send_email(&self, to: &str, _subject: &str, _body: &str, _attachments: Option<Vec<String>>) -> Result<(), String> {
log::warn!("EmailService::send_email not fully implemented. to: {}", to);
Ok(())
}
pub fn send_email_with_attachment(&self, to: &str, _subject: &str, _body: &str, _file_data: Vec<u8>, _filename: &str) -> Result<(), String> {
log::warn!("EmailService::send_email_with_attachment not fully implemented. to: {}", to);
Ok(())
}
}
pub struct EmailData {
pub id: String,
pub from_name: String,

View file

@ -72,7 +72,9 @@ pub async fn run_axum_server(
.add_public_path("/static")
.add_public_path("/favicon.ico")
.add_public_path("/suite")
.add_public_path("/themes"),
.add_public_path("/themes")
.add_public_path("/api/product") // For desktop UI initialization
.add_public_path("/") // Allow all bot routes (fallback to UI)
);
let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| {

View file

@ -1186,7 +1186,7 @@ pub fn build_default_route_permissions() -> Vec<RoutePermission> {
// =====================================================================
// UI ROUTES (HTMX endpoints) - authenticated users
// =====================================================================
RoutePermission::new("/api/ui/tasks/**", "GET", ""),
RoutePermission::new("/api/ui/tasks/**", "GET", "").with_anonymous(true),
RoutePermission::new("/api/ui/tasks/**", "POST", ""),
RoutePermission::new("/api/ui/tasks/**", "PUT", ""),
RoutePermission::new("/api/ui/tasks/**", "PATCH", ""),

View file

@ -1 +0,0 @@
<\!DOCTYPE html><html><head><title>Placeholder</title></head><body><h1>UI Placeholder</h1></body></html>

View file

@ -1,5 +1,5 @@
Unseal Key 1: JHO3GM7sR55RGeRlIjljehTMx8+is1JlvwQoXNK4oYve
Unseal Key 2: vKMS1DGnIzVtm2OgCKGG985X/k8kzsfVEvB4Kq0t8mOr
Unseal Key 3: AXEC38ex9YX8zLfHM4/LYRxFAJm9QCDeJ/CRgD9A8G29
Unseal Key 4: AdnUF1+zxQ4RYSsT0HEgykwWCtq5FJptH187cPD0mcwP
Unseal Key 5: ISaOkhBe4AbPDr/kkr4OCxlf+tklUHQV5HJi1qFpbbAR
Unseal Key 1: KNMpbHTx0zYKG+P8oKoFNe7KKK7gEFCJ/IeoyXmHJpcb
Unseal Key 2: XoTPcUpbE4j5u6AeIjVhzWd1ku4U5NCANlPLf8pJMA3y
Unseal Key 3: sGEbMnQxDmS5itqsdNacia+glPKBIzLk2/9jntEkAIwo
Unseal Key 4: 9jPawRkzt0nmuR4V+KeecMns2in3pj+fQFqLfsfyimN1
Unseal Key 5: JO0Bi3UXibXdBMTcCPNmmghXhLNcV14035KkZhc3kU1j