Get fs directories
Some checks failed
CI / Check Style (push) Failing after 26s
CI / Run Clippy (push) Failing after 5m6s
CI / Run Tests (push) Failing after 11m55s

This commit is contained in:
2025-07-28 03:38:54 +02:00
parent 275fc0c2be
commit 75ca414ef1
9 changed files with 123 additions and 2 deletions

View File

@@ -28,6 +28,7 @@ server:
enable: true
allow_origins:
- "http://localhost:3000"
- "http://localhost:3001"
allow_credentials: true
max_age: 3600
allow_headers:

View File

@@ -47,6 +47,7 @@ impl Hooks for App {
fn routes(_ctx: &AppContext) -> AppRoutes {
AppRoutes::with_default_routes() // controller routes below
.add_route(controllers::fs::routes())
.add_route(controllers::musicbrainz::routes())
.add_route(controllers::music_file::routes())
.add_route(controllers::music_library::routes())

18
src/controllers/fs.rs Normal file
View 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))
}

View File

@@ -2,4 +2,5 @@ pub mod auth;
pub mod music_library;
pub mod music_file;
pub mod musicbrainz;
pub mod musicbrainz;
pub mod fs;

81
src/services/fs.rs Normal file
View 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
}

View File

@@ -1,2 +1,3 @@
pub mod fs;
pub mod musicbrainz;
pub mod suggestion;

17
tests/requests/fs.rs Normal file
View 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;
}

View File

@@ -3,4 +3,5 @@ mod prepare_data;
pub mod music_library;
pub mod music_file;
pub mod musicbrainz;
pub mod musicbrainz;
pub mod fs;