From 70f5dd7b5b18c2b54ad751bec499702b6488cd3a Mon Sep 17 00:00:00 2001 From: jb-alvarado Date: Fri, 1 Jul 2022 15:45:24 +0200 Subject: [PATCH] work on better path response, add dir route --- ffplayout-api/src/main.rs | 11 +++--- ffplayout-api/src/utils/files.rs | 65 +++++++++++++++++++++++++++---- ffplayout-api/src/utils/routes.rs | 17 +++++++- 3 files changed, 79 insertions(+), 14 deletions(-) diff --git a/ffplayout-api/src/main.rs b/ffplayout-api/src/main.rs index 4ff9f0dc..dd021257 100644 --- a/ffplayout-api/src/main.rs +++ b/ffplayout-api/src/main.rs @@ -15,11 +15,11 @@ use utils::{ auth, db_path, init_config, models::LoginUser, routes::{ - add_preset, add_user, del_playlist, file_browser, gen_playlist, get_all_settings, get_log, - get_playlist, get_playout_config, get_presets, get_settings, get_user, jump_to_last, - jump_to_next, login, media_current, media_last, media_next, move_rename, patch_settings, - process_control, remove, reset_playout, save_file, save_playlist, send_text_message, - update_playout_config, update_preset, update_user, + add_dir, add_preset, add_user, del_playlist, file_browser, gen_playlist, get_all_settings, + get_log, get_playlist, get_playout_config, get_presets, get_settings, get_user, + jump_to_last, jump_to_next, login, media_current, media_last, media_next, move_rename, + patch_settings, process_control, remove, reset_playout, save_file, save_playlist, + send_text_message, update_playout_config, update_preset, update_user, }, run_args, Role, }; @@ -101,6 +101,7 @@ async fn main() -> std::io::Result<()> { .service(del_playlist) .service(get_log) .service(file_browser) + .service(add_dir) .service(move_rename) .service(remove) .service(save_file), diff --git a/ffplayout-api/src/utils/files.rs b/ffplayout-api/src/utils/files.rs index f4a3e4dd..75a96482 100644 --- a/ffplayout-api/src/utils/files.rs +++ b/ffplayout-api/src/utils/files.rs @@ -19,6 +19,7 @@ use ffplayout_lib::utils::file_extension; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct PathObject { pub source: String, + parent: Option, folders: Option>, files: Option>, } @@ -27,6 +28,7 @@ impl PathObject { fn new(source: String) -> Self { Self { source, + parent: None, folders: Some(vec![]), files: Some(vec![]), } @@ -39,21 +41,52 @@ pub struct MoveObject { target: String, } -pub async fn browser(id: i64, path_obj: &PathObject) -> Result { - let (config, _) = playout_config(&id).await?; - let path = PathBuf::from(config.storage.path); - let extensions = config.storage.extensions; - let path_component = RelativePath::new(&path_obj.source) +/// Normalize absolut path +/// +/// This function takes care, that it is not possible to break out from root_path. +/// It also gives alway a relative path back. +fn norm_abs_path(root_path: String, path_obj: &PathObject) -> (PathBuf, String, String) { + let mut path = PathBuf::from(root_path.clone()); + let path_relative = RelativePath::new(&root_path) .normalize() .to_string() .replace("../", ""); - let path = path.join(path_component.clone()); - let mut obj = PathObject::new(path_component.clone()); + let mut source_relative = RelativePath::new(&path_obj.source) + .normalize() + .to_string() + .replace("../", ""); + let path_suffix = path.file_name().unwrap().to_string_lossy().to_string(); + + if path_obj.source.starts_with(&root_path) || source_relative.starts_with(&path_relative) { + source_relative = source_relative + .strip_prefix(&path_relative) + .and_then(|s| s.strip_prefix('/')) + .unwrap_or_default() + .to_string(); + } else { + source_relative = source_relative + .strip_prefix(&path_suffix) + .and_then(|s| s.strip_prefix('/')) + .unwrap_or(&source_relative) + .to_string(); + } + + path = path.join(&source_relative); + + (path, path_suffix, source_relative) +} + +pub async fn browser(id: i64, path_obj: &PathObject) -> Result { + let (config, _) = playout_config(&id).await?; + let extensions = config.storage.extensions; + let (path, parent, path_component) = norm_abs_path(config.storage.path, path_obj); + let mut obj = PathObject::new(path_component); + obj.parent = Some(parent); let mut paths: Vec<_> = match fs::read_dir(path) { Ok(p) => p.filter_map(|r| r.ok()).collect(), Err(e) => { - error!("{e} in {path_component}"); + error!("{e} in {}", path_obj.source); return Err(ServiceError::InternalServerError); } }; @@ -87,6 +120,22 @@ pub async fn browser(id: i64, path_obj: &PathObject) -> Result Result { + let (config, _) = playout_config(&id).await?; + let (path, _, _) = norm_abs_path(config.storage.path, path_obj); + + if let Err(e) = fs::create_dir_all(&path) { + return Err(ServiceError::BadRequest(e.to_string())); + } + + info!("create folder: {}", path.display()); + + Ok(HttpResponse::Ok().into()) +} + // fn copy_and_delete(source: &PathBuf, target: &PathBuf) -> Result { // match fs::copy(&source, &target) { // Ok(_) => { diff --git a/ffplayout-api/src/utils/routes.rs b/ffplayout-api/src/utils/routes.rs index 85403491..b4e1a594 100644 --- a/ffplayout-api/src/utils/routes.rs +++ b/ffplayout-api/src/utils/routes.rs @@ -14,7 +14,10 @@ use crate::utils::{ auth::{create_jwt, Claims}, control::{control_service, control_state, media_info, send_message, Process}, errors::ServiceError, - files::{browser, remove_file_or_folder, rename_file, upload, MoveObject, PathObject}, + files::{ + browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject, + PathObject, + }, handles::{ db_add_preset, db_add_user, db_get_all_settings, db_get_presets, db_get_settings, db_get_user, db_login, db_role, db_update_preset, db_update_settings, db_update_user, @@ -481,6 +484,18 @@ pub async fn file_browser( } } +/// curl -X POST http://localhost:8080/api/file/1/create-folder/ +/// --header 'Content-Type: application/json' --header 'Authorization: ' +/// -d '{"source": ""}' +#[post("/file/{id}/create-folder/")] +#[has_any_role("Role::Admin", "Role::User", type = "Role")] +pub async fn add_dir( + id: web::Path, + data: web::Json, +) -> Result { + create_directory(*id, &data.into_inner()).await +} + /// curl -X POST http://localhost:8080/api/file/1/move/ /// --header 'Content-Type: application/json' --header 'Authorization: ' /// -d '{"source": "", "target": ""}'