diff --git a/Cargo.toml b/Cargo.toml index 996a65b8..2df1f1c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ features = ["database", "i18n"] [features] # ===== DEFAULT ===== -default = ["chat", "automation", "cache", "llm"] +default = ["chat", "automation", "cache", "llm", "vectordb", "crawler"] # ===== SECURITY MODES ===== # no-security: Minimal build - chat, automation, drive, cache only (no RBAC, directory, security, compliance) diff --git a/src/basic/keywords/get.rs b/src/basic/keywords/get.rs index 52267a90..406b32c3 100644 --- a/src/basic/keywords/get.rs +++ b/src/basic/keywords/get.rs @@ -160,6 +160,7 @@ pub async fn get_from_bucket( } let client = state.drive.as_ref().ok_or("S3 client not configured")?; let bot_name: String = { + use crate::core::shared::models::schema::bots::dsl::*; let mut db_conn = state.conn.get().map_err(|e| format!("DB error: {}", e))?; bots.filter(id.eq(&bot_id)) .select(name) diff --git a/src/core/bot/multimedia.rs b/src/core/bot/multimedia.rs index 351a4d01..b0a23cce 100644 --- a/src/core/bot/multimedia.rs +++ b/src/core/bot/multimedia.rs @@ -499,7 +499,7 @@ use std::sync::Arc; pub async fn upload_media_handler( - State(_state): State>, + State(state): State>, Json(request): Json, ) -> impl IntoResponse { #[cfg(feature = "drive")] @@ -519,7 +519,7 @@ pub async fn upload_media_handler( pub async fn download_media_handler( - State(_state): State>, + State(state): State>, Path(media_id): Path, ) -> impl IntoResponse { #[cfg(feature = "drive")] @@ -548,7 +548,7 @@ pub async fn download_media_handler( pub async fn generate_thumbnail_handler( - State(_state): State>, + State(state): State>, Path(media_id): Path, ) -> impl IntoResponse { #[cfg(feature = "drive")] @@ -576,7 +576,7 @@ pub async fn generate_thumbnail_handler( pub async fn web_search_handler( - State(_state): State>, + State(state): State>, Json(payload): Json, ) -> impl IntoResponse { let query = payload.get("query").and_then(|q| q.as_str()).unwrap_or(""); diff --git a/src/drive/drive_monitor/mod.rs b/src/drive/drive_monitor/mod.rs index 9bcf341c..f78ee7ae 100644 --- a/src/drive/drive_monitor/mod.rs +++ b/src/drive/drive_monitor/mod.rs @@ -7,7 +7,6 @@ use crate::core::kb::KnowledgeBaseManager; use crate::core::shared::memory_monitor::{log_jemalloc_stats, MemoryStats}; use crate::core::shared::message_types::MessageType; use crate::core::shared::state::AppState; -#![cfg_attr(not(feature = "drive"), allow(dead_code))] #[cfg(feature = "drive")] use aws_sdk_s3::Client; diff --git a/src/drive/mod.rs b/src/drive/mod.rs index c320e132..d6045f80 100644 --- a/src/drive/mod.rs +++ b/src/drive/mod.rs @@ -22,6 +22,7 @@ use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; use diesel::{QueryableByName, RunQueryDsl}; use serde::{Deserialize, Serialize}; +use std::sync::Arc; #[cfg(feature = "drive")] pub mod document_processing; @@ -1025,6 +1026,92 @@ pub async fn restore_version( })) } +#[cfg(feature = "drive")] +pub async fn read_file(State(state): State>, Json(_req): Json) -> Result, (StatusCode, Json)> { + let s3_client = state.drive.as_ref().ok_or_else(|| { + ( + StatusCode::SERVICE_UNAVAILABLE, + Json(serde_json::json!({ "error": "S3 service not available" })), + ) + })?; + + // Default implementation reading from S3 + let result = s3_client + .get_object() + .bucket(&_req.bucket) + .key(&_req.path) + .send() + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(serde_json::json!({ "error": format!("Failed to read object: {}", e) })), + ) + })?; + + let bytes = result.body.collect().await.map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(serde_json::json!({ "error": format!("Failed to read body: {}", e) })), + ) + })?.into_bytes(); + + let content = String::from_utf8(bytes.to_vec()).unwrap_or_default(); + + Ok(Json(ReadResponse { content })) +} + +#[cfg(feature = "drive")] +pub async fn write_file(State(state): State>, Json(req): Json) -> Result, (StatusCode, Json)> { + let s3_client = state.drive.as_ref().ok_or_else(|| { + ( + StatusCode::SERVICE_UNAVAILABLE, + Json(serde_json::json!({ "error": "S3 service not available" })), + ) + })?; + + s3_client + .put_object() + .bucket(&req.bucket) + .key(&req.path) + .body(req.content.into_bytes().into()) + .send() + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(serde_json::json!({ "error": format!("Failed to write object: {}", e) })), + ) + })?; + + Ok(Json(SuccessResponse { success: true, message: Some("File written successfully".to_string()) })) +} + +#[cfg(feature = "drive")] +pub async fn delete_file(State(state): State>, Json(req): Json) -> Result, (StatusCode, Json)> { + let s3_client = state.drive.as_ref().ok_or_else(|| { + ( + StatusCode::SERVICE_UNAVAILABLE, + Json(serde_json::json!({ "error": "S3 service not available" })), + ) + })?; + + s3_client + .delete_object() + .bucket(&req.bucket) + .key(&req.path) + .send() + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(serde_json::json!({ "error": format!("Failed to delete object: {}", e) })), + ) + })?; + + Ok(Json(SuccessResponse { success: true, message: Some("File deleted successfully".to_string()) })) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/main_module/bootstrap.rs b/src/main_module/bootstrap.rs index e6688e3b..cbdafaf5 100644 --- a/src/main_module/bootstrap.rs +++ b/src/main_module/bootstrap.rs @@ -410,7 +410,7 @@ pub async fn create_app_state( let voice_adapter = Arc::new(VoiceAdapter::new()); #[cfg(feature = "drive")] - let drive = match create_s3_operator(&config.drive).await { + let drive = match create_s3_operator(&cfg.drive).await { Ok(client) => client, Err(e) => { return Err(std::io::Error::other(format!("Failed to initialize Drive: {}", e))); @@ -845,7 +845,7 @@ fn init_llm_provider( /// Start background services and monitors pub async fn start_background_services( app_state: Arc, - _pool: &crate::core::shared::utils::DbPool, + pool: &crate::core::shared::utils::DbPool, ) { use crate::core::shared::memory_monitor::{log_process_memory, start_memory_monitor}; @@ -961,7 +961,7 @@ async fn start_drive_monitors( let bucket_name_clone = bucket_name.clone(); tokio::spawn(async move { - use crate::DriveMonitor; + use crate::drive::drive_monitor::DriveMonitor; register_thread(&format!("drive-monitor-{}", bot_name), "drive"); trace!("DriveMonitor::new starting for bot: {}", bot_name); let monitor =