diff --git a/src/modes/server.rs b/src/modes/server.rs index 643c568..e4d2432 100644 --- a/src/modes/server.rs +++ b/src/modes/server.rs @@ -13,6 +13,7 @@ use tower_http::cors::CorsLayer; use tower::ServiceBuilder; use tower_http::trace::TraceLayer; use crate::config; +use crate::services::item_service::ItemService; pub mod common; mod api; @@ -53,17 +54,21 @@ pub fn mode_server( password_hash: settings.server_password_hash(), }; + // Create ItemService once + let item_service = ItemService::new(data_path.clone()); + // We need to move the connection into the async runtime let rt = tokio::runtime::Runtime::new()?; // Take ownership of the connection and move it into the async runtime let owned_conn = std::mem::replace(conn, rusqlite::Connection::open_in_memory()?); - rt.block_on(run_server(server_config, owned_conn, data_path)) + rt.block_on(run_server(server_config, owned_conn, data_path, item_service)) } async fn run_server( config: common::ServerConfig, conn: rusqlite::Connection, data_dir: PathBuf, + item_service: ItemService, ) -> Result<()> { // Construct address with port let bind_address = if let Some(port) = config.port { @@ -80,6 +85,7 @@ async fn run_server( let state = AppState { db: db_conn, data_dir: data_dir.clone(), + item_service: Arc::new(item_service), }; // Create MCP router diff --git a/src/modes/server/api/item.rs b/src/modes/server/api/item.rs index 0380f36..f830c23 100644 --- a/src/modes/server/api/item.rs +++ b/src/modes/server/api/item.rs @@ -43,7 +43,7 @@ pub async fn handle_list_items( .map(|s| s.split(',').map(|t| t.trim().to_string()).collect()) .unwrap_or_default(); - let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone()); + let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone(), state.item_service.clone()); let mut items_with_meta = item_service .list_items(tags, HashMap::new()) .await @@ -166,7 +166,7 @@ pub async fn handle_get_item_latest_content( .map(|s| s.split(',').map(|t| t.trim().to_string()).collect()) .unwrap_or_default(); - let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone()); + let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone(), state.item_service.clone()); // First get the item metadata to check if it's binary and get MIME type let item_with_meta = item_service @@ -220,7 +220,7 @@ pub async fn handle_get_item_content( return Err(StatusCode::BAD_REQUEST); } - let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone()); + let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone(), state.item_service.clone()); stream_item_content_response(&item_service, item_id, params.allow_binary, params.offset, params.length).await } @@ -314,7 +314,7 @@ pub async fn handle_get_item_latest_meta( .map(|s| s.split(',').map(|t| t.trim().to_string()).collect()) .unwrap_or_default(); - let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone()); + let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone(), state.item_service.clone()); match item_service.find_item(vec![], tags, HashMap::new()).await { Ok(item_with_meta) => { @@ -361,7 +361,7 @@ pub async fn handle_get_item_meta( State(state): State, Path(item_id): Path, ) -> Result>>, StatusCode> { - let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone()); + let item_service = AsyncItemService::new(state.data_dir.clone(), state.db.clone(), state.item_service.clone()); match item_service.get_item(item_id).await { Ok(item_with_meta) => { diff --git a/src/modes/server/common.rs b/src/modes/server/common.rs index 61d53b4..17f60c4 100644 --- a/src/modes/server/common.rs +++ b/src/modes/server/common.rs @@ -15,6 +15,7 @@ use std::sync::Arc; use std::time::Instant; use tokio::sync::Mutex; use utoipa::ToSchema; +use crate::services::item_service::ItemService; #[derive(Debug, Clone)] pub struct ServerConfig { @@ -28,6 +29,7 @@ pub struct ServerConfig { pub struct AppState { pub db: Arc>, pub data_dir: PathBuf, + pub item_service: Arc, } #[derive(Serialize, Deserialize, ToSchema)] diff --git a/src/services/async_item_service.rs b/src/services/async_item_service.rs index 408a278..e7dd30f 100644 --- a/src/services/async_item_service.rs +++ b/src/services/async_item_service.rs @@ -17,21 +17,21 @@ use tokio_util::io::ReaderStream; pub struct AsyncItemService { pub data_dir: PathBuf, db: Arc>, + item_service: Arc, } #[allow(dead_code)] impl AsyncItemService { - pub fn new(data_dir: PathBuf, db: Arc>) -> Self { - Self { data_dir, db } + pub fn new(data_dir: PathBuf, db: Arc>, item_service: Arc) -> Self { + Self { data_dir, db, item_service } } pub async fn get_item(&self, id: i64) -> Result { - let data_dir = self.data_dir.clone(); let db = self.db.clone(); + let item_service = self.item_service.clone(); tokio::task::spawn_blocking(move || { let conn = db.blocking_lock(); - let item_service = ItemService::new(data_dir); item_service.get_item(&conn, id) }) .await @@ -39,12 +39,11 @@ impl AsyncItemService { } pub async fn get_item_content(&self, id: i64) -> Result { - let data_dir = self.data_dir.clone(); let db = self.db.clone(); + let item_service = self.item_service.clone(); tokio::task::spawn_blocking(move || { let conn = db.blocking_lock(); - let item_service = ItemService::new(data_dir); item_service.get_item_content(&conn, id) }) .await @@ -59,13 +58,12 @@ impl AsyncItemService { offset: u64, length: u64, ) -> Result<(std::pin::Pin> + Send>>, String), CoreError> { - let data_dir = self.data_dir.clone(); let db = self.db.clone(); + let item_service = self.item_service.clone(); // Get item metadata first to check binary status and get MIME type let item_with_content = tokio::task::spawn_blocking(move || { let conn = db.blocking_lock(); - let item_service = ItemService::new(data_dir); item_service.get_item_content(&conn, item_id) }) .await @@ -120,12 +118,11 @@ impl AsyncItemService { tags: Vec, meta: HashMap, ) -> Result { - let data_dir = self.data_dir.clone(); let db = self.db.clone(); + let item_service = self.item_service.clone(); tokio::task::spawn_blocking(move || { let conn = db.blocking_lock(); - let item_service = ItemService::new(data_dir); item_service.find_item(&conn, &ids, &tags, &meta) }) .await @@ -137,12 +134,11 @@ impl AsyncItemService { tags: Vec, meta: HashMap, ) -> Result, CoreError> { - let data_dir = self.data_dir.clone(); let db = self.db.clone(); + let item_service = self.item_service.clone(); tokio::task::spawn_blocking(move || { let conn = db.blocking_lock(); - let item_service = ItemService::new(data_dir); item_service.list_items(&conn, &tags, &meta) }) .await @@ -150,12 +146,11 @@ impl AsyncItemService { } pub async fn delete_item(&self, id: i64) -> Result<(), CoreError> { - let data_dir = self.data_dir.clone(); let db = self.db.clone(); + let item_service = self.item_service.clone(); tokio::task::spawn_blocking(move || { let mut conn = db.blocking_lock(); - let item_service = ItemService::new(data_dir); item_service.delete_item(&mut conn, id) }) .await @@ -168,12 +163,11 @@ impl AsyncItemService { tags: Vec, metadata: HashMap, ) -> Result { - let data_dir = self.data_dir.clone(); let db = self.db.clone(); + let item_service = self.item_service.clone(); tokio::task::spawn_blocking(move || { let mut conn = db.blocking_lock(); - let item_service = ItemService::new(data_dir); item_service.save_item_from_mcp(&content, &tags, &metadata, &mut conn) }) .await