Get fs directories
This commit is contained in:
@@ -28,6 +28,7 @@ server:
|
|||||||
enable: true
|
enable: true
|
||||||
allow_origins:
|
allow_origins:
|
||||||
- "http://localhost:3000"
|
- "http://localhost:3000"
|
||||||
|
- "http://localhost:3001"
|
||||||
allow_credentials: true
|
allow_credentials: true
|
||||||
max_age: 3600
|
max_age: 3600
|
||||||
allow_headers:
|
allow_headers:
|
||||||
|
Binary file not shown.
@@ -47,6 +47,7 @@ impl Hooks for App {
|
|||||||
|
|
||||||
fn routes(_ctx: &AppContext) -> AppRoutes {
|
fn routes(_ctx: &AppContext) -> AppRoutes {
|
||||||
AppRoutes::with_default_routes() // controller routes below
|
AppRoutes::with_default_routes() // controller routes below
|
||||||
|
.add_route(controllers::fs::routes())
|
||||||
.add_route(controllers::musicbrainz::routes())
|
.add_route(controllers::musicbrainz::routes())
|
||||||
.add_route(controllers::music_file::routes())
|
.add_route(controllers::music_file::routes())
|
||||||
.add_route(controllers::music_library::routes())
|
.add_route(controllers::music_library::routes())
|
||||||
|
18
src/controllers/fs.rs
Normal file
18
src/controllers/fs.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#![allow(clippy::missing_errors_doc)]
|
||||||
|
#![allow(clippy::unnecessary_struct_initialization)]
|
||||||
|
#![allow(clippy::unused_async)]
|
||||||
|
use loco_rs::prelude::*;
|
||||||
|
|
||||||
|
use crate::services::fs::get_readable_writable_dirs;
|
||||||
|
|
||||||
|
pub async fn get_fs_directories() -> Result<Response> {
|
||||||
|
let dirs = get_readable_writable_dirs(std::path::Path::new("/"));
|
||||||
|
|
||||||
|
format::json(dirs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Routes {
|
||||||
|
Routes::new()
|
||||||
|
.prefix("api/fs/")
|
||||||
|
.add("/directories", get(get_fs_directories))
|
||||||
|
}
|
@@ -3,3 +3,4 @@ pub mod auth;
|
|||||||
pub mod music_library;
|
pub mod music_library;
|
||||||
pub mod music_file;
|
pub mod music_file;
|
||||||
pub mod musicbrainz;
|
pub mod musicbrainz;
|
||||||
|
pub mod fs;
|
81
src/services/fs.rs
Normal file
81
src/services/fs.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{self, OpenOptions},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
|
const DEPTH: usize = 5;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Directory {
|
||||||
|
name: String,
|
||||||
|
path: String,
|
||||||
|
parent: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_readable_and_writable(path: &Path) -> bool {
|
||||||
|
if fs::read_dir(path).is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let test_file = path.join(".test_rw_check");
|
||||||
|
match OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(&test_file)
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
let _ = fs::remove_file(&test_file);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_hidden(entry: &DirEntry) -> bool {
|
||||||
|
let file_name_hidden = entry
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.map(|s| s.starts_with('.'))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use std::os::windows::fs::MetadataExt;
|
||||||
|
use winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN;
|
||||||
|
|
||||||
|
if let Ok(metadata) = entry.metadata() {
|
||||||
|
let attrs = metadata.file_attributes();
|
||||||
|
return file_name_hidden || (attrs & FILE_ATTRIBUTE_HIDDEN != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_name_hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_readable_writable_dirs(root: &Path) -> Vec<Directory> {
|
||||||
|
let mut dirs = Vec::new();
|
||||||
|
|
||||||
|
for entry in WalkDir::new(root)
|
||||||
|
.follow_links(false)
|
||||||
|
.max_depth(DEPTH)
|
||||||
|
.into_iter()
|
||||||
|
.filter_entry(|e| !is_hidden(e))
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.filter(|e| e.file_type().is_dir())
|
||||||
|
{
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
if is_readable_and_writable(path) {
|
||||||
|
dirs.push(Directory {
|
||||||
|
name: entry.file_name().to_string_lossy().to_string(),
|
||||||
|
path: path.to_string_lossy().to_string(),
|
||||||
|
parent: path.parent().map(|p| p.to_string_lossy().to_string()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs
|
||||||
|
}
|
@@ -1,2 +1,3 @@
|
|||||||
|
pub mod fs;
|
||||||
pub mod musicbrainz;
|
pub mod musicbrainz;
|
||||||
pub mod suggestion;
|
pub mod suggestion;
|
||||||
|
17
tests/requests/fs.rs
Normal file
17
tests/requests/fs.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use music_metadata_manager::app::App;
|
||||||
|
use loco_rs::testing::prelude::*;
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn can_get_fs() {
|
||||||
|
request::<App, _, _>(|request, _ctx| async move {
|
||||||
|
let res = request.get("/api/fs/").await;
|
||||||
|
assert_eq!(res.status_code(), 200);
|
||||||
|
|
||||||
|
// you can assert content like this:
|
||||||
|
// assert_eq!(res.text(), "content");
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
@@ -4,3 +4,4 @@ mod prepare_data;
|
|||||||
pub mod music_library;
|
pub mod music_library;
|
||||||
pub mod music_file;
|
pub mod music_file;
|
||||||
pub mod musicbrainz;
|
pub mod musicbrainz;
|
||||||
|
pub mod fs;
|
Reference in New Issue
Block a user