2022-05-03 08:42:53 +00:00
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::{fs, io};
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
use crate::fs::validate_path;
|
2022-04-24 20:07:41 +00:00
|
|
|
use axum::{extract::rejection::JsonRejection, Extension, Json};
|
|
|
|
use axum_auth::AuthBearer;
|
2022-04-29 19:33:44 +00:00
|
|
|
use byte_unit::Byte;
|
2022-05-01 18:34:28 +00:00
|
|
|
use homedisk_database::Database;
|
2022-05-03 08:42:53 +00:00
|
|
|
use homedisk_types::fs::list::DirInfo;
|
2022-04-24 20:07:41 +00:00
|
|
|
use homedisk_types::{
|
2022-06-08 19:16:12 +00:00
|
|
|
config::Config,
|
2022-05-01 18:34:28 +00:00
|
|
|
errors::{FsError, ServerError},
|
2022-04-29 19:33:44 +00:00
|
|
|
fs::list::{FileInfo, Request, Response},
|
2022-04-24 20:07:41 +00:00
|
|
|
};
|
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
use crate::middleware::{find_user, validate_json, validate_jwt};
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-06-07 20:36:26 +00:00
|
|
|
/// Get directory size on disk (size of all files in directory).
|
2022-05-03 08:42:53 +00:00
|
|
|
fn dir_size(path: impl Into<PathBuf>) -> io::Result<u64> {
|
|
|
|
fn dir_size(mut dir: fs::ReadDir) -> io::Result<u64> {
|
|
|
|
dir.try_fold(0, |acc, file| {
|
|
|
|
let file = file?;
|
|
|
|
let size = match file.metadata()? {
|
|
|
|
data if data.is_dir() => dir_size(fs::read_dir(file.path())?)?,
|
|
|
|
data => data.len(),
|
|
|
|
};
|
|
|
|
Ok(acc + size)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
dir_size(fs::read_dir(path.into())?)
|
|
|
|
}
|
|
|
|
|
2022-06-08 17:08:06 +00:00
|
|
|
/// Handle `/fs/list` requests
|
2022-04-24 20:07:41 +00:00
|
|
|
pub async fn handle(
|
2022-05-01 18:34:28 +00:00
|
|
|
Extension(db): Extension<Database>,
|
|
|
|
Extension(config): Extension<Config>,
|
2022-04-24 20:07:41 +00:00
|
|
|
AuthBearer(token): AuthBearer,
|
|
|
|
request: Result<Json<Request>, JsonRejection>,
|
|
|
|
) -> Result<Json<Response>, ServerError> {
|
2022-06-07 20:36:26 +00:00
|
|
|
// validate json request
|
2022-04-24 20:07:41 +00:00
|
|
|
let Json(request) = validate_json::<Request>(request)?;
|
2022-06-07 20:36:26 +00:00
|
|
|
|
|
|
|
// validate user token
|
2022-04-24 20:07:41 +00:00
|
|
|
let token = validate_jwt(config.jwt.secret.as_bytes(), &token)?;
|
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
// validate the `path` can be used
|
|
|
|
validate_path(&request.path)?;
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
// search for a user by UUID from a token
|
|
|
|
let user = find_user(db, token.claims.sub).await?;
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
// directory where the file will be placed
|
|
|
|
let path = format!(
|
|
|
|
"{user_dir}/{req_dir}",
|
|
|
|
user_dir = user.user_dir(&config.storage.path),
|
|
|
|
req_dir = request.path
|
|
|
|
);
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-06-07 20:36:26 +00:00
|
|
|
// get paths from dir
|
2022-05-01 18:34:28 +00:00
|
|
|
let paths = fs::read_dir(&path)
|
|
|
|
.map_err(|err| ServerError::FsError(FsError::ReadDir(err.to_string())))?;
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
let mut files = vec![];
|
|
|
|
let mut dirs = vec![];
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
for f in paths {
|
2022-06-07 20:36:26 +00:00
|
|
|
// handle Error
|
2022-05-07 13:19:36 +00:00
|
|
|
let f = f.map_err(|err| ServerError::FsError(FsError::UnknownError(err.to_string())))?;
|
2022-06-07 20:36:26 +00:00
|
|
|
|
|
|
|
// get path metadata
|
2022-05-01 18:34:28 +00:00
|
|
|
let metadata = f
|
|
|
|
.metadata()
|
2022-05-07 13:19:36 +00:00
|
|
|
.map_err(|err| ServerError::FsError(FsError::UnknownError(err.to_string())))?;
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-06-07 20:36:26 +00:00
|
|
|
// get name of the path
|
2022-05-01 18:34:28 +00:00
|
|
|
let name = f.path().display().to_string().replace(&path, "");
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-06-07 20:36:26 +00:00
|
|
|
// if path is directory
|
2022-05-01 18:34:28 +00:00
|
|
|
if metadata.is_dir() {
|
2022-06-07 20:36:26 +00:00
|
|
|
let size = Byte::from_bytes(
|
|
|
|
dir_size(f.path().display().to_string())
|
|
|
|
.map_err(|err| ServerError::FsError(FsError::UnknownError(err.to_string())))?
|
|
|
|
.into(),
|
|
|
|
)
|
|
|
|
.get_appropriate_unit(true)
|
|
|
|
.to_string();
|
2022-05-03 09:26:29 +00:00
|
|
|
|
|
|
|
dirs.push(DirInfo { name, size })
|
2022-06-07 20:36:26 +00:00
|
|
|
}
|
|
|
|
// if path is file
|
|
|
|
else {
|
|
|
|
// get file size in bytes
|
2022-05-03 09:26:29 +00:00
|
|
|
let size = Byte::from_bytes(metadata.len().into())
|
|
|
|
.get_appropriate_unit(true)
|
|
|
|
.to_string();
|
|
|
|
|
2022-06-07 20:36:26 +00:00
|
|
|
// check how long it has been since the file was last modified
|
2022-05-03 09:26:29 +00:00
|
|
|
let elapsed = metadata.modified().unwrap().elapsed().unwrap();
|
|
|
|
|
|
|
|
let seconds = elapsed.as_secs();
|
|
|
|
let minutes = seconds / 60;
|
|
|
|
let hours = minutes / 60;
|
|
|
|
let days = hours / 24;
|
|
|
|
|
|
|
|
let modified;
|
|
|
|
|
2022-06-07 20:36:26 +00:00
|
|
|
// format elapsed time
|
2022-05-03 09:26:29 +00:00
|
|
|
if days > 1 {
|
|
|
|
modified = format!("{} day(s)", days)
|
|
|
|
} else if hours > 1 {
|
|
|
|
modified = format!("{} hour(s)", hours)
|
|
|
|
} else if minutes > 1 {
|
|
|
|
modified = format!("{} minute(s)", minutes)
|
|
|
|
} else {
|
|
|
|
modified = format!("{} second(s)", seconds)
|
|
|
|
}
|
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
files.push(FileInfo {
|
|
|
|
name,
|
2022-05-03 09:26:29 +00:00
|
|
|
size,
|
|
|
|
modified,
|
2022-05-01 18:34:28 +00:00
|
|
|
})
|
2022-05-01 17:49:50 +00:00
|
|
|
}
|
2022-05-01 18:34:28 +00:00
|
|
|
}
|
2022-04-24 20:07:41 +00:00
|
|
|
|
2022-05-01 18:34:28 +00:00
|
|
|
Ok(Json(Response { files, dirs }))
|
2022-04-24 20:07:41 +00:00
|
|
|
}
|