copy files
This commit is contained in:
57
backend/src/lib/domain/warren/models/file/requests/cp.rs
Normal file
57
backend/src/lib/domain/warren/models/file/requests/cp.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::domain::warren::models::file::AbsoluteFilePath;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct CpRequest {
|
||||
path: AbsoluteFilePath,
|
||||
target_path: AbsoluteFilePath,
|
||||
}
|
||||
|
||||
impl CpRequest {
|
||||
pub fn new(path: AbsoluteFilePath, target_path: AbsoluteFilePath) -> Self {
|
||||
Self { path, target_path }
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &AbsoluteFilePath {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn target_path(&self) -> &AbsoluteFilePath {
|
||||
&self.target_path
|
||||
}
|
||||
|
||||
pub fn into_paths(self) -> (AbsoluteFilePath, AbsoluteFilePath) {
|
||||
(self.path, self.target_path)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CpError {
|
||||
#[error("The file does not exist")]
|
||||
NotFound,
|
||||
#[error("The target path already exists")]
|
||||
AlreadyExists,
|
||||
#[error(transparent)]
|
||||
Unknown(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct CpResponse {
|
||||
path: AbsoluteFilePath,
|
||||
target_path: AbsoluteFilePath,
|
||||
}
|
||||
|
||||
impl CpResponse {
|
||||
pub fn new(path: AbsoluteFilePath, target_path: AbsoluteFilePath) -> Self {
|
||||
Self { path, target_path }
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &AbsoluteFilePath {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn target_path(&self) -> &AbsoluteFilePath {
|
||||
&self.target_path
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
mod cat;
|
||||
mod cp;
|
||||
mod ls;
|
||||
mod mkdir;
|
||||
mod mv;
|
||||
@@ -7,6 +8,7 @@ mod save;
|
||||
mod touch;
|
||||
|
||||
pub use cat::*;
|
||||
pub use cp::*;
|
||||
pub use ls::*;
|
||||
pub use mkdir::*;
|
||||
pub use mv::*;
|
||||
|
||||
@@ -4,6 +4,9 @@ use futures_util::StreamExt;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::domain::warren::models::file::CpError;
|
||||
use crate::domain::warren::models::file::CpRequest;
|
||||
use crate::domain::warren::models::file::CpResponse;
|
||||
use crate::domain::warren::models::file::LsResponse;
|
||||
use crate::domain::warren::models::file::SaveResponse;
|
||||
use crate::domain::warren::models::file::{
|
||||
@@ -673,3 +676,68 @@ impl WarrenTouchResponse {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WarrenCpRequest {
|
||||
warren_id: Uuid,
|
||||
base: CpRequest,
|
||||
}
|
||||
|
||||
impl WarrenCpRequest {
|
||||
pub fn new(warren_id: Uuid, base: CpRequest) -> Self {
|
||||
Self { warren_id, base }
|
||||
}
|
||||
|
||||
pub fn warren_id(&self) -> &Uuid {
|
||||
&self.warren_id
|
||||
}
|
||||
|
||||
pub fn base(&self) -> &CpRequest {
|
||||
&self.base
|
||||
}
|
||||
|
||||
pub fn build_fs_request(self, warren: &Warren) -> CpRequest {
|
||||
let (base_path, base_target_path) = self.base.into_paths();
|
||||
|
||||
let path = warren.path().clone().join(&base_path.to_relative());
|
||||
let target_path = warren.path().clone().join(&base_target_path.to_relative());
|
||||
|
||||
CpRequest::new(path, target_path)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<FetchWarrenRequest> for &WarrenCpRequest {
|
||||
fn into(self) -> FetchWarrenRequest {
|
||||
FetchWarrenRequest::new(self.warren_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WarrenCpError {
|
||||
#[error(transparent)]
|
||||
FetchWarren(#[from] FetchWarrenError),
|
||||
#[error(transparent)]
|
||||
FileSystem(#[from] CpError),
|
||||
#[error(transparent)]
|
||||
Unknown(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WarrenCpResponse {
|
||||
warren: Warren,
|
||||
base: CpResponse,
|
||||
}
|
||||
|
||||
impl WarrenCpResponse {
|
||||
pub fn new(warren: Warren, base: CpResponse) -> Self {
|
||||
Self { warren, base }
|
||||
}
|
||||
|
||||
pub fn warren(&self) -> &Warren {
|
||||
&self.warren
|
||||
}
|
||||
|
||||
pub fn base(&self) -> &CpResponse {
|
||||
&self.base
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ pub trait WarrenMetrics: Clone + Send + Sync + 'static {
|
||||
|
||||
fn record_warren_touch_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_warren_touch_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
|
||||
fn record_warren_cp_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_warren_cp_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
pub trait FileSystemMetrics: Clone + Send + Sync + 'static {
|
||||
@@ -62,6 +65,9 @@ pub trait FileSystemMetrics: Clone + Send + Sync + 'static {
|
||||
|
||||
fn record_touch_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_touch_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
|
||||
fn record_cp_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_cp_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
pub trait AuthMetrics: Clone + Send + Sync + 'static {
|
||||
@@ -139,4 +145,7 @@ pub trait AuthMetrics: Clone + Send + Sync + 'static {
|
||||
|
||||
fn record_auth_warren_touch_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_auth_warren_touch_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
|
||||
fn record_auth_warren_cp_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_auth_warren_cp_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ use super::models::{
|
||||
},
|
||||
},
|
||||
file::{
|
||||
CatError, CatRequest, FileStream, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, SaveResponse, TouchError,
|
||||
TouchRequest,
|
||||
CatError, CatRequest, CpError, CpRequest, CpResponse, FileStream, LsError, LsRequest,
|
||||
LsResponse, MkdirError, MkdirRequest, MvError, MvRequest, RmError, RmRequest, SaveError,
|
||||
SaveRequest, SaveResponse, TouchError, TouchRequest,
|
||||
},
|
||||
user::{
|
||||
CreateUserError, CreateUserRequest, DeleteUserError, DeleteUserRequest, EditUserError,
|
||||
@@ -37,11 +37,11 @@ use super::models::{
|
||||
CreateWarrenError, CreateWarrenRequest, DeleteWarrenError, DeleteWarrenRequest,
|
||||
EditWarrenError, EditWarrenRequest, FetchWarrenError, FetchWarrenRequest,
|
||||
FetchWarrensError, FetchWarrensRequest, ListWarrensError, ListWarrensRequest, Warren,
|
||||
WarrenCatError, WarrenCatRequest, WarrenLsError, WarrenLsRequest, WarrenLsResponse,
|
||||
WarrenMkdirError, WarrenMkdirRequest, WarrenMkdirResponse, WarrenMvError, WarrenMvRequest,
|
||||
WarrenMvResponse, WarrenRmError, WarrenRmRequest, WarrenRmResponse, WarrenSaveError,
|
||||
WarrenSaveRequest, WarrenSaveResponse, WarrenTouchError, WarrenTouchRequest,
|
||||
WarrenTouchResponse,
|
||||
WarrenCatError, WarrenCatRequest, WarrenCpError, WarrenCpRequest, WarrenCpResponse,
|
||||
WarrenLsError, WarrenLsRequest, WarrenLsResponse, WarrenMkdirError, WarrenMkdirRequest,
|
||||
WarrenMkdirResponse, WarrenMvError, WarrenMvRequest, WarrenMvResponse, WarrenRmError,
|
||||
WarrenRmRequest, WarrenRmResponse, WarrenSaveError, WarrenSaveRequest, WarrenSaveResponse,
|
||||
WarrenTouchError, WarrenTouchRequest, WarrenTouchResponse,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -100,6 +100,10 @@ pub trait WarrenService: Clone + Send + Sync + 'static {
|
||||
&self,
|
||||
request: WarrenTouchRequest,
|
||||
) -> impl Future<Output = Result<WarrenTouchResponse, WarrenTouchError>> + Send;
|
||||
fn warren_cp(
|
||||
&self,
|
||||
request: WarrenCpRequest,
|
||||
) -> impl Future<Output = Result<WarrenCpResponse, WarrenCpError>> + Send;
|
||||
}
|
||||
|
||||
pub trait FileSystemService: Clone + Send + Sync + 'static {
|
||||
@@ -114,6 +118,7 @@ pub trait FileSystemService: Clone + Send + Sync + 'static {
|
||||
request: SaveRequest,
|
||||
) -> impl Future<Output = Result<SaveResponse, SaveError>> + Send;
|
||||
fn touch(&self, request: TouchRequest) -> impl Future<Output = Result<(), TouchError>> + Send;
|
||||
fn cp(&self, request: CpRequest) -> impl Future<Output = Result<CpResponse, CpError>> + Send;
|
||||
}
|
||||
|
||||
pub trait AuthService: Clone + Send + Sync + 'static {
|
||||
@@ -252,4 +257,9 @@ pub trait AuthService: Clone + Send + Sync + 'static {
|
||||
request: AuthRequest<WarrenTouchRequest>,
|
||||
warren_service: &WS,
|
||||
) -> impl Future<Output = Result<WarrenTouchResponse, AuthError<WarrenTouchError>>> + Send;
|
||||
fn auth_warren_cp<WS: WarrenService>(
|
||||
&self,
|
||||
request: AuthRequest<WarrenCpRequest>,
|
||||
warren_service: &WS,
|
||||
) -> impl Future<Output = Result<WarrenCpResponse, AuthError<WarrenCpError>>> + Send;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::domain::warren::models::{
|
||||
user::{ListAllUsersAndWarrensResponse, LoginUserResponse, User},
|
||||
user_warren::UserWarren,
|
||||
warren::{
|
||||
Warren, WarrenLsResponse, WarrenMkdirResponse, WarrenMvResponse, WarrenRmResponse,
|
||||
WarrenSaveResponse, WarrenTouchResponse,
|
||||
Warren, WarrenCpResponse, WarrenLsResponse, WarrenMkdirResponse, WarrenMvResponse,
|
||||
WarrenRmResponse, WarrenSaveResponse, WarrenTouchResponse,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -43,6 +43,7 @@ pub trait WarrenNotifier: Clone + Send + Sync + 'static {
|
||||
warren: &Warren,
|
||||
path: &AbsoluteFilePath,
|
||||
) -> impl Future<Output = ()> + Send;
|
||||
fn warren_cp(&self, response: &WarrenCpResponse) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
pub trait FileSystemNotifier: Clone + Send + Sync + 'static {
|
||||
@@ -57,6 +58,11 @@ pub trait FileSystemNotifier: Clone + Send + Sync + 'static {
|
||||
) -> impl Future<Output = ()> + Send;
|
||||
fn save(&self, path: &AbsoluteFilePath) -> impl Future<Output = ()> + Send;
|
||||
fn touch(&self, path: &AbsoluteFilePath) -> impl Future<Output = ()> + Send;
|
||||
fn cp(
|
||||
&self,
|
||||
path: &AbsoluteFilePath,
|
||||
target_path: &AbsoluteFilePath,
|
||||
) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
pub trait AuthNotifier: Clone + Send + Sync + 'static {
|
||||
@@ -160,4 +166,9 @@ pub trait AuthNotifier: Clone + Send + Sync + 'static {
|
||||
user: &User,
|
||||
response: &WarrenTouchResponse,
|
||||
) -> impl Future<Output = ()> + Send;
|
||||
fn auth_warren_cp(
|
||||
&self,
|
||||
user: &User,
|
||||
response: &WarrenCpResponse,
|
||||
) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ use crate::domain::warren::models::{
|
||||
},
|
||||
},
|
||||
file::{
|
||||
CatError, CatRequest, FileStream, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, SaveResponse, TouchError,
|
||||
TouchRequest,
|
||||
CatError, CatRequest, CpError, CpRequest, CpResponse, FileStream, LsError, LsRequest,
|
||||
LsResponse, MkdirError, MkdirRequest, MvError, MvRequest, RmError, RmRequest, SaveError,
|
||||
SaveRequest, SaveResponse, TouchError, TouchRequest,
|
||||
},
|
||||
user::{
|
||||
CreateUserError, CreateUserRequest, DeleteUserError, DeleteUserRequest, EditUserError,
|
||||
@@ -77,6 +77,7 @@ pub trait FileSystemRepository: Clone + Send + Sync + 'static {
|
||||
request: SaveRequest,
|
||||
) -> impl Future<Output = Result<SaveResponse, SaveError>> + Send;
|
||||
fn touch(&self, request: TouchRequest) -> impl Future<Output = Result<(), TouchError>> + Send;
|
||||
fn cp(&self, request: CpRequest) -> impl Future<Output = Result<CpResponse, CpError>> + Send;
|
||||
}
|
||||
|
||||
pub trait AuthRepository: Clone + Send + Sync + 'static {
|
||||
|
||||
@@ -28,12 +28,12 @@ use crate::{
|
||||
warren::{
|
||||
CreateWarrenError, CreateWarrenRequest, DeleteWarrenError, DeleteWarrenRequest,
|
||||
EditWarrenError, EditWarrenRequest, FetchWarrenError, FetchWarrenRequest,
|
||||
FetchWarrensRequest, Warren, WarrenCatError, WarrenCatRequest, WarrenLsError,
|
||||
WarrenLsRequest, WarrenLsResponse, WarrenMkdirError, WarrenMkdirRequest,
|
||||
WarrenMkdirResponse, WarrenMvError, WarrenMvRequest, WarrenMvResponse,
|
||||
WarrenRmError, WarrenRmRequest, WarrenRmResponse, WarrenSaveError,
|
||||
WarrenSaveRequest, WarrenSaveResponse, WarrenTouchError, WarrenTouchRequest,
|
||||
WarrenTouchResponse,
|
||||
FetchWarrensRequest, Warren, WarrenCatError, WarrenCatRequest, WarrenCpError,
|
||||
WarrenCpRequest, WarrenCpResponse, WarrenLsError, WarrenLsRequest,
|
||||
WarrenLsResponse, WarrenMkdirError, WarrenMkdirRequest, WarrenMkdirResponse,
|
||||
WarrenMvError, WarrenMvRequest, WarrenMvResponse, WarrenRmError, WarrenRmRequest,
|
||||
WarrenRmResponse, WarrenSaveError, WarrenSaveRequest, WarrenSaveResponse,
|
||||
WarrenTouchError, WarrenTouchRequest, WarrenTouchResponse,
|
||||
},
|
||||
},
|
||||
ports::{AuthMetrics, AuthNotifier, AuthRepository, AuthService, WarrenService},
|
||||
@@ -839,12 +839,51 @@ where
|
||||
.map_err(AuthError::Custom);
|
||||
|
||||
if let Ok(response) = result.as_ref() {
|
||||
self.metrics.record_auth_warren_save_success().await;
|
||||
self.metrics.record_auth_warren_touch_success().await;
|
||||
self.notifier
|
||||
.auth_warren_touch(session_response.user(), response)
|
||||
.await;
|
||||
} else {
|
||||
self.metrics.record_auth_warren_save_failure().await;
|
||||
self.metrics.record_auth_warren_touch_failure().await;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn auth_warren_cp<WS: WarrenService>(
|
||||
&self,
|
||||
request: AuthRequest<WarrenCpRequest>,
|
||||
warren_service: &WS,
|
||||
) -> Result<WarrenCpResponse, AuthError<WarrenCpError>> {
|
||||
let session_response = self.fetch_auth_session((&request).into()).await?;
|
||||
|
||||
let request = request.into_value();
|
||||
|
||||
let user_warren = self
|
||||
.repository
|
||||
.fetch_user_warren(FetchUserWarrenRequest::new(
|
||||
session_response.user().id().clone(),
|
||||
request.warren_id().clone(),
|
||||
))
|
||||
.await?;
|
||||
|
||||
// TODO: Maybe create a separate permission for this
|
||||
if !user_warren.can_modify_files() {
|
||||
return Err(AuthError::InsufficientPermissions);
|
||||
}
|
||||
|
||||
let result = warren_service
|
||||
.warren_cp(request)
|
||||
.await
|
||||
.map_err(AuthError::Custom);
|
||||
|
||||
if let Ok(response) = result.as_ref() {
|
||||
self.metrics.record_auth_warren_cp_success().await;
|
||||
self.notifier
|
||||
.auth_warren_cp(session_response.user(), response)
|
||||
.await;
|
||||
} else {
|
||||
self.metrics.record_auth_warren_cp_failure().await;
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::domain::warren::{
|
||||
models::file::{
|
||||
CatError, CatRequest, FileStream, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RmError, RmRequest, SaveError, SaveRequest, SaveResponse, TouchError,
|
||||
TouchRequest,
|
||||
CatError, CatRequest, CpError, CpRequest, CpResponse, FileStream, LsError, LsRequest,
|
||||
LsResponse, MkdirError, MkdirRequest, MvError, MvRequest, RmError, RmRequest, SaveError,
|
||||
SaveRequest, SaveResponse, TouchError, TouchRequest,
|
||||
},
|
||||
ports::{FileSystemMetrics, FileSystemNotifier, FileSystemRepository, FileSystemService},
|
||||
};
|
||||
@@ -137,4 +137,19 @@ where
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn cp(&self, request: CpRequest) -> Result<CpResponse, CpError> {
|
||||
let path = request.path().clone();
|
||||
let target_path = request.target_path().clone();
|
||||
let result = self.repository.cp(request).await;
|
||||
|
||||
if result.is_ok() {
|
||||
self.metrics.record_cp_success().await;
|
||||
self.notifier.cp(&path, &target_path).await;
|
||||
} else {
|
||||
self.metrics.record_cp_failure().await;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ use crate::domain::warren::{
|
||||
warren::{
|
||||
CreateWarrenError, CreateWarrenRequest, DeleteWarrenError, DeleteWarrenRequest,
|
||||
EditWarrenError, EditWarrenRequest, FetchWarrensError, FetchWarrensRequest,
|
||||
ListWarrensError, ListWarrensRequest, WarrenCatError, WarrenCatRequest,
|
||||
WarrenLsResponse, WarrenMkdirResponse, WarrenMvError, WarrenMvRequest,
|
||||
WarrenMvResponse, WarrenRmRequest, WarrenRmResponse, WarrenSaveResponse,
|
||||
WarrenTouchError, WarrenTouchRequest, WarrenTouchResponse,
|
||||
ListWarrensError, ListWarrensRequest, WarrenCatError, WarrenCatRequest, WarrenCpError,
|
||||
WarrenCpRequest, WarrenCpResponse, WarrenLsResponse, WarrenMkdirResponse,
|
||||
WarrenMvError, WarrenMvRequest, WarrenMvResponse, WarrenRmRequest, WarrenRmResponse,
|
||||
WarrenSaveResponse, WarrenTouchError, WarrenTouchRequest, WarrenTouchResponse,
|
||||
},
|
||||
},
|
||||
ports::FileSystemService,
|
||||
@@ -314,4 +314,25 @@ where
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn warren_cp(&self, request: WarrenCpRequest) -> Result<WarrenCpResponse, WarrenCpError> {
|
||||
let warren = self.repository.fetch_warren((&request).into()).await?;
|
||||
|
||||
let cp_request = request.build_fs_request(&warren);
|
||||
let result = self
|
||||
.fs_service
|
||||
.cp(cp_request)
|
||||
.await
|
||||
.map(|base| WarrenCpResponse::new(warren, base))
|
||||
.map_err(Into::into);
|
||||
|
||||
if let Ok(response) = result.as_ref() {
|
||||
self.metrics.record_warren_cp_success().await;
|
||||
self.notifier.warren_cp(response).await;
|
||||
} else {
|
||||
self.metrics.record_warren_cp_failure().await;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ mod fetch_warren;
|
||||
mod list_warrens;
|
||||
mod upload_warren_files;
|
||||
mod warren_cat;
|
||||
mod warren_cp;
|
||||
mod warren_ls;
|
||||
mod warren_mkdir;
|
||||
mod warren_move;
|
||||
mod warren_mv;
|
||||
mod warren_rm;
|
||||
|
||||
use axum::{
|
||||
@@ -27,7 +28,8 @@ use warren_rm::warren_rm;
|
||||
|
||||
use upload_warren_files::warren_save;
|
||||
use warren_cat::fetch_file;
|
||||
use warren_move::warren_move;
|
||||
use warren_cp::warren_cp;
|
||||
use warren_mv::warren_mv;
|
||||
|
||||
pub fn routes<WS: WarrenService, AS: AuthService>() -> Router<AppState<WS, AS>> {
|
||||
Router::new()
|
||||
@@ -42,5 +44,6 @@ pub fn routes<WS: WarrenService, AS: AuthService>() -> Router<AppState<WS, AS>>
|
||||
// 10737418240 bytes = 10GB
|
||||
post(warren_save).route_layer(DefaultBodyLimit::max(10737418240)),
|
||||
)
|
||||
.route("/files/mv", post(warren_move))
|
||||
.route("/files/mv", post(warren_mv))
|
||||
.route("/files/cp", post(warren_cp))
|
||||
}
|
||||
|
||||
80
backend/src/lib/inbound/http/handlers/warrens/warren_cp.rs
Normal file
80
backend/src/lib/inbound/http/handlers/warrens/warren_cp.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use axum::{Json, extract::State, http::StatusCode};
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
domain::warren::{
|
||||
models::{
|
||||
auth_session::AuthRequest,
|
||||
file::{AbsoluteFilePath, AbsoluteFilePathError, CpRequest, FilePath, FilePathError},
|
||||
warren::WarrenCpRequest,
|
||||
},
|
||||
ports::{AuthService, WarrenService},
|
||||
},
|
||||
inbound::http::{
|
||||
AppState,
|
||||
handlers::extractors::SessionIdHeader,
|
||||
responses::{ApiError, ApiSuccess},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CpWarrenEntryHttpRequestBody {
|
||||
warren_id: Uuid,
|
||||
path: String,
|
||||
target_path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum ParseWarrenCpHttpRequestError {
|
||||
#[error(transparent)]
|
||||
FilePath(#[from] FilePathError),
|
||||
#[error(transparent)]
|
||||
AbsoluteFilePath(#[from] AbsoluteFilePathError),
|
||||
}
|
||||
|
||||
impl CpWarrenEntryHttpRequestBody {
|
||||
fn try_into_domain(self) -> Result<WarrenCpRequest, ParseWarrenCpHttpRequestError> {
|
||||
let path: AbsoluteFilePath = FilePath::new(&self.path)?.try_into()?;
|
||||
let target_path: AbsoluteFilePath = FilePath::new(&self.target_path)?.try_into()?;
|
||||
|
||||
Ok(WarrenCpRequest::new(
|
||||
self.warren_id,
|
||||
CpRequest::new(path, target_path),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseWarrenCpHttpRequestError> for ApiError {
|
||||
fn from(value: ParseWarrenCpHttpRequestError) -> Self {
|
||||
match value {
|
||||
ParseWarrenCpHttpRequestError::FilePath(err) => match err {
|
||||
FilePathError::InvalidPath => {
|
||||
ApiError::BadRequest("The file path must be valid".to_string())
|
||||
}
|
||||
},
|
||||
ParseWarrenCpHttpRequestError::AbsoluteFilePath(err) => match err {
|
||||
AbsoluteFilePathError::NotAbsolute => {
|
||||
ApiError::BadRequest("The file path must be absolute".to_string())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn warren_cp<WS: WarrenService, AS: AuthService>(
|
||||
State(state): State<AppState<WS, AS>>,
|
||||
SessionIdHeader(session): SessionIdHeader,
|
||||
Json(request): Json<CpWarrenEntryHttpRequestBody>,
|
||||
) -> Result<ApiSuccess<()>, ApiError> {
|
||||
let domain_request = AuthRequest::new(session, request.try_into_domain()?);
|
||||
|
||||
state
|
||||
.auth_service
|
||||
.auth_warren_cp(domain_request, state.warren_service.as_ref())
|
||||
.await
|
||||
.map(|_| ApiSuccess::new(StatusCode::OK, ()))
|
||||
.map_err(ApiError::from)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenameWarrenEntryHttpRequestBody {
|
||||
pub struct MvWarrenEntryHttpRequestBody {
|
||||
warren_id: Uuid,
|
||||
path: String,
|
||||
target_path: String,
|
||||
@@ -35,7 +35,7 @@ pub enum ParseWarrenMvHttpRequestError {
|
||||
AbsoluteFilePath(#[from] AbsoluteFilePathError),
|
||||
}
|
||||
|
||||
impl RenameWarrenEntryHttpRequestBody {
|
||||
impl MvWarrenEntryHttpRequestBody {
|
||||
fn try_into_domain(self) -> Result<WarrenMvRequest, ParseWarrenMvHttpRequestError> {
|
||||
let path: AbsoluteFilePath = FilePath::new(&self.path)?.try_into()?;
|
||||
let target_path: AbsoluteFilePath = FilePath::new(&self.target_path)?.try_into()?;
|
||||
@@ -64,10 +64,10 @@ impl From<ParseWarrenMvHttpRequestError> for ApiError {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn warren_move<WS: WarrenService, AS: AuthService>(
|
||||
pub async fn warren_mv<WS: WarrenService, AS: AuthService>(
|
||||
State(state): State<AppState<WS, AS>>,
|
||||
SessionIdHeader(session): SessionIdHeader,
|
||||
Json(request): Json<RenameWarrenEntryHttpRequestBody>,
|
||||
Json(request): Json<MvWarrenEntryHttpRequestBody>,
|
||||
) -> Result<ApiSuccess<()>, ApiError> {
|
||||
let domain_request = AuthRequest::new(session, request.try_into_domain()?);
|
||||
|
||||
@@ -14,10 +14,10 @@ use crate::{
|
||||
domain::warren::{
|
||||
models::{
|
||||
file::{
|
||||
AbsoluteFilePath, CatError, CatRequest, File, FileMimeType, FileName, FilePath,
|
||||
FileStream, FileType, LsError, LsRequest, LsResponse, MkdirError, MkdirRequest,
|
||||
MvError, MvRequest, RelativeFilePath, RmError, RmRequest, SaveError, SaveRequest,
|
||||
SaveResponse, TouchError, TouchRequest,
|
||||
AbsoluteFilePath, CatError, CatRequest, CpError, CpRequest, CpResponse, File,
|
||||
FileMimeType, FileName, FilePath, FileStream, FileType, LsError, LsRequest,
|
||||
LsResponse, MkdirError, MkdirRequest, MvError, MvRequest, RelativeFilePath,
|
||||
RmError, RmRequest, SaveError, SaveRequest, SaveResponse, TouchError, TouchRequest,
|
||||
},
|
||||
warren::UploadFileStream,
|
||||
},
|
||||
@@ -242,7 +242,20 @@ impl FileSystem {
|
||||
async fn touch(&self, path: &AbsoluteFilePath) -> io::Result<()> {
|
||||
let path = self.get_target_path(path);
|
||||
|
||||
tokio::fs::File::create(&path).await.map(|_| ())
|
||||
fs::File::create(&path).await.map(|_| ())
|
||||
}
|
||||
|
||||
async fn cp(
|
||||
&self,
|
||||
path: AbsoluteFilePath,
|
||||
target_path: AbsoluteFilePath,
|
||||
) -> io::Result<CpResponse> {
|
||||
let fs_current_path = self.get_target_path(&path);
|
||||
let fs_target_path = self.get_target_path(&target_path);
|
||||
|
||||
fs::copy(fs_current_path, fs_target_path).await?;
|
||||
|
||||
Ok(CpResponse::new(path, target_path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,6 +326,13 @@ impl FileSystemRepository for FileSystem {
|
||||
let (path, mut stream) = request.unpack();
|
||||
Ok(self.save(&path, &mut stream).await.map(SaveResponse::new)?)
|
||||
}
|
||||
|
||||
async fn cp(&self, request: CpRequest) -> Result<CpResponse, CpError> {
|
||||
let (path, target_path) = request.into_paths();
|
||||
self.cp(path, target_path)
|
||||
.await
|
||||
.map_err(|e| CpError::Unknown(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use `DirEntry::metadata` once `target=x86_64-unknown-linux-musl` updates from musl 1.2.3 to 1.2.5
|
||||
|
||||
@@ -100,6 +100,13 @@ impl WarrenMetrics for MetricsDebugLogger {
|
||||
async fn record_warren_touch_failure(&self) {
|
||||
tracing::debug!("[Metrics] Warren entry touch failed");
|
||||
}
|
||||
|
||||
async fn record_warren_cp_success(&self) {
|
||||
tracing::debug!("[Metrics] Warren entry cp succeeded");
|
||||
}
|
||||
async fn record_warren_cp_failure(&self) {
|
||||
tracing::debug!("[Metrics] Warren entry cp failed");
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystemMetrics for MetricsDebugLogger {
|
||||
@@ -151,6 +158,13 @@ impl FileSystemMetrics for MetricsDebugLogger {
|
||||
async fn record_touch_failure(&self) {
|
||||
tracing::debug!("[Metrics] Touch failed");
|
||||
}
|
||||
|
||||
async fn record_cp_success(&self) {
|
||||
tracing::debug!("[Metrics] Cp succeeded");
|
||||
}
|
||||
async fn record_cp_failure(&self) {
|
||||
tracing::debug!("[Metrics] Cp failed");
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthMetrics for MetricsDebugLogger {
|
||||
@@ -328,4 +342,11 @@ impl AuthMetrics for MetricsDebugLogger {
|
||||
async fn record_auth_warren_touch_failure(&self) {
|
||||
tracing::debug!("[Metrics] Auth warren touch failed");
|
||||
}
|
||||
|
||||
async fn record_auth_warren_cp_success(&self) {
|
||||
tracing::debug!("[Metrics] Auth warren cp succeeded");
|
||||
}
|
||||
async fn record_auth_warren_cp_failure(&self) {
|
||||
tracing::debug!("[Metrics] Auth warren cp failed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::domain::warren::{
|
||||
user::{ListAllUsersAndWarrensResponse, LoginUserResponse, User},
|
||||
user_warren::UserWarren,
|
||||
warren::{
|
||||
Warren, WarrenLsResponse, WarrenMkdirResponse, WarrenMvResponse, WarrenRmResponse,
|
||||
WarrenSaveResponse, WarrenTouchResponse,
|
||||
Warren, WarrenCpResponse, WarrenLsResponse, WarrenMkdirResponse, WarrenMvResponse,
|
||||
WarrenRmResponse, WarrenSaveResponse, WarrenTouchResponse,
|
||||
},
|
||||
},
|
||||
ports::{AuthNotifier, FileSystemNotifier, WarrenNotifier},
|
||||
@@ -98,6 +98,15 @@ impl WarrenNotifier for NotifierDebugLogger {
|
||||
warren.name()
|
||||
);
|
||||
}
|
||||
|
||||
async fn warren_cp(&self, response: &WarrenCpResponse) {
|
||||
tracing::debug!(
|
||||
"[Notifier] Copied file {} to {} in warren {}",
|
||||
response.base().path(),
|
||||
response.base().target_path(),
|
||||
response.warren().name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystemNotifier for NotifierDebugLogger {
|
||||
@@ -128,6 +137,10 @@ impl FileSystemNotifier for NotifierDebugLogger {
|
||||
async fn touch(&self, path: &AbsoluteFilePath) {
|
||||
tracing::debug!("[Notifier] Touched file {}", path);
|
||||
}
|
||||
|
||||
async fn cp(&self, path: &AbsoluteFilePath, target_path: &AbsoluteFilePath) {
|
||||
tracing::debug!("[Notifier] Copied file {} to {}", path, target_path);
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthNotifier for NotifierDebugLogger {
|
||||
@@ -330,4 +343,14 @@ impl AuthNotifier for NotifierDebugLogger {
|
||||
user.id()
|
||||
)
|
||||
}
|
||||
|
||||
async fn auth_warren_cp(&self, user: &User, response: &WarrenCpResponse) {
|
||||
tracing::debug!(
|
||||
"[Notifier] Copied file {} to {} in warren {} for authenticated user {}",
|
||||
response.base().path(),
|
||||
response.base().target_path(),
|
||||
response.warren().name(),
|
||||
user.id()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user