diff --git a/src/data/mod.rs b/src/data/mod.rs index 235e96e..d520f7a 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -100,6 +100,19 @@ pub struct PhotosResponse { pub dirs: Vec, } +#[derive(Deserialize)] +pub struct FilesRequest { + pub path: String, + pub tag_ids: Option>, + pub tag_filter_mode: Option, +} + +#[derive(Copy, Clone, Deserialize)] +pub enum FilterMode { + Any, + All, +} + #[derive(Deserialize)] pub struct ThumbnailRequest { pub path: String, diff --git a/src/files.rs b/src/files.rs index 114bf8f..c18c608 100644 --- a/src/files.rs +++ b/src/files.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use std::fs::read_dir; use std::io; use std::path::{Path, PathBuf}; +use std::sync::Mutex; use ::anyhow; use anyhow::{anyhow, Context}; @@ -10,18 +11,19 @@ use actix_web::{ web::{self, Query}, HttpResponse, }; - use log::{debug, error}; -use crate::data::{Claims, PhotosResponse, ThumbnailRequest}; +use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse, ThumbnailRequest}; use crate::AppState; +use crate::tags::TagDao; use path_absolutize::*; -pub async fn list_photos( +pub async fn list_photos( _: Claims, - req: Query, + req: Query, app_state: web::Data, + tag_dao: web::Data>, ) -> HttpResponse { let path = &req.path; if let Some(path) = is_valid_full_path(&PathBuf::from(&app_state.base_path), path) { @@ -44,6 +46,19 @@ pub async fn list_photos( relative.to_path_buf() }) .map(|f| f.to_str().unwrap().to_string()) + .filter(|path| { + if let (Some(tag_ids), Ok(mut tag_dao)) = (&req.tag_ids, tag_dao.lock()) { + let filter_mode = &req.tag_filter_mode.unwrap_or(FilterMode::Any); + + let file_tags = tag_dao.get_tags_for_path(path).unwrap_or_default(); + return match filter_mode { + FilterMode::Any => file_tags.iter().any(|t| tag_ids.contains(&t.id)), + FilterMode::All => file_tags.iter().all(|t| tag_ids.contains(&t.id)), + }; + } + + true + }) .collect::>(); let dirs = files @@ -153,6 +168,8 @@ mod tests { }; use std::{fs, sync::Arc}; + use actix_web::web::Data; + use crate::tags::SqliteTagDao; fn setup() { let _ = env_logger::builder().is_test(true).try_init(); @@ -167,7 +184,7 @@ mod tests { exp: 12345, }; - let request: Query = Query::from_query("path=").unwrap(); + let request: Query = Query::from_query("path=").unwrap(); let mut temp_photo = std::env::temp_dir(); let mut tmp = temp_photo.clone(); @@ -187,8 +204,9 @@ mod tests { String::from("/tmp"), String::from("/tmp/thumbs"), )), + Data::new(Mutex::new(SqliteTagDao::default())), ) - .await; + .await; let status = response.status(); let body: PhotosResponse = serde_json::from_str(&response.read_to_str()).unwrap(); @@ -215,7 +233,7 @@ mod tests { exp: 12345, }; - let request: Query = Query::from_query("path=..").unwrap(); + let request: Query = Query::from_query("path=..").unwrap(); let response = list_photos( claims, @@ -225,8 +243,9 @@ mod tests { String::from("/tmp"), String::from("/tmp/thumbs"), )), + Data::new(Mutex::new(SqliteTagDao::default())), ) - .await; + .await; assert_eq!(response.status(), 400); } diff --git a/src/main.rs b/src/main.rs index d2ed44f..5b02c45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -474,7 +474,7 @@ fn main() -> std::io::Result<()> { App::new() .wrap(middleware::Logger::default()) .service(web::resource("/login").route(web::post().to(login::))) - .service(web::resource("/photos").route(web::get().to(files::list_photos))) + .service(web::resource("/photos").route(web::get().to(files::list_photos::))) .service(get_image) .service(upload_image) .service(generate_video)