HomeDisk/server/src/fs/upload.rs

79 lines
2.4 KiB
Rust
Raw Normal View History

2022-08-15 19:34:23 +00:00
use std::{fs, io::Write, path::Path};
2022-04-24 19:31:50 +00:00
2022-07-02 10:36:02 +00:00
use axum::{
extract::{Multipart, Query},
Extension,
};
2022-04-24 19:31:50 +00:00
use axum_auth::AuthBearer;
use futures::TryStreamExt;
2022-05-01 18:34:28 +00:00
use homedisk_database::Database;
2022-04-24 19:31:50 +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-06-11 08:19:47 +00:00
fs::upload::Pagination,
2022-04-24 19:31:50 +00:00
};
2022-07-25 21:02:25 +00:00
use crate::middleware::{find_user, validate_jwt, validate_path};
2022-06-16 14:58:37 +00:00
2022-04-24 19:31:50 +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 19:31:50 +00:00
AuthBearer(token): AuthBearer,
mut multipart: Multipart,
query: Query<Pagination>,
2022-06-11 08:19:47 +00:00
) -> Result<(), ServerError> {
2022-06-07 20:36:26 +00:00
// validate user token
2022-04-24 19:31:50 +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(&query.path)?;
2022-04-24 19:31:50 +00:00
2022-05-01 18:34:28 +00:00
// search for a user by UUID from a token
2022-06-14 20:37:42 +00:00
let user = find_user(&db, &token.claims.sub).await?;
2022-04-24 19:31:50 +00:00
// path to the file
let file_path = format!(
"{user_dir}/{request_path}",
2022-05-01 18:34:28 +00:00
user_dir = user.user_dir(&config.storage.path),
request_path = query.path
2022-05-01 18:34:28 +00:00
);
let file_path = Path::new(&file_path);
2022-04-24 19:31:50 +00:00
2022-05-01 18:34:28 +00:00
// check if the file currently exists to avoid overwriting it
if file_path.exists() {
2022-05-01 18:34:28 +00:00
return Err(ServerError::FsError(FsError::FileAlreadyExists));
}
2022-04-24 19:31:50 +00:00
2022-05-01 18:34:28 +00:00
// create a directory where the file will be placed
// e.g. path ==> `/secret/files/images/screenshot.png`
2022-06-11 08:19:47 +00:00
// directories up to `{storage dir}/{username}/secret/files/images/` will be created
if let Some(prefix) = file_path.parent() {
fs::create_dir_all(&prefix)
.map_err(|err| ServerError::FsError(FsError::CreateFile(err.to_string())))?
2022-05-01 18:34:28 +00:00
}
2022-04-24 19:31:50 +00:00
// get multipart field
let field = multipart
.next_field()
.await
.map_err(|_| ServerError::FsError(FsError::MultipartError))?
.ok_or(ServerError::FsError(FsError::MultipartError))?;
// create file
let file = std::fs::File::create(&file_path)
.map_err(|err| ServerError::FsError(FsError::CreateFile(err.to_string())))?;
2022-05-01 18:34:28 +00:00
// write file
field
.try_fold((file, 0u64), |(mut file, written_len), bytes| async move {
file.write_all(bytes.as_ref()).expect("write file error");
Ok((file, written_len + bytes.len() as u64))
})
.await
2022-05-01 18:34:28 +00:00
.map_err(|err| ServerError::FsError(FsError::WriteFile(err.to_string())))?;
2022-04-24 19:31:50 +00:00
2022-07-25 21:02:25 +00:00
// send an empty response
2022-06-11 08:19:47 +00:00
Ok(())
2022-04-24 19:31:50 +00:00
}