move multiple files at once
This commit is contained in:
@@ -33,6 +33,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NoopService {}
|
||||||
|
impl OidcService for NoopService {
|
||||||
|
async fn get_redirect(
|
||||||
|
&self,
|
||||||
|
_: GetRedirectRequest,
|
||||||
|
) -> Result<GetRedirectResponse, GetRedirectError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_user_info(
|
||||||
|
&self,
|
||||||
|
_: GetUserInfoRequest,
|
||||||
|
) -> Result<GetUserInfoResponse, GetUserInfoError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<R, M, N> OidcService for Service<R, M, N>
|
impl<R, M, N> OidcService for Service<R, M, N>
|
||||||
where
|
where
|
||||||
R: OidcRepository,
|
R: OidcRepository,
|
||||||
|
|||||||
@@ -2,36 +2,38 @@ use thiserror::Error;
|
|||||||
|
|
||||||
use crate::domain::warren::models::file::AbsoluteFilePath;
|
use crate::domain::warren::models::file::AbsoluteFilePath;
|
||||||
|
|
||||||
|
use super::AbsoluteFilePathList;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct MvRequest {
|
pub struct MvRequest {
|
||||||
path: AbsoluteFilePath,
|
paths: AbsoluteFilePathList,
|
||||||
target_path: AbsoluteFilePath,
|
target_path: AbsoluteFilePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MvRequest {
|
impl MvRequest {
|
||||||
pub fn new(path: AbsoluteFilePath, target_path: AbsoluteFilePath) -> Self {
|
pub fn new(paths: AbsoluteFilePathList, target_path: AbsoluteFilePath) -> Self {
|
||||||
Self { path, target_path }
|
Self { paths, target_path }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> &AbsoluteFilePath {
|
pub fn paths(&self) -> &AbsoluteFilePathList {
|
||||||
&self.path
|
&self.paths
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn target_path(&self) -> &AbsoluteFilePath {
|
pub fn target_path(&self) -> &AbsoluteFilePath {
|
||||||
&self.target_path
|
&self.target_path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unpack(self) -> (AbsoluteFilePath, AbsoluteFilePath) {
|
pub fn unpack(self) -> (AbsoluteFilePathList, AbsoluteFilePath) {
|
||||||
(self.path, self.target_path)
|
(self.paths, self.target_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum MvError {
|
pub enum MvError {
|
||||||
#[error("The path does not exist")]
|
#[error("The path does not exist")]
|
||||||
NotFound,
|
NotFound(AbsoluteFilePath),
|
||||||
#[error("The target path already exists")]
|
#[error("The target path already exists")]
|
||||||
AlreadyExists,
|
AlreadyExists(AbsoluteFilePath),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Unknown(#[from] anyhow::Error),
|
Unknown(#[from] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -422,11 +422,16 @@ impl WarrenMvRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_fs_request(self, warren: &Warren) -> MvRequest {
|
pub fn build_fs_request(self, warren: &Warren) -> MvRequest {
|
||||||
let (base_path, base_target_path) = self.base.unpack();
|
let (mut base_paths, base_target_path) = self.base.unpack();
|
||||||
let path = warren.path().clone().join(&base_path.to_relative());
|
|
||||||
let target_path = warren.path().clone().join(&base_target_path.to_relative());
|
let target_path = warren.path().clone().join(&base_target_path.to_relative());
|
||||||
|
|
||||||
MvRequest::new(path, target_path)
|
base_paths
|
||||||
|
.paths_mut()
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|path| *path = warren.path.clone().join(&path.clone().to_relative()));
|
||||||
|
|
||||||
|
MvRequest::new(base_paths, target_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,32 +447,26 @@ impl Into<FetchWarrenRequest> for &WarrenMvRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug)]
|
||||||
pub struct WarrenMvResponse {
|
pub struct WarrenMvResponse {
|
||||||
warren: Warren,
|
warren: Warren,
|
||||||
old_path: AbsoluteFilePath,
|
results: Vec<Result<(AbsoluteFilePath, AbsoluteFilePath), MvError>>,
|
||||||
path: AbsoluteFilePath,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WarrenMvResponse {
|
impl WarrenMvResponse {
|
||||||
pub fn new(warren: Warren, old_path: AbsoluteFilePath, path: AbsoluteFilePath) -> Self {
|
pub fn new(
|
||||||
Self {
|
warren: Warren,
|
||||||
warren,
|
results: Vec<Result<(AbsoluteFilePath, AbsoluteFilePath), MvError>>,
|
||||||
old_path,
|
) -> Self {
|
||||||
path,
|
Self { warren, results }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn warren(&self) -> &Warren {
|
pub fn warren(&self) -> &Warren {
|
||||||
&self.warren
|
&self.warren
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn old_path(&self) -> &AbsoluteFilePath {
|
pub fn results(&self) -> &Vec<Result<(AbsoluteFilePath, AbsoluteFilePath), MvError>> {
|
||||||
&self.old_path
|
&self.results
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self) -> &AbsoluteFilePath {
|
|
||||||
&self.path
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,8 +475,6 @@ pub enum WarrenMvError {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FetchWarren(#[from] FetchWarrenError),
|
FetchWarren(#[from] FetchWarrenError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FileSystem(#[from] MvError),
|
|
||||||
#[error(transparent)]
|
|
||||||
Unknown(#[from] anyhow::Error),
|
Unknown(#[from] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,10 @@ pub trait FileSystemService: Clone + Send + Sync + 'static {
|
|||||||
&self,
|
&self,
|
||||||
request: RmRequest,
|
request: RmRequest,
|
||||||
) -> impl Future<Output = Vec<Result<AbsoluteFilePath, RmError>>> + Send;
|
) -> impl Future<Output = Vec<Result<AbsoluteFilePath, RmError>>> + Send;
|
||||||
fn mv(&self, request: MvRequest) -> impl Future<Output = Result<(), MvError>> + Send;
|
fn mv(
|
||||||
|
&self,
|
||||||
|
request: MvRequest,
|
||||||
|
) -> impl Future<Output = Vec<Result<(AbsoluteFilePath, AbsoluteFilePath), MvError>>> + Send;
|
||||||
fn save(
|
fn save(
|
||||||
&self,
|
&self,
|
||||||
request: SaveRequest,
|
request: SaveRequest,
|
||||||
|
|||||||
@@ -102,7 +102,10 @@ pub trait FileSystemRepository: Clone + Send + Sync + 'static {
|
|||||||
&self,
|
&self,
|
||||||
request: RmRequest,
|
request: RmRequest,
|
||||||
) -> impl Future<Output = Vec<Result<AbsoluteFilePath, RmError>>> + Send;
|
) -> impl Future<Output = Vec<Result<AbsoluteFilePath, RmError>>> + Send;
|
||||||
fn mv(&self, request: MvRequest) -> impl Future<Output = Result<(), MvError>> + Send;
|
fn mv(
|
||||||
|
&self,
|
||||||
|
request: MvRequest,
|
||||||
|
) -> impl Future<Output = Vec<Result<(AbsoluteFilePath, AbsoluteFilePath), MvError>>> + Send;
|
||||||
fn save(
|
fn save(
|
||||||
&self,
|
&self,
|
||||||
request: SaveRequest,
|
request: SaveRequest,
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ where
|
|||||||
oidc,
|
oidc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn oidc(&self) -> Option<&OIDC> {
|
||||||
|
self.oidc.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R, M, N, OIDC> AuthService for Service<R, M, N, OIDC>
|
impl<R, M, N, OIDC> AuthService for Service<R, M, N, OIDC>
|
||||||
@@ -240,7 +244,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
request: GetOidcRedirectRequest,
|
request: GetOidcRedirectRequest,
|
||||||
) -> Result<GetOidcRedirectResponse, GetOidcRedirectError> {
|
) -> Result<GetOidcRedirectResponse, GetOidcRedirectError> {
|
||||||
let oidc = self.oidc.as_ref().ok_or(GetOidcRedirectError::Disabled)?;
|
let oidc = self.oidc().ok_or(GetOidcRedirectError::Disabled)?;
|
||||||
|
|
||||||
oidc.get_redirect(request.into())
|
oidc.get_redirect(request.into())
|
||||||
.await
|
.await
|
||||||
@@ -298,7 +302,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
request: LoginUserOidcRequest,
|
request: LoginUserOidcRequest,
|
||||||
) -> Result<LoginUserOidcResponse, LoginUserOidcError> {
|
) -> Result<LoginUserOidcResponse, LoginUserOidcError> {
|
||||||
let oidc = self.oidc.as_ref().ok_or(LoginUserOidcError::Disabled)?;
|
let oidc = self.oidc().ok_or(LoginUserOidcError::Disabled)?;
|
||||||
|
|
||||||
let user_info = oidc.get_user_info(request.into()).await?;
|
let user_info = oidc.get_user_info(request.into()).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -97,19 +97,22 @@ where
|
|||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mv(&self, request: MvRequest) -> Result<(), MvError> {
|
async fn mv(
|
||||||
let old_path = request.path().clone();
|
&self,
|
||||||
let new_path = request.target_path().clone();
|
request: MvRequest,
|
||||||
let result = self.repository.mv(request).await;
|
) -> Vec<Result<(AbsoluteFilePath, AbsoluteFilePath), MvError>> {
|
||||||
|
let results = self.repository.mv(request).await;
|
||||||
|
|
||||||
if result.is_ok() {
|
for result in results.iter() {
|
||||||
self.metrics.record_mv_success().await;
|
if let Ok((old_path, new_path)) = result.as_ref() {
|
||||||
self.notifier.mv(&old_path, &new_path).await;
|
self.metrics.record_mv_success().await;
|
||||||
} else {
|
self.notifier.mv(old_path, new_path).await;
|
||||||
self.metrics.record_mv_failure().await;
|
} else {
|
||||||
|
self.metrics.record_mv_failure().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save(&self, request: SaveRequest<'_>) -> Result<SaveResponse, SaveError> {
|
async fn save(&self, request: SaveRequest<'_>) -> Result<SaveResponse, SaveError> {
|
||||||
|
|||||||
@@ -269,26 +269,21 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn warren_mv(&self, request: WarrenMvRequest) -> Result<WarrenMvResponse, WarrenMvError> {
|
async fn warren_mv(&self, request: WarrenMvRequest) -> Result<WarrenMvResponse, WarrenMvError> {
|
||||||
let warren = self.repository.fetch_warren((&request).into()).await?;
|
let warren = match self.repository.fetch_warren((&request).into()).await {
|
||||||
|
Ok(warren) => warren,
|
||||||
|
Err(e) => {
|
||||||
|
self.metrics.record_warren_mv_failure().await;
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let old_path = request.base().path().clone();
|
|
||||||
let new_path = request.base().target_path().clone();
|
|
||||||
let mv_request = request.build_fs_request(&warren);
|
let mv_request = request.build_fs_request(&warren);
|
||||||
let result = self
|
let response = WarrenMvResponse::new(warren, self.fs_service.mv(mv_request).await);
|
||||||
.fs_service
|
|
||||||
.mv(mv_request)
|
|
||||||
.await
|
|
||||||
.map(|_| WarrenMvResponse::new(warren, old_path, new_path))
|
|
||||||
.map_err(Into::into);
|
|
||||||
|
|
||||||
if let Ok(response) = result.as_ref() {
|
self.metrics.record_warren_mv_success().await;
|
||||||
self.metrics.record_warren_mv_success().await;
|
self.notifier.warren_mv(&response).await;
|
||||||
self.notifier.warren_mv(response).await;
|
|
||||||
} else {
|
|
||||||
self.metrics.record_warren_mv_failure().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn warren_touch(
|
async fn warren_touch(
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ use crate::{
|
|||||||
domain::warren::{
|
domain::warren::{
|
||||||
models::{
|
models::{
|
||||||
auth_session::AuthRequest,
|
auth_session::AuthRequest,
|
||||||
file::{AbsoluteFilePath, AbsoluteFilePathError, FilePath, FilePathError, MvRequest},
|
file::{
|
||||||
|
AbsoluteFilePath, AbsoluteFilePathError, AbsoluteFilePathList,
|
||||||
|
AbsoluteFilePathListError, FilePath, FilePathError, MvRequest,
|
||||||
|
},
|
||||||
warren::WarrenMvRequest,
|
warren::WarrenMvRequest,
|
||||||
},
|
},
|
||||||
ports::{AuthService, WarrenService},
|
ports::{AuthService, WarrenService},
|
||||||
@@ -23,7 +26,7 @@ use crate::{
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MvWarrenEntryHttpRequestBody {
|
pub struct MvWarrenEntryHttpRequestBody {
|
||||||
warren_id: Uuid,
|
warren_id: Uuid,
|
||||||
path: String,
|
paths: Vec<String>,
|
||||||
target_path: String,
|
target_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,16 +36,25 @@ pub enum ParseWarrenMvHttpRequestError {
|
|||||||
FilePath(#[from] FilePathError),
|
FilePath(#[from] FilePathError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
AbsoluteFilePath(#[from] AbsoluteFilePathError),
|
AbsoluteFilePath(#[from] AbsoluteFilePathError),
|
||||||
|
#[error(transparent)]
|
||||||
|
AbsoluteFilePathList(#[from] AbsoluteFilePathListError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MvWarrenEntryHttpRequestBody {
|
impl MvWarrenEntryHttpRequestBody {
|
||||||
fn try_into_domain(self) -> Result<WarrenMvRequest, ParseWarrenMvHttpRequestError> {
|
fn try_into_domain(self) -> Result<WarrenMvRequest, ParseWarrenMvHttpRequestError> {
|
||||||
let path: AbsoluteFilePath = FilePath::new(&self.path)?.try_into()?;
|
let mut paths = Vec::<AbsoluteFilePath>::new();
|
||||||
|
|
||||||
|
for path in self.paths.iter() {
|
||||||
|
paths.push(FilePath::new(path)?.try_into()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let path_list = AbsoluteFilePathList::new(paths)?;
|
||||||
|
|
||||||
let target_path: AbsoluteFilePath = FilePath::new(&self.target_path)?.try_into()?;
|
let target_path: AbsoluteFilePath = FilePath::new(&self.target_path)?.try_into()?;
|
||||||
|
|
||||||
Ok(WarrenMvRequest::new(
|
Ok(WarrenMvRequest::new(
|
||||||
self.warren_id,
|
self.warren_id,
|
||||||
MvRequest::new(path, target_path),
|
MvRequest::new(path_list, target_path),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,12 +64,17 @@ impl From<ParseWarrenMvHttpRequestError> for ApiError {
|
|||||||
match value {
|
match value {
|
||||||
ParseWarrenMvHttpRequestError::FilePath(err) => match err {
|
ParseWarrenMvHttpRequestError::FilePath(err) => match err {
|
||||||
FilePathError::InvalidPath => {
|
FilePathError::InvalidPath => {
|
||||||
ApiError::BadRequest("The file path must be valid".to_string())
|
Self::BadRequest("The file path must be valid".to_string())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ParseWarrenMvHttpRequestError::AbsoluteFilePath(err) => match err {
|
ParseWarrenMvHttpRequestError::AbsoluteFilePath(err) => match err {
|
||||||
AbsoluteFilePathError::NotAbsolute => {
|
AbsoluteFilePathError::NotAbsolute => {
|
||||||
ApiError::BadRequest("The file path must be absolute".to_string())
|
Self::BadRequest("The file path must be absolute".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ParseWarrenMvHttpRequestError::AbsoluteFilePathList(err) => match err {
|
||||||
|
AbsoluteFilePathListError::Empty => {
|
||||||
|
Self::BadRequest("You must provide at least 1 path".to_string())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,19 +302,50 @@ impl FileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mv(&self, path: &AbsoluteFilePath, target_path: &AbsoluteFilePath) -> io::Result<()> {
|
async fn mv(
|
||||||
let current_path = self.get_target_path(path);
|
&self,
|
||||||
let target_path = self.get_target_path(target_path);
|
path: &AbsoluteFilePath,
|
||||||
|
target_path: &AbsoluteFilePath,
|
||||||
|
) -> io::Result<(AbsoluteFilePath, AbsoluteFilePath)> {
|
||||||
|
let mut target_path = target_path.clone();
|
||||||
|
|
||||||
if !fs::try_exists(¤t_path).await? {
|
let current_fs_path = self.get_target_path(path);
|
||||||
|
let mut target_fs_path = self.get_target_path(&target_path);
|
||||||
|
|
||||||
|
if !fs::try_exists(¤t_fs_path).await? {
|
||||||
return Err(io::ErrorKind::NotFound.into());
|
return Err(io::ErrorKind::NotFound.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if fs::try_exists(&target_path).await? {
|
if !fs::try_exists(&target_fs_path).await? {
|
||||||
|
return fs::rename(current_fs_path, target_fs_path)
|
||||||
|
.await
|
||||||
|
.map(|_| (path.clone(), target_path.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_is_dir = fs::metadata(target_fs_path).await?.is_dir();
|
||||||
|
|
||||||
|
if !target_is_dir {
|
||||||
return Err(io::ErrorKind::AlreadyExists.into());
|
return Err(io::ErrorKind::AlreadyExists.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::rename(current_path, &target_path).await
|
let name = {
|
||||||
|
let current_path = path.as_str();
|
||||||
|
if let Some(last_slash_index) = current_path.rfind("/")
|
||||||
|
&& last_slash_index > 0
|
||||||
|
{
|
||||||
|
¤t_path[last_slash_index + 1..]
|
||||||
|
} else {
|
||||||
|
return Err(io::ErrorKind::AlreadyExists.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
target_path =
|
||||||
|
target_path.join(&RelativeFilePath::new(FilePath::new(name).unwrap()).unwrap());
|
||||||
|
target_fs_path = self.get_target_path(&target_path);
|
||||||
|
|
||||||
|
fs::rename(current_fs_path, target_fs_path)
|
||||||
|
.await
|
||||||
|
.map(|_| (path.clone(), target_path.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn touch(&self, path: &AbsoluteFilePath) -> io::Result<()> {
|
async fn touch(&self, path: &AbsoluteFilePath) -> io::Result<()> {
|
||||||
@@ -431,29 +462,42 @@ impl FileSystemRepository for FileSystem {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let results: Vec<Result<AbsoluteFilePath, RmError>> = join_all(
|
join_all(
|
||||||
paths
|
paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|path| _rm(&self, path, force))
|
.map(|path| _rm(&self, path, force))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
|
||||||
results
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mv(&self, request: MvRequest) -> Result<(), MvError> {
|
async fn mv(
|
||||||
self.mv(request.path(), request.target_path())
|
&self,
|
||||||
.await
|
request: MvRequest,
|
||||||
.map_err(|e| match e.kind() {
|
) -> Vec<Result<(AbsoluteFilePath, AbsoluteFilePath), MvError>> {
|
||||||
std::io::ErrorKind::NotFound => MvError::NotFound,
|
async fn _mv(
|
||||||
_ => anyhow!(
|
fs: &FileSystem,
|
||||||
"Failed to move {} to {}: {e:?}",
|
path: AbsoluteFilePath,
|
||||||
request.path(),
|
target_path: &AbsoluteFilePath,
|
||||||
request.target_path()
|
) -> Result<(AbsoluteFilePath, AbsoluteFilePath), MvError> {
|
||||||
)
|
fs.mv(&path, target_path).await.map_err(|e| match e.kind() {
|
||||||
.into(),
|
std::io::ErrorKind::NotFound => MvError::NotFound(path),
|
||||||
|
_ => MvError::Unknown(
|
||||||
|
anyhow!("Failed to move {} to {}: {e:?}", path, target_path).into(),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let (path_list, target_path) = request.unpack();
|
||||||
|
let paths = Vec::<AbsoluteFilePath>::from(path_list);
|
||||||
|
|
||||||
|
join_all(
|
||||||
|
paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| _mv(&self, path, &target_path))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn touch(&self, request: TouchRequest) -> Result<(), TouchError> {
|
async fn touch(&self, request: TouchRequest) -> Result<(), TouchError> {
|
||||||
|
|||||||
@@ -110,12 +110,28 @@ impl WarrenNotifier for NotifierDebugLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn warren_mv(&self, response: &WarrenMvResponse) {
|
async fn warren_mv(&self, response: &WarrenMvResponse) {
|
||||||
tracing::debug!(
|
let span = tracing::debug_span!("warren_mv", "{}", response.warren().name()).entered();
|
||||||
"[Notifier] Renamed file {} to {} in warren {}",
|
|
||||||
response.old_path(),
|
let results = response.results();
|
||||||
response.path(),
|
|
||||||
response.warren().name(),
|
for result in results {
|
||||||
);
|
match result.as_ref() {
|
||||||
|
Ok((old_path, new_path)) => {
|
||||||
|
tracing::debug!("Moved file {old_path} to {new_path}")
|
||||||
|
}
|
||||||
|
Err(e) => match e {
|
||||||
|
crate::domain::warren::models::file::MvError::NotFound(path) => {
|
||||||
|
tracing::debug!("File not found: {path}")
|
||||||
|
}
|
||||||
|
crate::domain::warren::models::file::MvError::AlreadyExists(path) => {
|
||||||
|
tracing::debug!("File already exists: {path}")
|
||||||
|
}
|
||||||
|
crate::domain::warren::models::file::MvError::Unknown(_) => (),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn warren_touch(&self, warren: &Warren, path: &AbsoluteFilePath) {
|
async fn warren_touch(&self, warren: &Warren, path: &AbsoluteFilePath) {
|
||||||
@@ -418,10 +434,11 @@ impl AuthNotifier for NotifierDebugLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn auth_warren_mv(&self, user: &User, response: &WarrenMvResponse) {
|
async fn auth_warren_mv(&self, user: &User, response: &WarrenMvResponse) {
|
||||||
|
let results = response.results();
|
||||||
|
let successes = results.iter().filter(|r| r.is_ok()).count();
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"[Notifier] Renamed file {} to {} in warren {} for authenticated user {}",
|
"[Notifier] Moved {successes} file(s) in warren {} for authenticated user {}",
|
||||||
response.old_path(),
|
|
||||||
response.path(),
|
|
||||||
response.warren().name(),
|
response.warren().name(),
|
||||||
user.id(),
|
user.id(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -382,9 +382,9 @@ export async function fetchFileStream(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function moveFile(
|
export async function moveFiles(
|
||||||
warrenId: string,
|
warrenId: string,
|
||||||
currentPath: string,
|
currentPaths: string[],
|
||||||
targetPath: string
|
targetPath: string
|
||||||
): Promise<{ success: boolean }> {
|
): Promise<{ success: boolean }> {
|
||||||
const { status } = await useFetch(getApiUrl(`warrens/files/mv`), {
|
const { status } = await useFetch(getApiUrl(`warrens/files/mv`), {
|
||||||
@@ -392,7 +392,7 @@ export async function moveFile(
|
|||||||
headers: getApiHeaders(),
|
headers: getApiHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
warrenId,
|
warrenId,
|
||||||
path: currentPath,
|
paths: currentPaths,
|
||||||
targetPath: targetPath,
|
targetPath: targetPath,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ definePageMeta({
|
|||||||
layout: 'share',
|
layout: 'share',
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectionRect = useSelectionRect();
|
|
||||||
const warrenStore = useWarrenStore();
|
const warrenStore = useWarrenStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { copyFile, moveFile } from '~/lib/api/warrens';
|
import { copyFile, moveFiles } from '~/lib/api/warrens';
|
||||||
import type { DirectoryEntry } from '~/shared/types';
|
import type { DirectoryEntry } from '~/shared/types';
|
||||||
|
|
||||||
export function joinPaths(path: string, ...other: string[]): string {
|
export function joinPaths(path: string, ...other: string[]): string {
|
||||||
@@ -25,7 +25,11 @@ export function onDirectoryEntryDrop(
|
|||||||
return async (e: DragEvent) => {
|
return async (e: DragEvent) => {
|
||||||
const warrenStore = useWarrenStore();
|
const warrenStore = useWarrenStore();
|
||||||
|
|
||||||
if (e.dataTransfer == null || warrenStore.current == null) {
|
if (
|
||||||
|
e.dataTransfer == null ||
|
||||||
|
warrenStore.current == null ||
|
||||||
|
warrenStore.current.dir == null
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,23 +43,29 @@ export function onDirectoryEntryDrop(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentPath = joinPaths(warrenStore.current.path, fileName);
|
const draggedEntry = warrenStore.current.dir.entries.find(
|
||||||
|
(e) => e.name === fileName
|
||||||
|
);
|
||||||
|
if (draggedEntry == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetPaths = getTargetsFromSelection(
|
||||||
|
draggedEntry,
|
||||||
|
warrenStore.selection
|
||||||
|
).map((currentEntry) =>
|
||||||
|
joinPaths(warrenStore.current!.path, currentEntry.name)
|
||||||
|
);
|
||||||
|
|
||||||
let targetPath: string;
|
let targetPath: string;
|
||||||
|
|
||||||
if (isParent) {
|
if (isParent) {
|
||||||
targetPath = joinPaths(
|
targetPath = getParentPath(warrenStore.current.path);
|
||||||
getParentPath(warrenStore.current.path),
|
|
||||||
fileName
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
targetPath = joinPaths(
|
targetPath = joinPaths(warrenStore.current.path, entry.name);
|
||||||
warrenStore.current.path,
|
|
||||||
entry.name,
|
|
||||||
fileName
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await moveFile(warrenStore.current.warrenId, currentPath, targetPath);
|
await moveFiles(warrenStore.current.warrenId, targetPaths, targetPath);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user