From fb33fed0935afcbeefd8c7e0a22e0fd641faae7f Mon Sep 17 00:00:00 2001 From: rawdaGastan Date: Tue, 12 Nov 2024 16:16:29 +0200 Subject: [PATCH] support pagination and filters for listing flists --- fl-server/src/handlers.rs | 64 ++++++++++++++++++++++++++++++++--- fl-server/src/response.rs | 2 +- fl-server/src/serve_flists.rs | 27 +++++++++++++-- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/fl-server/src/handlers.rs b/fl-server/src/handlers.rs index 08a47d7..6b84b9f 100644 --- a/fl-server/src/handlers.rs +++ b/fl-server/src/handlers.rs @@ -1,6 +1,6 @@ use anyhow::Error; use axum::{ - extract::{Path, State}, + extract::{Path, Query, State}, response::IntoResponse, Extension, Json, }; @@ -74,6 +74,23 @@ pub struct FlistStateInfo { progress: f32, } +const DEFAULT_LIMIT: usize = 10; +const DEFAULT_PAGE: usize = 1; + +#[derive(Deserialize)] +pub struct Pagination { + page: Option, + limit: Option, +} + +#[derive(Deserialize, Clone)] +pub struct Filter { + pub max_size: Option, + pub min_size: Option, + username: Option, + pub name: Option, +} + #[utoipa::path( get, path = "/v1/api", @@ -337,11 +354,27 @@ pub async fn get_flist_state_handler( ) )] #[debug_handler] -pub async fn list_flists_handler(State(state): State>) -> impl IntoResponse { +pub async fn list_flists_handler( + State(state): State>, + pagination: Query, + filter: Query, +) -> impl IntoResponse { let mut flists: HashMap> = HashMap::new(); + let pagination: Pagination = pagination.0; + let page = pagination.page.unwrap_or(DEFAULT_PAGE); + let limit = pagination.limit.unwrap_or(DEFAULT_LIMIT); + + if page == 0 { + return Err(ResponseError::BadRequest( + "requested page should be nonzero positive number".to_string(), + )); + } + + let filter: Filter = filter.0; + let rs: Result, std::io::Error> = - visit_dir_one_level(&state.config.flist_dir, &state).await; + visit_dir_one_level(&state.config.flist_dir, &state, None).await; let files = match rs { Ok(files) => files, @@ -353,9 +386,30 @@ pub async fn list_flists_handler(State(state): State>) -> for file in files { if !file.is_file { - let flists_per_username = visit_dir_one_level(&file.path_uri, &state).await; + let flists_per_username = + visit_dir_one_level(&file.path_uri, &state, Some(filter.clone())).await; + + if let Some(ref filter_username) = filter.username { + if filter_username.clone() != file.name { + continue; + } + } + match flists_per_username { - Ok(files) => flists.insert(file.name, files), + Ok(files) => { + let username = file.name; + flists.insert(username.clone(), Vec::new()); + + let start = limit * (page - 1); + let end = limit * page; + if files.len() > start { + if files.len() >= end { + flists.insert(username, files[start..end].to_vec()); + } else { + flists.insert(username, files[start..].to_vec()); + } + } + } Err(e) => { log::error!("failed to list flists per username with error: {}", e); return Err(ResponseError::InternalServerError); diff --git a/fl-server/src/response.rs b/fl-server/src/response.rs index 9169fc6..afae01f 100644 --- a/fl-server/src/response.rs +++ b/fl-server/src/response.rs @@ -124,7 +124,7 @@ impl IntoResponse for ResponseResult { //////// TEMPLATES //////// -#[derive(Serialize, ToSchema)] +#[derive(Serialize, Clone, Debug, ToSchema)] pub struct FileInfo { pub name: String, pub path_uri: String, diff --git a/fl-server/src/serve_flists.rs b/fl-server/src/serve_flists.rs index da1c19c..65fc4d1 100644 --- a/fl-server/src/serve_flists.rs +++ b/fl-server/src/serve_flists.rs @@ -14,6 +14,7 @@ use percent_encoding::percent_decode; use crate::{ config, + handlers::Filter, response::{ DirListTemplate, DirLister, ErrorTemplate, FileInfo, ResponseError, ResponseResult, TemplateErr, @@ -47,7 +48,7 @@ pub async fn serve_flists( match cur_path.is_dir() { true => { - let rs = visit_dir_one_level(&full_path, &state).await; + let rs = visit_dir_one_level(&full_path, &state, None).await; match rs { Ok(files) => Ok(ResponseResult::DirTemplate(DirListTemplate { lister: DirLister { files }, @@ -98,6 +99,7 @@ fn validate_path(path: &str) -> io::Result { pub async fn visit_dir_one_level>( path: P, state: &Arc, + filter: Option, ) -> io::Result> { let path = path.as_ref(); let mut dir = tokio::fs::read_dir(path).await?; @@ -107,6 +109,7 @@ pub async fn visit_dir_one_level>( let path_uri = child.path().to_string_lossy().to_string(); let is_file = child.file_type().await?.is_file(); let name = child.file_name().to_string_lossy().to_string(); + let size = child.metadata().await?.len(); let mut progress = 0.0; if is_file { @@ -131,11 +134,31 @@ pub async fn visit_dir_one_level>( } } + if let Some(ref filter_files) = filter { + if let Some(ref filter_name) = filter_files.name { + if filter_name.clone() != name { + continue; + } + } + + if let Some(ref filter_max_size) = filter_files.max_size { + if filter_max_size.clone() < size as usize { + continue; + } + } + + if let Some(ref filter_min_size) = filter_files.min_size { + if filter_min_size.clone() > size as usize { + continue; + } + } + } + files.push(FileInfo { name, path_uri, is_file, - size: child.metadata().await?.len(), + size: size, last_modified: child .metadata() .await?