feat(serve): add list directory

This commit is contained in:
xfy
2025-06-18 20:41:01 +08:00
parent 6bb543a9bd
commit 0c925f289b
3 changed files with 78 additions and 10 deletions

View File

@ -26,6 +26,9 @@ location = "/"
root = "./html"
# Only use for root field
index = ["index.html"]
# List directory
auto_index = true
# Custom 500 page
[host.route.error_page]
status = 500

View File

@ -23,6 +23,10 @@ pub struct SettingRoute {
pub location: String,
/// The static assets root folder
pub root: Option<String>,
/// List directory
#[serde(default = "default_disabled")]
pub auto_index: bool,
/// Index files format
#[serde(default = "host_index")]
pub index: Vec<String>,
@ -124,10 +128,6 @@ mod tests {
let path = file.path().to_str().unwrap();
let settings = Settings::new(path).unwrap();
// Verify default values
assert_eq!(settings.default_type, "text/plain");
assert_eq!(settings.types.len(), 2);
// Verify host settings
let host = &settings.host[0];
assert_eq!(host.ip, "127.0.0.1");

View File

@ -1,4 +1,8 @@
use std::{path::PathBuf, str::FromStr, time::UNIX_EPOCH};
use std::{
path::PathBuf,
str::FromStr,
time::{SystemTime, UNIX_EPOCH},
};
use anyhow::{Context, anyhow};
use axum::{
@ -152,10 +156,24 @@ pub async fn serve(
break;
}
}
let Some(path_exists) = path_exists else {
debug!("No valid file found in path candidates");
return custom_not_found!(host_route, request, false).await;
let path_exists = match path_exists {
Some(path_exists) => path_exists,
None => {
if host_route.auto_index {
let root_path = PathBuf::from(root);
let list = list_dir(&root_path).await?;
debug!("list: {:?}", list);
todo!()
} else {
debug!("No valid file found in path candidates");
return custom_not_found!(host_route, request, false).await;
}
}
};
// let Some(path_exists) = path_exists else {
// debug!("No valid file found in path candidates");
// return custom_not_found!(host_route, request, false).await;
// };
match stream_file(path_exists.into(), request, None).await {
Ok(res) => Ok(res),
Err(e) => {
@ -229,8 +247,6 @@ async fn stream_file(
} else {
ReaderStream::new(file)
};
// let stream = stream.map(|res| res.map(Frame::data));
// let body = StreamBody::new(stream);
let body = Body::from_stream(stream);
let mime = from_path(path).first_or_octet_stream();
@ -315,3 +331,52 @@ pub fn resolve_parent_path(uri: &Uri, path: Option<&Path<String>>) -> String {
}
}
}
#[derive(Debug, Clone)]
pub struct DirList {
pub name: String,
pub path: PathBuf,
pub is_dir: bool,
pub size: u64,
pub last_modified: SystemTime,
}
async fn list_dir(path: &PathBuf) -> anyhow::Result<Vec<DirList>> {
let mut list = vec![];
let mut entries = fs::read_dir(path)
.await
.with_context(|| format!("无法读取目录: {}", path.display()))?;
let mut tasks = vec![];
while let Some(entry) = entries
.next_entry()
.await
.with_context(|| format!("读取目录条目失败: {}", path.display()))?
{
let task = tokio::task::spawn(async move {
let metadata = entry
.metadata()
.await
.with_context(|| "get file metadata failed")?;
let last_modified = metadata
.modified()
.with_context(|| "get file modified failed")?;
let size = metadata.len();
let is_dir = metadata.is_dir();
let name = entry.file_name().to_string_lossy().to_string();
let dir = DirList {
name,
path: entry.path(),
is_dir,
size,
last_modified,
};
anyhow::Ok(dir)
});
tasks.push(task);
}
for task in tasks {
list.push(task.await??);
}
Ok(list)
}