Error handling
This commit is contained in:
parent
8a4e2dc467
commit
9f76228ebe
@ -16,7 +16,7 @@ use rand::{rngs::OsRng, RngCore};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
|
|
||||||
use crate::{db, errors::handle_error, Pool};
|
use crate::{db, Pool};
|
||||||
|
|
||||||
pub const HASH_LENGTH: usize = 64;
|
pub const HASH_LENGTH: usize = 64;
|
||||||
pub const SALT_LENGTH: usize = 64;
|
pub const SALT_LENGTH: usize = 64;
|
||||||
@ -186,7 +186,7 @@ where
|
|||||||
Ok(true) => Ok(claims),
|
Ok(true) => Ok(claims),
|
||||||
Ok(false) => Err(Error::WrongCredentials),
|
Ok(false) => Err(Error::WrongCredentials),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
handle_error(err);
|
tracing::error!(%err);
|
||||||
Err(Error::Validation)
|
Err(Error::Validation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,41 +32,39 @@ impl From<Option<PermissionRaw>> for PermissionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PermissionType> for PermissionRaw {
|
|
||||||
fn from(value: PermissionType) -> Self {
|
|
||||||
match value {
|
|
||||||
PermissionType::Manage => Self::Manage,
|
|
||||||
PermissionType::Write => Self::Write,
|
|
||||||
PermissionType::Read => Self::Read,
|
|
||||||
PermissionType::NoPermission => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PermissionType {
|
impl PermissionType {
|
||||||
pub fn can_read(self) -> bool {
|
pub fn can_read(self) -> bool {
|
||||||
self >= PermissionType::Read
|
self >= PermissionType::Read
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_read_guard(self) -> Result<(), StatusCode> {
|
pub fn can_read_guard(self) -> GeneralResult<()> {
|
||||||
if !self.can_read() {
|
if !self.can_read() {
|
||||||
return Err(StatusCode::NOT_FOUND);
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
"Item not found",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_write_guard(self) -> Result<(), StatusCode> {
|
pub fn can_write_guard(self) -> GeneralResult<()> {
|
||||||
self.can_read_guard()?;
|
self.can_read_guard()?;
|
||||||
if self < PermissionType::Write {
|
if self < PermissionType::Write {
|
||||||
return Err(StatusCode::FORBIDDEN);
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::FORBIDDEN,
|
||||||
|
"Cannot write to the folder",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_manage_guard(self) -> Result<(), StatusCode> {
|
pub fn can_manage_guard(self) -> GeneralResult<()> {
|
||||||
self.can_read_guard()?;
|
self.can_read_guard()?;
|
||||||
if self < PermissionType::Manage {
|
if self < PermissionType::Manage {
|
||||||
return Err(StatusCode::FORBIDDEN);
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::FORBIDDEN,
|
||||||
|
"Cannot manage the folder",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Params {
|
pub struct Params {
|
||||||
@ -9,24 +9,27 @@ pub async fn delete(
|
|||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
) -> Result<StatusCode, StatusCode> {
|
) -> GeneralResult<StatusCode> {
|
||||||
db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
|
db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_write_guard()?;
|
.can_write_guard()?;
|
||||||
|
|
||||||
let deleted = db::file::delete(params.file_id, &state.pool)
|
let deleted = db::file::delete(params.file_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error deleting the file")?;
|
||||||
if !deleted {
|
if !deleted {
|
||||||
return Err(StatusCode::NOT_FOUND); // Will not happen most of the time due to can write guard
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
"Item not found",
|
||||||
|
)); // Will not happen most of the time due to can write guard
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
.storage
|
.storage
|
||||||
.delete(params.file_id)
|
.delete(params.file_id)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error deleting the file")?;
|
||||||
|
|
||||||
Ok(StatusCode::NO_CONTENT)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,16 @@ pub async fn download(
|
|||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
) -> Result<impl IntoResponse, StatusCode> {
|
) -> GeneralResult<impl IntoResponse> {
|
||||||
db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
|
db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_read_guard()?;
|
.can_read_guard()?;
|
||||||
|
|
||||||
let mut name = db::file::get_name(params.file_id, &state.pool)
|
let mut name = db::file::get_name(params.file_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.handle_internal("Error getting file info")?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.ok_or_else(GeneralError::item_not_found)?;
|
||||||
name = name
|
name = name
|
||||||
.chars()
|
.chars()
|
||||||
.fold(String::with_capacity(name.len()), |mut result, char| {
|
.fold(String::with_capacity(name.len()), |mut result, char| {
|
||||||
@ -32,7 +32,11 @@ pub async fn download(
|
|||||||
result
|
result
|
||||||
});
|
});
|
||||||
|
|
||||||
let file = state.storage.read(params.file_id).await.handle_internal()?;
|
let file = state
|
||||||
|
.storage
|
||||||
|
.read(params.file_id)
|
||||||
|
.await
|
||||||
|
.handle_internal("Error reading the file")?;
|
||||||
let body = Body::from_stream(ReaderStream::new(file));
|
let body = Body::from_stream(ReaderStream::new(file));
|
||||||
let disposition = format!("attachment; filename=\"{name}\"");
|
let disposition = format!("attachment; filename=\"{name}\"");
|
||||||
let headers = [(header::CONTENT_DISPOSITION, disposition)];
|
let headers = [(header::CONTENT_DISPOSITION, disposition)];
|
||||||
|
@ -12,10 +12,10 @@ pub async fn modify(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
mut multipart: Multipart,
|
mut multipart: Multipart,
|
||||||
) -> Result<StatusCode, StatusCode> {
|
) -> GeneralResult<StatusCode> {
|
||||||
db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
|
db::file::get_permissions(params.file_id, claims.user_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_write_guard()?;
|
.can_write_guard()?;
|
||||||
|
|
||||||
// Very weird work around to get the first file in multipart
|
// Very weird work around to get the first file in multipart
|
||||||
@ -23,7 +23,12 @@ pub async fn modify(
|
|||||||
match multipart.next_field().await {
|
match multipart.next_field().await {
|
||||||
Ok(Some(field)) if field.file_name().is_some() => break field,
|
Ok(Some(field)) if field.file_name().is_some() => break field,
|
||||||
Ok(Some(_)) => continue,
|
Ok(Some(_)) => continue,
|
||||||
_ => return Err(StatusCode::BAD_REQUEST),
|
_ => {
|
||||||
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"No file in the multipart",
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,19 +36,22 @@ pub async fn modify(
|
|||||||
.storage
|
.storage
|
||||||
.write(params.file_id)
|
.write(params.file_id)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.handle_internal("Error writing to the file")?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.ok_or_else(GeneralError::item_not_found)?;
|
||||||
|
|
||||||
let (hash, size) = crate::FileStorage::write_to_file(&mut file, &mut field)
|
let (hash, size) = crate::FileStorage::write_to_file(&mut file, &mut field)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
tracing::warn!(%err);
|
tracing::warn!(%err);
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
GeneralError::message(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Error writing to the file",
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
db::file::update(params.file_id, size, hash, &state.pool)
|
db::file::update(params.file_id, size, hash, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error updating the file")?;
|
||||||
|
|
||||||
Ok(StatusCode::NO_CONTENT)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
}
|
}
|
||||||
|
@ -36,16 +36,16 @@ pub async fn upload(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
mut multi: Multipart,
|
mut multi: Multipart,
|
||||||
) -> Result<Json<HashMap<String, Uuid>>, StatusCode> {
|
) -> GeneralResult<Json<HashMap<String, Uuid>>> {
|
||||||
db::folder::get_permissions(params.parent_folder, claims.user_id, &state.pool)
|
db::folder::get_permissions(params.parent_folder, claims.user_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_write_guard()?;
|
.can_write_guard()?;
|
||||||
|
|
||||||
let existing_names: HashSet<String> = db::folder::get_names(params.parent_folder, &state.pool)
|
let existing_names: HashSet<String> = db::folder::get_names(params.parent_folder, &state.pool)
|
||||||
.try_collect()
|
.try_collect()
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error getting existing names")?;
|
||||||
|
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
while let Ok(Some(mut field)) = multi.next_field().await {
|
while let Ok(Some(mut field)) = multi.next_field().await {
|
||||||
|
@ -10,22 +10,24 @@ pub async fn create(
|
|||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
Json(params): Json<Params>,
|
Json(params): Json<Params>,
|
||||||
) -> Result<Json<Uuid>, StatusCode> {
|
) -> GeneralResult<Json<Uuid>> {
|
||||||
db::folder::get_permissions(params.parent_folder_id, claims.user_id, &pool)
|
db::folder::get_permissions(params.parent_folder_id, claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_write_guard()?;
|
.can_write_guard()?;
|
||||||
|
|
||||||
let exists = db::folder::name_exists(params.parent_folder_id, ¶ms.folder_name, &pool)
|
let exists = db::folder::name_exists(params.parent_folder_id, ¶ms.folder_name, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error getting existing names")?;
|
||||||
if exists {
|
if exists {
|
||||||
return Err(StatusCode::CONFLICT);
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::CONFLICT,
|
||||||
|
"Name already taken",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = db::folder::insert(params.parent_folder_id, ¶ms.folder_name, &pool)
|
db::folder::insert(params.parent_folder_id, ¶ms.folder_name, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error creating the folder")
|
||||||
|
.map(Json)
|
||||||
Ok(Json(id))
|
|
||||||
}
|
}
|
||||||
|
@ -9,17 +9,20 @@ pub async fn delete(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
Json(params): Json<Params>,
|
Json(params): Json<Params>,
|
||||||
) -> Result<(), StatusCode> {
|
) -> GeneralResult<()> {
|
||||||
let root = db::folder::get_root(claims.user_id, &state.pool)
|
let root = db::folder::get_root(claims.user_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error getting the root folder")?;
|
||||||
if params.folder_id == root {
|
if params.folder_id == root {
|
||||||
return Err(StatusCode::BAD_REQUEST);
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Cannot delete the root folder",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool)
|
db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_write_guard()?;
|
.can_write_guard()?;
|
||||||
|
|
||||||
let storage = &state.storage;
|
let storage = &state.storage;
|
||||||
@ -29,5 +32,5 @@ pub async fn delete(
|
|||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.handle_internal()
|
.handle_internal("Error deleting the fodler")
|
||||||
}
|
}
|
||||||
|
@ -25,16 +25,16 @@ pub async fn structure(
|
|||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
) -> Result<Json<FolderStructure>, StatusCode> {
|
) -> GeneralResult<Json<FolderStructure>> {
|
||||||
let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool)
|
let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.ok_or_else(GeneralError::item_not_found)?;
|
||||||
|
|
||||||
let folder = db::folder::get_by_id(folder_id, &pool)
|
let folder = db::folder::get_by_id(folder_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.handle_internal("Error getting folder info")?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.ok_or_else(GeneralError::item_not_found)?;
|
||||||
|
|
||||||
let mut response: FolderStructure = folder.into();
|
let mut response: FolderStructure = folder.into();
|
||||||
let mut stack = vec![&mut response];
|
let mut stack = vec![&mut response];
|
||||||
@ -45,7 +45,7 @@ pub async fn structure(
|
|||||||
.map_ok(Into::into)
|
.map_ok(Into::into)
|
||||||
.try_collect()
|
.try_collect()
|
||||||
)
|
)
|
||||||
.handle_internal()?;
|
.handle_internal("Error getting folder contents")?;
|
||||||
folder.folders = folders;
|
folder.folders = folders;
|
||||||
folder.files = files;
|
folder.files = files;
|
||||||
stack.extend(folder.folders.iter_mut());
|
stack.extend(folder.folders.iter_mut());
|
||||||
|
@ -18,17 +18,17 @@ pub async fn list(
|
|||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
) -> Result<Json<Response>, StatusCode> {
|
) -> GeneralResult<Json<Response>> {
|
||||||
let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool)
|
let folder_id = db::folder::process_id(params.folder_id, claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.handle(StatusCode::NOT_FOUND, "Item not found")?;
|
||||||
|
|
||||||
let (files, folders) = try_join!(
|
let (files, folders) = try_join!(
|
||||||
db::file::get_files(folder_id, &pool).try_collect(),
|
db::file::get_files(folder_id, &pool).try_collect(),
|
||||||
db::folder::get_folders(folder_id, claims.user_id, &pool).try_collect()
|
db::folder::get_folders(folder_id, claims.user_id, &pool).try_collect()
|
||||||
)
|
)
|
||||||
.handle_internal()?;
|
.handle_internal("Error getting folder contents")?;
|
||||||
|
|
||||||
Ok(Json(Response {
|
Ok(Json(Response {
|
||||||
folder_id,
|
folder_id,
|
||||||
|
@ -10,17 +10,17 @@ pub async fn delete(
|
|||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
Json(params): Json<Params>,
|
Json(params): Json<Params>,
|
||||||
) -> Result<StatusCode, StatusCode> {
|
) -> GeneralResult<StatusCode> {
|
||||||
if params.user_id != claims.user_id {
|
if params.user_id != claims.user_id {
|
||||||
db::folder::get_permissions(params.folder_id, claims.user_id, &pool)
|
db::folder::get_permissions(params.folder_id, claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_manage_guard()?;
|
.can_manage_guard()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
db::permissions::delete_for_folder(params.folder_id, params.user_id, &pool)
|
db::permissions::delete_for_folder(params.folder_id, params.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error deleting the permissions")?;
|
||||||
|
|
||||||
Ok(StatusCode::NO_CONTENT)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,14 @@ pub async fn get(
|
|||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
) -> Result<Json<HashMap<String, PermissionRaw>>, StatusCode> {
|
) -> GeneralResult<Json<HashMap<String, PermissionRaw>>> {
|
||||||
db::folder::get_permissions(params.folder_id, claims.user_id, &pool)
|
db::folder::get_permissions(params.folder_id, claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_manage_guard()?;
|
.can_manage_guard()?;
|
||||||
|
|
||||||
let permissions = db::permissions::get_all_for_folder(params.folder_id, &pool)
|
let permissions = db::permissions::get_all_for_folder(params.folder_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error getting permissions")?;
|
||||||
Ok(Json(permissions))
|
Ok(Json(permissions))
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ use crate::prelude::*;
|
|||||||
pub async fn get_top_level(
|
pub async fn get_top_level(
|
||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
) -> Result<Json<Vec<Uuid>>, StatusCode> {
|
) -> GeneralResult<Json<Vec<Uuid>>> {
|
||||||
let folders = db::permissions::get_top_level_permitted_folders(claims.user_id, &pool)
|
let folders = db::permissions::get_top_level_permitted_folders(claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error reading from the database")?;
|
||||||
Ok(Json(folders))
|
Ok(Json(folders))
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use db::permissions::PermissionRaw;
|
use crate::{db::permissions::PermissionRaw, prelude::*};
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Params {
|
pub struct Params {
|
||||||
@ -13,25 +11,31 @@ pub async fn set(
|
|||||||
claims: Claims,
|
claims: Claims,
|
||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
Json(params): Json<Params>,
|
Json(params): Json<Params>,
|
||||||
) -> Result<StatusCode, StatusCode> {
|
) -> GeneralResult<StatusCode> {
|
||||||
let root = db::folder::get_root(claims.user_id, &pool)
|
let root = db::folder::get_root(claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error getting the root folder")?;
|
||||||
if params.folder_id == root {
|
if params.folder_id == root {
|
||||||
return Err(StatusCode::BAD_REQUEST);
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Cannot delete the root folder",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
db::folder::get_permissions(params.folder_id, claims.user_id, &pool)
|
db::folder::get_permissions(params.folder_id, claims.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.map_err(GeneralError::permissions)?
|
||||||
.can_manage_guard()?;
|
.can_manage_guard()?;
|
||||||
|
|
||||||
let folder_info = db::folder::get_by_id(params.folder_id, &pool)
|
let folder_info = db::folder::get_by_id(params.folder_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.handle_internal("Error getting folder info")?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.ok_or_else(GeneralError::item_not_found)?;
|
||||||
if folder_info.owner_id == params.user_id {
|
if folder_info.owner_id == params.user_id {
|
||||||
return Err(StatusCode::BAD_REQUEST);
|
return Err(GeneralError::message(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Cannot set permissions of the folder owner",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
db::permissions::insert(
|
db::permissions::insert(
|
||||||
@ -41,7 +45,7 @@ pub async fn set(
|
|||||||
&pool,
|
&pool,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?;
|
.handle_internal("Error writing to the database")?;
|
||||||
|
|
||||||
Ok(StatusCode::NO_CONTENT)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@ use crate::prelude::*;
|
|||||||
pub async fn delete(
|
pub async fn delete(
|
||||||
State(AppState { pool, ref storage }): State<AppState>,
|
State(AppState { pool, ref storage }): State<AppState>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
) -> Result<(), StatusCode> {
|
) -> GeneralResult<()> {
|
||||||
db::users::delete_user(claims.user_id, &pool)
|
db::users::delete_user(claims.user_id, &pool)
|
||||||
.try_for_each_concurrent(5, |file_id| async move {
|
.try_for_each_concurrent(5, |file_id| async move {
|
||||||
let _ = storage.delete(file_id).await;
|
let _ = storage.delete(file_id).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.handle_internal()
|
.handle_internal("Error deleting the user")
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,13 @@ pub struct Params {
|
|||||||
user_id: i32,
|
user_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response = Result<Json<db::users::UserInfo>, StatusCode>;
|
type Response = GeneralResult<Json<db::users::UserInfo>>;
|
||||||
|
|
||||||
pub async fn get(State(pool): State<Pool>, Query(params): Query<Params>) -> Response {
|
pub async fn get(State(pool): State<Pool>, Query(params): Query<Params>) -> Response {
|
||||||
let info = db::users::get(params.user_id, &pool)
|
let info = db::users::get(params.user_id, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()?
|
.handle_internal("Error getting the user")?
|
||||||
.ok_or(StatusCode::NOT_FOUND)?;
|
.handle(StatusCode::NOT_FOUND, "User not found")?;
|
||||||
Ok(Json(info))
|
Ok(Json(info))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,13 +14,10 @@ pub async fn put(
|
|||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
Json(params): Json<Params>,
|
Json(params): Json<Params>,
|
||||||
) -> Result<Json<db::users::UserInfo>, (StatusCode, String)> {
|
) -> GeneralResult<Json<db::users::UserInfo>> {
|
||||||
params
|
params.validate().map_err(GeneralError::validation)?;
|
||||||
.validate()
|
|
||||||
.map_err(|err| (StatusCode::BAD_REQUEST, err.to_string()))?;
|
|
||||||
db::users::update(claims.user_id, ¶ms.username, ¶ms.email, &pool)
|
db::users::update(claims.user_id, ¶ms.username, ¶ms.email, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()
|
.handle_internal("Error updating the user")
|
||||||
.map_err(|status| (status, String::new()))
|
|
||||||
.map(Json)
|
.map(Json)
|
||||||
}
|
}
|
||||||
|
@ -48,22 +48,22 @@ fn validate_password(password: &str) -> Result<(), ValidationError> {
|
|||||||
pub async fn register(
|
pub async fn register(
|
||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
Form(params): Form<Params>,
|
Form(params): Form<Params>,
|
||||||
) -> Result<Json<Token>, Either<(StatusCode, String), Error>> {
|
) -> Result<Json<Token>, Either<GeneralError, Error>> {
|
||||||
params
|
params
|
||||||
.validate()
|
.validate()
|
||||||
.map_err(|err| Either::E1((StatusCode::BAD_REQUEST, err.to_string())))?;
|
.map_err(GeneralError::validation)
|
||||||
|
.map_err(Either::E1)?;
|
||||||
|
|
||||||
let password = HashedBytes::hash_bytes(params.password.as_bytes()).as_bytes();
|
let password = HashedBytes::hash_bytes(params.password.as_bytes()).as_bytes();
|
||||||
let Some(id) = db::users::create_user(¶ms.username, ¶ms.email, &password, &pool)
|
let id = db::users::create_user(¶ms.username, ¶ms.email, &password, &pool)
|
||||||
.await
|
.await
|
||||||
.handle_internal()
|
.handle_internal("Error creating the user")
|
||||||
.map_err(|status| Either::E1((status, String::new())))?
|
.map_err(Either::E1)?
|
||||||
else {
|
.handle(
|
||||||
return Err(Either::E1((
|
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
"Either the user name or the email are taken".to_owned(),
|
"The username or the email are taken",
|
||||||
)));
|
)
|
||||||
};
|
.map_err(Either::E1)?;
|
||||||
|
|
||||||
let token = Claims::new(id).encode().map_err(Either::E2)?;
|
let token = Claims::new(id).encode().map_err(Either::E2)?;
|
||||||
Ok(Json(token))
|
Ok(Json(token))
|
||||||
|
@ -8,12 +8,12 @@ pub struct Params {
|
|||||||
pub async fn search(
|
pub async fn search(
|
||||||
State(pool): State<Pool>,
|
State(pool): State<Pool>,
|
||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
) -> sqlx::Result<Json<Vec<db::users::UserSearch>>, StatusCode> {
|
) -> GeneralResult<Json<Vec<db::users::UserSearch>>> {
|
||||||
db::users::search_for_user(¶ms.search_string, &pool)
|
db::users::search_for_user(¶ms.search_string, &pool)
|
||||||
.take(20)
|
.take(20)
|
||||||
.try_filter(|user| future::ready(user.similarity > 0.1))
|
.try_filter(|user| future::ready(user.similarity > 0.1))
|
||||||
.try_collect()
|
.try_collect()
|
||||||
.await
|
.await
|
||||||
.handle_internal()
|
.handle_internal("Error getting users from the database")
|
||||||
.map(Json)
|
.map(Json)
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,96 @@
|
|||||||
use axum::http::StatusCode;
|
use std::{borrow::Cow, convert::Infallible};
|
||||||
|
|
||||||
|
use axum::{http::StatusCode, response::IntoResponse};
|
||||||
|
|
||||||
type BoxError = Box<dyn std::error::Error>;
|
type BoxError = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
pub fn handle_error(error: impl Into<BoxError>) {
|
pub struct GeneralError {
|
||||||
let error: BoxError = error.into();
|
pub status_code: StatusCode,
|
||||||
tracing::error!(error);
|
pub message: Cow<'static, str>,
|
||||||
|
pub error: Option<BoxError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type GeneralResult<T> = Result<T, GeneralError>;
|
||||||
|
|
||||||
|
impl GeneralError {
|
||||||
|
pub fn message(status_code: StatusCode, message: impl Into<Cow<'static, str>>) -> Self {
|
||||||
|
Self {
|
||||||
|
status_code,
|
||||||
|
message: message.into(),
|
||||||
|
error: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
pub fn validation(error: validator::ValidationErrors) -> Self {
|
||||||
|
Self::message(StatusCode::BAD_REQUEST, error.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn permissions(error: sqlx::Error) -> Self {
|
||||||
|
GeneralError {
|
||||||
|
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
message: Cow::Borrowed("Error getting permissions"),
|
||||||
|
error: Some(error.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn item_not_found() -> Self {
|
||||||
|
GeneralError {
|
||||||
|
status_code: StatusCode::NOT_FOUND,
|
||||||
|
message: Cow::Borrowed("Item not found"),
|
||||||
|
error: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for GeneralError {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
if let Some(err) = self.error {
|
||||||
|
tracing::error!(err, message = %self.message, status_code = ?self.status_code);
|
||||||
|
}
|
||||||
|
(self.status_code, self.message).into_response()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ErrorHandlingExt<T, E>
|
pub trait ErrorHandlingExt<T, E>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
fn handle(self, code: StatusCode) -> Result<T, StatusCode>;
|
fn handle(
|
||||||
|
self,
|
||||||
|
status_code: StatusCode,
|
||||||
|
message: impl Into<Cow<'static, str>>,
|
||||||
|
) -> GeneralResult<T>;
|
||||||
|
|
||||||
fn handle_internal(self) -> Result<T, StatusCode> {
|
fn handle_internal(self, message: impl Into<Cow<'static, str>>) -> GeneralResult<T> {
|
||||||
self.handle(StatusCode::INTERNAL_SERVER_ERROR)
|
self.handle(StatusCode::INTERNAL_SERVER_ERROR, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E: Into<BoxError>> ErrorHandlingExt<T, E> for Result<T, E> {
|
impl<T, E: Into<BoxError>> ErrorHandlingExt<T, E> for Result<T, E> {
|
||||||
fn handle(self, code: StatusCode) -> Result<T, StatusCode> {
|
fn handle(
|
||||||
self.map_err(|err| {
|
self,
|
||||||
handle_error(err);
|
status_code: StatusCode,
|
||||||
code
|
message: impl Into<Cow<'static, str>>,
|
||||||
|
) -> GeneralResult<T> {
|
||||||
|
self.map_err(|err| GeneralError {
|
||||||
|
status_code,
|
||||||
|
message: message.into(),
|
||||||
|
error: Some(err.into()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ErrorHandlingExt<T, Infallible> for Option<T> {
|
||||||
|
fn handle(
|
||||||
|
self,
|
||||||
|
status_code: StatusCode,
|
||||||
|
message: impl Into<Cow<'static, str>>,
|
||||||
|
) -> GeneralResult<T> {
|
||||||
|
self.ok_or_else(|| GeneralError {
|
||||||
|
status_code,
|
||||||
|
message: message.into(),
|
||||||
|
error: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
pub(crate) use crate::{auth::Claims, db, errors::ErrorHandlingExt as _, AppState, Pool};
|
pub(crate) use crate::{
|
||||||
|
auth::Claims,
|
||||||
|
db,
|
||||||
|
errors::{ErrorHandlingExt as _, GeneralError, GeneralResult},
|
||||||
|
AppState, Pool,
|
||||||
|
};
|
||||||
pub use axum::{
|
pub use axum::{
|
||||||
extract::{Json, Query, State},
|
extract::{Json, Query, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
|
Reference in New Issue
Block a user