diff --git a/src/bin/server.rs b/src/bin/server.rs index c411965..cab74e9 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -20,7 +20,7 @@ use zettle_db::{ middleware_api_auth::api_auth, routes::{ api::{ - file::upload_handler, ingress::ingress_handler, query::query_handler, + file::upload_handler, ingress::ingress_data, query::query_handler, queue_length::queue_length_handler, }, html::{ @@ -132,7 +132,7 @@ async fn main() -> Result<(), Box> { fn api_routes_v1(app_state: &AppState) -> Router { Router::new() // Ingress routes - .route("/ingress", post(ingress_handler)) + .route("/ingress", post(ingress_data)) .route("/message_count", get(queue_length_handler)) // File routes .route("/file", post(upload_handler)) diff --git a/src/ingress/types/ingress_input.rs b/src/ingress/types/ingress_input.rs index 37288bf..70307aa 100644 --- a/src/ingress/types/ingress_input.rs +++ b/src/ingress/types/ingress_input.rs @@ -16,20 +16,19 @@ pub struct IngressInput { pub content: Option, pub instructions: String, pub category: String, - pub files: Option>, + pub files: Vec, } /// Function to create ingress objects from input. /// /// # Arguments /// * `input` - IngressInput containing information needed to ingress content. -/// * `redis_client` - Initialized redis client needed to retrieve file information +/// * `user_id` - User id of the ingressing user /// /// # Returns /// * `Vec` - An array containing the ingressed objects, one file/contenttype per object. -pub async fn create_ingress_objects( +pub fn create_ingress_objects( input: IngressInput, - db_client: &SurrealDbClient, user_id: &str, ) -> Result, AppError> { // Initialize list @@ -59,20 +58,13 @@ pub async fn create_ingress_objects( } } - // Look up FileInfo objects using the db and the submitted uuids in input.files - if let Some(file_ids) = input.files { - for id in file_ids { - if let Some(file_info) = get_item::(db_client, &id).await? { - object_list.push(IngressObject::File { - file_info, - instructions: input.instructions.clone(), - category: input.category.clone(), - user_id: user_id.into(), - }); - } else { - info!("No file with id: {}", id); - } - } + for file in input.files { + object_list.push(IngressObject::File { + file_info: file, + instructions: input.instructions.clone(), + category: input.category.clone(), + user_id: user_id.into(), + }) } // If no objects are constructed, we return Err diff --git a/src/server/routes/api/ingress.rs b/src/server/routes/api/ingress.rs index 0ed434c..b3d43bc 100644 --- a/src/server/routes/api/ingress.rs +++ b/src/server/routes/api/ingress.rs @@ -1,21 +1,49 @@ use crate::{ error::{ApiError, AppError}, - ingress::types::ingress_input::{create_ingress_objects, IngressInput}, + ingress::types::{ + ingress_input::{create_ingress_objects, IngressInput}, + ingress_object, + }, server::AppState, - storage::types::user::User, + storage::types::{file_info::FileInfo, user::User}, }; use axum::{extract::State, http::StatusCode, response::IntoResponse, Extension, Json}; -use futures::future::try_join_all; +use axum_typed_multipart::{FieldData, TryFromMultipart, TypedMultipart}; +use futures::{future::try_join_all, TryFutureExt}; +use tempfile::NamedTempFile; use tracing::info; -pub async fn ingress_handler( +#[derive(Debug, TryFromMultipart)] +pub struct IngressParams { + pub content: Option, + pub instructions: String, + pub category: String, + #[form_data(limit = "10000000")] // Adjust limit as needed + #[form_data(default)] + pub files: Vec>, +} + +pub async fn ingress_data( State(state): State, Extension(user): Extension, - Json(input): Json, + TypedMultipart(input): TypedMultipart, ) -> Result { info!("Received input: {:?}", input); - let ingress_objects = create_ingress_objects(input, &state.surreal_db_client, &user.id).await?; + let file_infos = try_join_all(input.files.into_iter().map(|file| { + FileInfo::new(file, &state.surreal_db_client, &user.id).map_err(|e| AppError::from(e)) + })) + .await?; + + let ingress_objects = create_ingress_objects( + IngressInput { + content: input.content, + instructions: input.instructions, + category: input.category, + files: file_infos, + }, + user.id.as_str(), + )?; let futures: Vec<_> = ingress_objects .into_iter() diff --git a/src/server/routes/html/ingress.rs b/src/server/routes/html/ingress.rs index dcae272..6bbbd42 100644 --- a/src/server/routes/html/ingress.rs +++ b/src/server/routes/html/ingress.rs @@ -43,6 +43,7 @@ pub struct IngressParams { pub instructions: String, pub category: String, #[form_data(limit = "10000000")] // Adjust limit as needed + #[form_data(default)] pub files: Vec>, } diff --git a/src/storage/types/file_info.rs b/src/storage/types/file_info.rs index 7596f9d..9decdc8 100644 --- a/src/storage/types/file_info.rs +++ b/src/storage/types/file_info.rs @@ -46,6 +46,7 @@ impl FileInfo { pub async fn new( field_data: FieldData, db_client: &SurrealDbClient, + user_id: &str, ) -> Result { let file = field_data.contents; let file_name = field_data @@ -74,7 +75,7 @@ impl FileInfo { let file_info = Self { id: uuid.to_string(), sha256, - path: Self::persist_file(&uuid, file, &sanitized_file_name) + path: Self::persist_file(&uuid, file, &sanitized_file_name, user_id) .await? .to_string_lossy() .into(), @@ -139,12 +140,13 @@ impl FileInfo { .collect() } - /// Persists the file to the filesystem under `./data/{uuid}/{file_name}`. + /// Persists the file to the filesystem under `./data/{user_id}/{uuid}/{file_name}`. /// /// # Arguments /// * `uuid` - The UUID of the file. /// * `file` - The temporary file to persist. /// * `file_name` - The sanitized file name. + /// * `user-id` - User id /// /// # Returns /// * `Result` - The persisted file path or an error. @@ -152,11 +154,13 @@ impl FileInfo { uuid: &Uuid, file: NamedTempFile, file_name: &str, + user_id: &str, ) -> Result { let base_dir = Path::new("./data"); - let uuid_dir = base_dir.join(uuid.to_string()); + let user_dir = base_dir.join(user_id); // Create the user directory + let uuid_dir = user_dir.join(uuid.to_string()); // Create the UUID directory under the user directory - // Create the UUID directory if it doesn't exist + // Create the user and UUID directories if they don't exist tokio::fs::create_dir_all(&uuid_dir) .await .map_err(FileError::Io)?; @@ -167,7 +171,6 @@ impl FileInfo { // Persist the temporary file to the final path file.persist(&final_path)?; - info!("Persisted file to {:?}", final_path); Ok(final_path) diff --git a/templates/body_base.html b/templates/body_base.html index 65d2506..119641d 100644 --- a/templates/body_base.html +++ b/templates/body_base.html @@ -6,7 +6,7 @@