validate file version on upload
This commit is contained in:
parent
50d0f6dfa5
commit
c239819376
@ -1,3 +1,4 @@
|
|||||||
|
use crate::db::repository::models::FileModel;
|
||||||
use crate::db::repository::{insert, Repository};
|
use crate::db::repository::{insert, Repository};
|
||||||
use crate::errors::to_internal_error;
|
use crate::errors::to_internal_error;
|
||||||
use crate::files::PathInfo;
|
use crate::files::PathInfo;
|
||||||
@ -17,6 +18,7 @@ use std::io::Error;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use tokio::fs::{create_dir_all, File};
|
use tokio::fs::{create_dir_all, File};
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
pub async fn handle_file_uploads(
|
pub async fn handle_file_uploads(
|
||||||
@ -46,13 +48,17 @@ async fn handle_single_file_upload<'field_lifetime>(
|
|||||||
None | Some("") => Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
None | Some("") => Err(ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||||
.with_detail(errors::ERROR_DETAILS_NO_NAME_PROVIDED_MULTIPART_FIELD)),
|
.with_detail(errors::ERROR_DETAILS_NO_NAME_PROVIDED_MULTIPART_FIELD)),
|
||||||
Some(field_name) => {
|
Some(field_name) => {
|
||||||
let path = PathBuf::from(field_name);
|
let parsed = UploadParameter::from_string(field_name)?;
|
||||||
let path_info = PathInfo::build(&state.config.files.directory, user_id, &path)?;
|
let path_info = PathInfo::build(&state.config.files.directory, user_id, &parsed.path)?;
|
||||||
let lock_id = &build_lock_id(user_id, &path_info.relative_user_location);
|
let lock_id = &build_lock_id(user_id, &path_info.relative_user_location);
|
||||||
if state
|
if state.get_lock().lock(lock_id).await {
|
||||||
.get_lock()
|
validate_file_version(
|
||||||
.lock(lock_id).await
|
state.get_repository(),
|
||||||
{
|
user_id,
|
||||||
|
&path_info.relative_user_location,
|
||||||
|
&parsed.version,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let result = update_file(state, user_id, field, &path_info).await;
|
let result = update_file(state, user_id, field, &path_info).await;
|
||||||
state.get_lock().unlock(lock_id).await;
|
state.get_lock().unlock(lock_id).await;
|
||||||
result
|
result
|
||||||
@ -64,6 +70,71 @@ async fn handle_single_file_upload<'field_lifetime>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn validate_file_version(
|
||||||
|
repo: &impl Repository,
|
||||||
|
user_id: &str,
|
||||||
|
path: &Path,
|
||||||
|
version: &u64,
|
||||||
|
) -> Result<(), ProblemDetails> {
|
||||||
|
match repo.get_file(user_id, &path.to_string_lossy()).await? {
|
||||||
|
None => validate_initial_version(version)?,
|
||||||
|
Some(file_model) => validate_version_increment(&file_model, version)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_version_increment(model: &FileModel, version: &u64) -> Result<(), ProblemDetails> {
|
||||||
|
if model.version + 1 != *version {
|
||||||
|
return Err(
|
||||||
|
ProblemDetails::from_status_code(StatusCode::CONFLICT).with_detail(format!(
|
||||||
|
"Version does not match. Provided {} is not one higher than existing {}",
|
||||||
|
version, model.version
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_initial_version(version: &u64) -> Result<(), ProblemDetails> {
|
||||||
|
if *version != 0 {
|
||||||
|
return Err(
|
||||||
|
ProblemDetails::from_status_code(StatusCode::BAD_REQUEST).with_detail(format!(
|
||||||
|
"Version for new files must be 0. Provided version {}",
|
||||||
|
version
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UploadParameter {
|
||||||
|
path: PathBuf,
|
||||||
|
version: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UploadParameter {
|
||||||
|
fn from_string(name: &str) -> Result<Self, ProblemDetails> {
|
||||||
|
let mut split = name.split(';');
|
||||||
|
let path = split.next().ok_or_else(build_wrong_field_name_error)?;
|
||||||
|
let version: u64 = split
|
||||||
|
.next()
|
||||||
|
.ok_or_else(build_wrong_field_name_error)?
|
||||||
|
.parse()
|
||||||
|
.map_err(|err| {
|
||||||
|
debug!("Error while parsing version: {}", err);
|
||||||
|
ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||||
|
.with_detail("Version cannot be parsed as a number")
|
||||||
|
})?;
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
Ok(Self { path, version })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_wrong_field_name_error() -> ProblemDetails {
|
||||||
|
ProblemDetails::from_status_code(StatusCode::BAD_REQUEST)
|
||||||
|
.with_detail("Multipart field name must have the following structure: {path_to_file};{version_number + 1}. Example: folder/nested/file.pdf;15")
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_file<'field_lifetime>(
|
async fn update_file<'field_lifetime>(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user