mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-18 11:59:11 +00:00
Merge branch 'main' of github.com:spacedriveapp/spacedrive into consistent-formatting-please
This commit is contained in:
commit
49218cb1bb
|
@ -3,8 +3,8 @@ import React, { Suspense } from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import '@sd/ui/style';
|
import '@sd/ui/style';
|
||||||
// THIS MUST GO BEFORE importing the App
|
// THIS MUST GO BEFORE importing the App
|
||||||
|
import '~/patches';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import './patches';
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||||
root.render(
|
root.render(
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
"extends": "../../packages/config/base.tsconfig.json",
|
"extends": "../../packages/config/base.tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"declarationDir": "dist"
|
"declarationDir": "dist",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"references": [
|
"references": [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
location::{indexer::IndexerError, LocationError},
|
location::{indexer::IndexerError, LocationError, LocationManagerError},
|
||||||
object::{identifier_job::IdentifierJobError, preview::ThumbnailError},
|
object::{identifier_job::IdentifierJobError, preview::ThumbnailError},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ pub enum JobError {
|
||||||
MissingJobDataState(Uuid, String),
|
MissingJobDataState(Uuid, String),
|
||||||
#[error("missing some job data: '{value}'")]
|
#[error("missing some job data: '{value}'")]
|
||||||
MissingData { value: String },
|
MissingData { value: String },
|
||||||
|
#[error("Location manager error: {0}")]
|
||||||
|
LocationManager(#[from] LocationManagerError),
|
||||||
|
|
||||||
// Specific job errors
|
// Specific job errors
|
||||||
#[error("Indexer error: {0}")]
|
#[error("Indexer error: {0}")]
|
||||||
|
|
|
@ -77,6 +77,11 @@ impl Node {
|
||||||
.parse()
|
.parse()
|
||||||
.expect("Error invalid tracing directive!"),
|
.expect("Error invalid tracing directive!"),
|
||||||
)
|
)
|
||||||
|
.add_directive(
|
||||||
|
"sd_core::location::manager=info"
|
||||||
|
.parse()
|
||||||
|
.expect("Error invalid tracing directive!"),
|
||||||
|
)
|
||||||
.add_directive(
|
.add_directive(
|
||||||
"sd_core_mobile=debug"
|
"sd_core_mobile=debug"
|
||||||
.parse()
|
.parse()
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use crate::{library::LibraryContext, prisma::location};
|
use crate::{library::LibraryContext, prisma::location};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tokio::{fs, io::ErrorKind, time::sleep};
|
use tokio::{fs, io::ErrorKind, sync::oneshot, time::sleep};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::{watcher::LocationWatcher, LocationId};
|
use super::{watcher::LocationWatcher, LocationId, LocationManagerError};
|
||||||
|
|
||||||
type LibraryId = Uuid;
|
type LibraryId = Uuid;
|
||||||
type LocationAndLibraryKey = (LocationId, LibraryId);
|
type LocationAndLibraryKey = (LocationId, LibraryId);
|
||||||
|
@ -163,3 +163,176 @@ pub(super) fn subtract_location_path(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) async fn handle_remove_location_request(
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
response_tx: oneshot::Sender<Result<(), LocationManagerError>>,
|
||||||
|
forced_unwatch: &mut HashSet<LocationAndLibraryKey>,
|
||||||
|
locations_watched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
locations_unwatched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
to_remove: &mut HashSet<LocationAndLibraryKey>,
|
||||||
|
) {
|
||||||
|
let key = (location_id, library_ctx.id);
|
||||||
|
if let Some(location) = get_location(location_id, &library_ctx).await {
|
||||||
|
if let Some(ref local_path_str) = location.local_path.clone() {
|
||||||
|
unwatch_location(
|
||||||
|
location,
|
||||||
|
library_ctx.id,
|
||||||
|
local_path_str,
|
||||||
|
locations_watched,
|
||||||
|
locations_unwatched,
|
||||||
|
);
|
||||||
|
locations_unwatched.remove(&key);
|
||||||
|
forced_unwatch.remove(&key);
|
||||||
|
} else {
|
||||||
|
drop_location(
|
||||||
|
location_id,
|
||||||
|
library_ctx.id,
|
||||||
|
"Dropping location from location manager, because we don't have a `local_path` anymore",
|
||||||
|
locations_watched,
|
||||||
|
locations_unwatched
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drop_location(
|
||||||
|
location_id,
|
||||||
|
library_ctx.id,
|
||||||
|
"Removing location from manager, as we failed to fetch from db",
|
||||||
|
locations_watched,
|
||||||
|
locations_unwatched,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marking location as removed, so we don't try to check it when the time comes
|
||||||
|
to_remove.insert(key);
|
||||||
|
|
||||||
|
let _ = response_tx.send(Ok(())); // ignore errors, we handle errors on receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn handle_stop_watcher_request(
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
response_tx: oneshot::Sender<Result<(), LocationManagerError>>,
|
||||||
|
forced_unwatch: &mut HashSet<LocationAndLibraryKey>,
|
||||||
|
locations_watched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
locations_unwatched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
) {
|
||||||
|
async fn inner(
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
forced_unwatch: &mut HashSet<LocationAndLibraryKey>,
|
||||||
|
locations_watched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
locations_unwatched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
) -> Result<(), LocationManagerError> {
|
||||||
|
let key = (location_id, library_ctx.id);
|
||||||
|
if !forced_unwatch.contains(&key) && locations_watched.contains_key(&key) {
|
||||||
|
get_location(location_id, &library_ctx)
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| LocationManagerError::FailedToStopOrReinitWatcher {
|
||||||
|
reason: String::from("failed to fetch location from db"),
|
||||||
|
})
|
||||||
|
.map(|location| {
|
||||||
|
location
|
||||||
|
.local_path
|
||||||
|
.clone()
|
||||||
|
.ok_or(LocationManagerError::LocationMissingLocalPath(location_id))
|
||||||
|
.map(|local_path_str| {
|
||||||
|
unwatch_location(
|
||||||
|
location,
|
||||||
|
library_ctx.id,
|
||||||
|
local_path_str,
|
||||||
|
locations_watched,
|
||||||
|
locations_unwatched,
|
||||||
|
);
|
||||||
|
forced_unwatch.insert(key);
|
||||||
|
})
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = response_tx.send(
|
||||||
|
inner(
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
forced_unwatch,
|
||||||
|
locations_watched,
|
||||||
|
locations_unwatched,
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
); // ignore errors, we handle errors on receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn handle_reinit_watcher_request(
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
response_tx: oneshot::Sender<Result<(), LocationManagerError>>,
|
||||||
|
forced_unwatch: &mut HashSet<LocationAndLibraryKey>,
|
||||||
|
locations_watched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
locations_unwatched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
) {
|
||||||
|
async fn inner(
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
forced_unwatch: &mut HashSet<LocationAndLibraryKey>,
|
||||||
|
locations_watched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
locations_unwatched: &mut HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
) -> Result<(), LocationManagerError> {
|
||||||
|
let key = (location_id, library_ctx.id);
|
||||||
|
if forced_unwatch.contains(&key) && locations_unwatched.contains_key(&key) {
|
||||||
|
get_location(location_id, &library_ctx)
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| LocationManagerError::FailedToStopOrReinitWatcher {
|
||||||
|
reason: String::from("failed to fetch location from db"),
|
||||||
|
})
|
||||||
|
.map(|location| {
|
||||||
|
location
|
||||||
|
.local_path
|
||||||
|
.clone()
|
||||||
|
.ok_or(LocationManagerError::LocationMissingLocalPath(location_id))
|
||||||
|
.map(|local_path_str| {
|
||||||
|
watch_location(
|
||||||
|
location,
|
||||||
|
library_ctx.id,
|
||||||
|
local_path_str,
|
||||||
|
locations_watched,
|
||||||
|
locations_unwatched,
|
||||||
|
);
|
||||||
|
forced_unwatch.remove(&key);
|
||||||
|
})
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = response_tx.send(
|
||||||
|
inner(
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
forced_unwatch,
|
||||||
|
locations_watched,
|
||||||
|
locations_unwatched,
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
); // ignore errors, we handle errors on receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_ignore_path_request(
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
path: PathBuf,
|
||||||
|
ignore: bool,
|
||||||
|
response_tx: oneshot::Sender<Result<(), LocationManagerError>>,
|
||||||
|
locations_watched: &HashMap<LocationAndLibraryKey, LocationWatcher>,
|
||||||
|
) {
|
||||||
|
let _ = response_tx.send(
|
||||||
|
if let Some(watcher) = locations_watched.get(&(location_id, library_ctx.id)) {
|
||||||
|
watcher.ignore_path(path, ignore)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
); // ignore errors, we handle errors on receiver
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
use crate::library::LibraryContext;
|
use crate::library::LibraryContext;
|
||||||
|
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
use thiserror::Error;
|
sync::Arc,
|
||||||
use tokio::{
|
|
||||||
io,
|
|
||||||
sync::{mpsc, oneshot},
|
|
||||||
};
|
};
|
||||||
use tracing::{debug, error};
|
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use thiserror::Error;
|
||||||
|
use tokio::{io, sync::oneshot};
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
#[cfg(feature = "location-watcher")]
|
#[cfg(feature = "location-watcher")]
|
||||||
mod watcher;
|
mod watcher;
|
||||||
|
@ -17,16 +24,53 @@ mod helpers;
|
||||||
|
|
||||||
pub type LocationId = i32;
|
pub type LocationId = i32;
|
||||||
|
|
||||||
type ManagerMessage = (
|
#[derive(Clone, Copy, Debug)]
|
||||||
LocationId,
|
#[allow(dead_code)]
|
||||||
LibraryContext,
|
enum ManagementMessageAction {
|
||||||
oneshot::Sender<Result<(), LocationManagerError>>,
|
Add,
|
||||||
);
|
Remove,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct LocationManagementMessage {
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
action: ManagementMessageAction,
|
||||||
|
response_tx: oneshot::Sender<Result<(), LocationManagerError>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum WatcherManagementMessageAction {
|
||||||
|
Stop,
|
||||||
|
Reinit,
|
||||||
|
IgnoreEventsForPath { path: PathBuf, ignore: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct WatcherManagementMessage {
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
action: WatcherManagementMessageAction,
|
||||||
|
response_tx: oneshot::Sender<Result<(), LocationManagerError>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum LocationManagerError {
|
pub enum LocationManagerError {
|
||||||
#[error("Unable to send location id to be checked by actor: (error: {0})")]
|
#[cfg(feature = "location-watcher")]
|
||||||
ActorSendLocationError(#[from] mpsc::error::SendError<ManagerMessage>),
|
#[error("Unable to send location management message to location manager actor: (error: {0})")]
|
||||||
|
ActorSendLocationError(#[from] mpsc::error::SendError<LocationManagementMessage>),
|
||||||
|
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
#[error("Unable to send path to be ignored by watcher actor: (error: {0})")]
|
||||||
|
ActorIgnorePathError(#[from] mpsc::error::SendError<watcher::IgnorePath>),
|
||||||
|
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
#[error("Unable to watcher management message to watcher manager actor: (error: {0})")]
|
||||||
|
ActorIgnorePathMessageError(#[from] mpsc::error::SendError<WatcherManagementMessage>),
|
||||||
|
|
||||||
#[error("Unable to receive actor response: (error: {0})")]
|
#[error("Unable to receive actor response: (error: {0})")]
|
||||||
ActorResponseError(#[from] oneshot::error::RecvError),
|
ActorResponseError(#[from] oneshot::error::RecvError),
|
||||||
|
|
||||||
|
@ -34,6 +78,9 @@ pub enum LocationManagerError {
|
||||||
#[error("Watcher error: (error: {0})")]
|
#[error("Watcher error: (error: {0})")]
|
||||||
WatcherError(#[from] notify::Error),
|
WatcherError(#[from] notify::Error),
|
||||||
|
|
||||||
|
#[error("Failed to stop or reinit a watcher: {reason}")]
|
||||||
|
FailedToStopOrReinitWatcher { reason: String },
|
||||||
|
|
||||||
#[error("Location missing local path: <id='{0}'>")]
|
#[error("Location missing local path: <id='{0}'>")]
|
||||||
LocationMissingLocalPath(LocationId),
|
LocationMissingLocalPath(LocationId),
|
||||||
#[error("Tried to update a non-existing file: <path='{0}'>")]
|
#[error("Tried to update a non-existing file: <path='{0}'>")]
|
||||||
|
@ -48,35 +95,98 @@ pub enum LocationManagerError {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LocationManager {
|
pub struct LocationManager {
|
||||||
add_locations_tx: mpsc::Sender<ManagerMessage>,
|
#[cfg(feature = "location-watcher")]
|
||||||
remove_locations_tx: mpsc::Sender<ManagerMessage>,
|
location_management_tx: mpsc::Sender<LocationManagementMessage>,
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
watcher_management_tx: mpsc::Sender<WatcherManagementMessage>,
|
||||||
stop_tx: Option<oneshot::Sender<()>>,
|
stop_tx: Option<oneshot::Sender<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocationManager {
|
impl LocationManager {
|
||||||
#[allow(unused)]
|
|
||||||
pub fn new() -> Arc<Self> {
|
pub fn new() -> Arc<Self> {
|
||||||
let (add_locations_tx, add_locations_rx) = mpsc::channel(128);
|
|
||||||
let (remove_locations_tx, remove_locations_rx) = mpsc::channel(128);
|
|
||||||
let (stop_tx, stop_rx) = oneshot::channel();
|
|
||||||
|
|
||||||
#[cfg(feature = "location-watcher")]
|
#[cfg(feature = "location-watcher")]
|
||||||
tokio::spawn(Self::run_locations_checker(
|
{
|
||||||
add_locations_rx,
|
let (location_management_tx, location_management_rx) = mpsc::channel(128);
|
||||||
remove_locations_rx,
|
let (watcher_management_tx, watcher_management_rx) = mpsc::channel(128);
|
||||||
stop_rx,
|
let (stop_tx, stop_rx) = oneshot::channel();
|
||||||
));
|
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
tokio::spawn(Self::run_locations_checker(
|
||||||
|
location_management_rx,
|
||||||
|
watcher_management_rx,
|
||||||
|
stop_rx,
|
||||||
|
));
|
||||||
|
|
||||||
|
debug!("Location manager initialized");
|
||||||
|
|
||||||
|
Arc::new(Self {
|
||||||
|
location_management_tx,
|
||||||
|
watcher_management_tx,
|
||||||
|
stop_tx: Some(stop_tx),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "location-watcher"))]
|
#[cfg(not(feature = "location-watcher"))]
|
||||||
tracing::warn!("Location watcher is disabled, locations will not be checked");
|
{
|
||||||
|
tracing::warn!("Location watcher is disabled, locations will not be checked");
|
||||||
|
Arc::new(Self { stop_tx: None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug!("Location manager initialized");
|
#[inline]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn location_management_message(
|
||||||
|
&self,
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
action: ManagementMessageAction,
|
||||||
|
) -> Result<(), LocationManagerError> {
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
{
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
Arc::new(Self {
|
self.location_management_tx
|
||||||
add_locations_tx,
|
.send(LocationManagementMessage {
|
||||||
remove_locations_tx,
|
location_id,
|
||||||
stop_tx: Some(stop_tx),
|
library_ctx,
|
||||||
})
|
action,
|
||||||
|
response_tx: tx,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
rx.await?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "location-watcher"))]
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn watcher_management_message(
|
||||||
|
&self,
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
action: WatcherManagementMessageAction,
|
||||||
|
) -> Result<(), LocationManagerError> {
|
||||||
|
#[cfg(feature = "location-watcher")]
|
||||||
|
{
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
|
self.watcher_management_tx
|
||||||
|
.send(WatcherManagementMessage {
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
action,
|
||||||
|
response_tx: tx,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
rx.await?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "location-watcher"))]
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add(
|
pub async fn add(
|
||||||
|
@ -84,17 +194,8 @@ impl LocationManager {
|
||||||
location_id: LocationId,
|
location_id: LocationId,
|
||||||
library_ctx: LibraryContext,
|
library_ctx: LibraryContext,
|
||||||
) -> Result<(), LocationManagerError> {
|
) -> Result<(), LocationManagerError> {
|
||||||
if cfg!(feature = "location-watcher") {
|
self.location_management_message(location_id, library_ctx, ManagementMessageAction::Add)
|
||||||
let (tx, rx) = oneshot::channel();
|
.await
|
||||||
|
|
||||||
self.add_locations_tx
|
|
||||||
.send((location_id, library_ctx, tx))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
rx.await?
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove(
|
pub async fn remove(
|
||||||
|
@ -102,23 +203,80 @@ impl LocationManager {
|
||||||
location_id: LocationId,
|
location_id: LocationId,
|
||||||
library_ctx: LibraryContext,
|
library_ctx: LibraryContext,
|
||||||
) -> Result<(), LocationManagerError> {
|
) -> Result<(), LocationManagerError> {
|
||||||
if cfg!(feature = "location-watcher") {
|
self.location_management_message(location_id, library_ctx, ManagementMessageAction::Remove)
|
||||||
let (tx, rx) = oneshot::channel();
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
self.remove_locations_tx
|
pub async fn stop_watcher(
|
||||||
.send((location_id, library_ctx, tx))
|
&self,
|
||||||
.await?;
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
) -> Result<(), LocationManagerError> {
|
||||||
|
self.watcher_management_message(
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
WatcherManagementMessageAction::Stop,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
rx.await?
|
pub async fn reinit_watcher(
|
||||||
} else {
|
&self,
|
||||||
Ok(())
|
location_id: LocationId,
|
||||||
}
|
library_ctx: LibraryContext,
|
||||||
|
) -> Result<(), LocationManagerError> {
|
||||||
|
self.watcher_management_message(
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
WatcherManagementMessageAction::Reinit,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn temporary_stop(
|
||||||
|
&self,
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
) -> Result<StopWatcherGuard, LocationManagerError> {
|
||||||
|
self.stop_watcher(location_id, library_ctx.clone()).await?;
|
||||||
|
|
||||||
|
Ok(StopWatcherGuard {
|
||||||
|
location_id,
|
||||||
|
library_ctx: Some(library_ctx),
|
||||||
|
manager: self,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn temporary_ignore_events_for_path(
|
||||||
|
&self,
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: LibraryContext,
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
) -> Result<IgnoreEventsForPathGuard, LocationManagerError> {
|
||||||
|
let path = path.as_ref().to_path_buf();
|
||||||
|
|
||||||
|
self.watcher_management_message(
|
||||||
|
location_id,
|
||||||
|
library_ctx.clone(),
|
||||||
|
WatcherManagementMessageAction::IgnoreEventsForPath {
|
||||||
|
path: path.clone(),
|
||||||
|
ignore: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(IgnoreEventsForPathGuard {
|
||||||
|
location_id,
|
||||||
|
library_ctx: Some(library_ctx),
|
||||||
|
manager: self,
|
||||||
|
path: Some(path),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "location-watcher")]
|
#[cfg(feature = "location-watcher")]
|
||||||
async fn run_locations_checker(
|
async fn run_locations_checker(
|
||||||
mut add_locations_rx: mpsc::Receiver<ManagerMessage>,
|
mut location_management_rx: mpsc::Receiver<LocationManagementMessage>,
|
||||||
mut remove_locations_rx: mpsc::Receiver<ManagerMessage>,
|
mut watcher_management_rx: mpsc::Receiver<WatcherManagementMessage>,
|
||||||
mut stop_rx: oneshot::Receiver<()>,
|
mut stop_rx: oneshot::Receiver<()>,
|
||||||
) -> Result<(), LocationManagerError> {
|
) -> Result<(), LocationManagerError> {
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
@ -128,8 +286,9 @@ impl LocationManager {
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use helpers::{
|
use helpers::{
|
||||||
check_online, drop_location, get_location, location_check_sleep, unwatch_location,
|
check_online, drop_location, get_location, handle_ignore_path_request,
|
||||||
watch_location,
|
handle_reinit_watcher_request, handle_remove_location_request,
|
||||||
|
handle_stop_watcher_request, location_check_sleep, unwatch_location, watch_location,
|
||||||
};
|
};
|
||||||
use watcher::LocationWatcher;
|
use watcher::LocationWatcher;
|
||||||
|
|
||||||
|
@ -137,95 +296,133 @@ impl LocationManager {
|
||||||
let mut to_remove = HashSet::new();
|
let mut to_remove = HashSet::new();
|
||||||
let mut locations_watched = HashMap::new();
|
let mut locations_watched = HashMap::new();
|
||||||
let mut locations_unwatched = HashMap::new();
|
let mut locations_unwatched = HashMap::new();
|
||||||
|
let mut forced_unwatch = HashSet::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
// To add a new location
|
// Location management messages
|
||||||
Some((location_id, library_ctx, response_tx)) = add_locations_rx.recv() => {
|
Some(LocationManagementMessage{
|
||||||
if let Some(location) = get_location(location_id, &library_ctx).await {
|
location_id,
|
||||||
let is_online = check_online(&location, &library_ctx).await;
|
library_ctx,
|
||||||
let _ = response_tx.send(
|
action,
|
||||||
LocationWatcher::new(location, library_ctx.clone())
|
response_tx
|
||||||
.await
|
}) = location_management_rx.recv() => {
|
||||||
.map(|mut watcher| {
|
match action {
|
||||||
if is_online {
|
|
||||||
watcher.watch();
|
|
||||||
locations_watched.insert(
|
|
||||||
(location_id, library_ctx.id),
|
|
||||||
watcher
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
locations_unwatched.insert(
|
|
||||||
(location_id, library_ctx.id),
|
|
||||||
watcher
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
to_check_futures.push(
|
// To add a new location
|
||||||
location_check_sleep(location_id, library_ctx)
|
ManagementMessageAction::Add => {
|
||||||
);
|
if let Some(location) = get_location(location_id, &library_ctx).await {
|
||||||
}
|
let is_online = check_online(&location, &library_ctx).await;
|
||||||
)
|
let _ = response_tx.send(
|
||||||
); // ignore errors, we handle errors on receiver
|
LocationWatcher::new(location, library_ctx.clone())
|
||||||
} else {
|
.await
|
||||||
warn!(
|
.map(|mut watcher| {
|
||||||
"Location not found in database to be watched: {}",
|
if is_online {
|
||||||
location_id
|
watcher.watch();
|
||||||
);
|
locations_watched.insert(
|
||||||
|
(location_id, library_ctx.id),
|
||||||
|
watcher
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
locations_unwatched.insert(
|
||||||
|
(location_id, library_ctx.id),
|
||||||
|
watcher
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_check_futures.push(
|
||||||
|
location_check_sleep(location_id, library_ctx)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
); // ignore errors, we handle errors on receiver
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Location not found in database to be watched: {}",
|
||||||
|
location_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// To remove an location
|
||||||
|
ManagementMessageAction::Remove => {
|
||||||
|
handle_remove_location_request(
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
response_tx,
|
||||||
|
&mut forced_unwatch,
|
||||||
|
&mut locations_watched,
|
||||||
|
&mut locations_unwatched,
|
||||||
|
&mut to_remove,
|
||||||
|
).await;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To remove an location
|
// Watcher management messages
|
||||||
Some((location_id, library_ctx, response_tx)) = remove_locations_rx.recv() => {
|
Some(WatcherManagementMessage{
|
||||||
if let Some(location) = get_location(location_id, &library_ctx).await {
|
location_id,
|
||||||
if let Some(ref local_path_str) = location.local_path.clone() {
|
library_ctx,
|
||||||
unwatch_location(
|
action,
|
||||||
location,
|
response_tx,
|
||||||
library_ctx.id,
|
}) = watcher_management_rx.recv() => {
|
||||||
local_path_str,
|
match action {
|
||||||
|
// To stop a watcher
|
||||||
|
WatcherManagementMessageAction::Stop => {
|
||||||
|
handle_stop_watcher_request(
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
response_tx,
|
||||||
|
&mut forced_unwatch,
|
||||||
&mut locations_watched,
|
&mut locations_watched,
|
||||||
&mut locations_unwatched,
|
&mut locations_unwatched,
|
||||||
);
|
).await;
|
||||||
locations_unwatched.remove(&(location_id, library_ctx.id));
|
},
|
||||||
} else {
|
|
||||||
drop_location(
|
// To reinit a stopped watcher
|
||||||
|
WatcherManagementMessageAction::Reinit => {
|
||||||
|
handle_reinit_watcher_request(
|
||||||
location_id,
|
location_id,
|
||||||
library_ctx.id,
|
library_ctx,
|
||||||
"Dropping location from location manager, because we don't have a `local_path` anymore",
|
response_tx,
|
||||||
|
&mut forced_unwatch,
|
||||||
&mut locations_watched,
|
&mut locations_watched,
|
||||||
&mut locations_unwatched
|
&mut locations_unwatched,
|
||||||
|
).await;
|
||||||
|
},
|
||||||
|
|
||||||
|
// To ignore or not events for a path
|
||||||
|
WatcherManagementMessageAction::IgnoreEventsForPath { path, ignore } => {
|
||||||
|
handle_ignore_path_request(
|
||||||
|
location_id,
|
||||||
|
library_ctx,
|
||||||
|
path,
|
||||||
|
ignore,
|
||||||
|
response_tx,
|
||||||
|
&locations_watched,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
} else {
|
|
||||||
drop_location(
|
|
||||||
location_id,
|
|
||||||
library_ctx.id,
|
|
||||||
"Removing location from manager, as we failed to fetch from db",
|
|
||||||
&mut locations_watched,
|
|
||||||
&mut locations_unwatched
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marking location as removed, so we don't try to check it when the time comes
|
|
||||||
to_remove.insert((location_id, library_ctx.id));
|
|
||||||
|
|
||||||
let _ = response_tx.send(Ok(())); // ignore errors, we handle errors on receiver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Periodically checking locations
|
// Periodically checking locations
|
||||||
Some((location_id, library_ctx)) = to_check_futures.next() => {
|
Some((location_id, library_ctx)) = to_check_futures.next() => {
|
||||||
if to_remove.contains(&(location_id, library_ctx.id)) {
|
let key = (location_id, library_ctx.id);
|
||||||
|
|
||||||
|
if to_remove.contains(&key) {
|
||||||
// The time to check came for an already removed library, so we just ignore it
|
// The time to check came for an already removed library, so we just ignore it
|
||||||
to_remove.remove(&(location_id, library_ctx.id));
|
to_remove.remove(&key);
|
||||||
} else if let Some(location) = get_location(location_id, &library_ctx).await {
|
} else if let Some(location) = get_location(location_id, &library_ctx).await {
|
||||||
if let Some(ref local_path_str) = location.local_path.clone() {
|
if let Some(ref local_path_str) = location.local_path.clone() {
|
||||||
if check_online(&location, &library_ctx).await {
|
if check_online(&location, &library_ctx).await
|
||||||
|
&& !forced_unwatch.contains(&key)
|
||||||
|
{
|
||||||
watch_location(
|
watch_location(
|
||||||
location,
|
location,
|
||||||
library_ctx.id,
|
library_ctx.id,
|
||||||
local_path_str,
|
local_path_str,
|
||||||
&mut locations_watched,
|
&mut locations_watched,
|
||||||
&mut locations_unwatched
|
&mut locations_unwatched,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
unwatch_location(
|
unwatch_location(
|
||||||
|
@ -233,7 +430,7 @@ impl LocationManager {
|
||||||
library_ctx.id,
|
library_ctx.id,
|
||||||
local_path_str,
|
local_path_str,
|
||||||
&mut locations_watched,
|
&mut locations_watched,
|
||||||
&mut locations_unwatched
|
&mut locations_unwatched,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
to_check_futures.push(location_check_sleep(location_id, library_ctx));
|
to_check_futures.push(location_check_sleep(location_id, library_ctx));
|
||||||
|
@ -241,10 +438,12 @@ impl LocationManager {
|
||||||
drop_location(
|
drop_location(
|
||||||
location_id,
|
location_id,
|
||||||
library_ctx.id,
|
library_ctx.id,
|
||||||
"Dropping location from location manager, because we don't have a `local_path` anymore",
|
"Dropping location from location manager, because \
|
||||||
|
we don't have a `local_path` anymore",
|
||||||
&mut locations_watched,
|
&mut locations_watched,
|
||||||
&mut locations_unwatched
|
&mut locations_unwatched
|
||||||
);
|
);
|
||||||
|
forced_unwatch.remove(&key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
drop_location(
|
drop_location(
|
||||||
|
@ -252,8 +451,9 @@ impl LocationManager {
|
||||||
library_ctx.id,
|
library_ctx.id,
|
||||||
"Removing location from manager, as we failed to fetch from db",
|
"Removing location from manager, as we failed to fetch from db",
|
||||||
&mut locations_watched,
|
&mut locations_watched,
|
||||||
&mut locations_unwatched
|
&mut locations_unwatched,
|
||||||
);
|
);
|
||||||
|
forced_unwatch.remove(&key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,3 +477,50 @@ impl Drop for LocationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use = "this `StopWatcherGuard` must be held for some time, so the watcher is stopped"]
|
||||||
|
pub struct StopWatcherGuard<'m> {
|
||||||
|
manager: &'m LocationManager,
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: Option<LibraryContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for StopWatcherGuard<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if cfg!(feature = "location-watcher") {
|
||||||
|
// FIXME: change this Drop to async drop in the future
|
||||||
|
if let Err(e) = block_on(
|
||||||
|
self.manager
|
||||||
|
.reinit_watcher(self.location_id, self.library_ctx.take().unwrap()),
|
||||||
|
) {
|
||||||
|
error!("Failed to reinit watcher on stop watcher guard drop: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use = "this `IgnoreEventsForPathGuard` must be held for some time, so the watcher can ignore events for the desired path"]
|
||||||
|
pub struct IgnoreEventsForPathGuard<'m> {
|
||||||
|
manager: &'m LocationManager,
|
||||||
|
path: Option<PathBuf>,
|
||||||
|
location_id: LocationId,
|
||||||
|
library_ctx: Option<LibraryContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IgnoreEventsForPathGuard<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if cfg!(feature = "location-watcher") {
|
||||||
|
// FIXME: change this Drop to async drop in the future
|
||||||
|
if let Err(e) = block_on(self.manager.watcher_management_message(
|
||||||
|
self.location_id,
|
||||||
|
self.library_ctx.take().unwrap(),
|
||||||
|
WatcherManagementMessageAction::IgnoreEventsForPath {
|
||||||
|
path: self.path.take().unwrap(),
|
||||||
|
ignore: false,
|
||||||
|
},
|
||||||
|
)) {
|
||||||
|
error!("Failed to un-ignore path on watcher guard drop: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@ use crate::{
|
||||||
prisma::{file_path, location},
|
prisma::{file_path, location},
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
@ -39,6 +42,8 @@ type Handler = windows::WindowsEventHandler;
|
||||||
|
|
||||||
file_path::include!(file_path_with_object { object });
|
file_path::include!(file_path_with_object { object });
|
||||||
|
|
||||||
|
pub(super) type IgnorePath = (PathBuf, bool);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
trait EventHandler {
|
trait EventHandler {
|
||||||
fn new() -> Self
|
fn new() -> Self
|
||||||
|
@ -58,6 +63,7 @@ pub(super) struct LocationWatcher {
|
||||||
location: location::Data,
|
location: location::Data,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
watcher: RecommendedWatcher,
|
watcher: RecommendedWatcher,
|
||||||
|
ignore_path_tx: mpsc::UnboundedSender<IgnorePath>,
|
||||||
handle: Option<JoinHandle<()>>,
|
handle: Option<JoinHandle<()>>,
|
||||||
stop_tx: Option<oneshot::Sender<()>>,
|
stop_tx: Option<oneshot::Sender<()>>,
|
||||||
}
|
}
|
||||||
|
@ -68,6 +74,7 @@ impl LocationWatcher {
|
||||||
library_ctx: LibraryContext,
|
library_ctx: LibraryContext,
|
||||||
) -> Result<Self, LocationManagerError> {
|
) -> Result<Self, LocationManagerError> {
|
||||||
let (events_tx, events_rx) = mpsc::unbounded_channel();
|
let (events_tx, events_rx) = mpsc::unbounded_channel();
|
||||||
|
let (ignore_path_tx, ignore_path_rx) = mpsc::unbounded_channel();
|
||||||
let (stop_tx, stop_rx) = oneshot::channel();
|
let (stop_tx, stop_rx) = oneshot::channel();
|
||||||
|
|
||||||
let watcher = RecommendedWatcher::new(
|
let watcher = RecommendedWatcher::new(
|
||||||
|
@ -89,12 +96,6 @@ impl LocationWatcher {
|
||||||
Config::default(),
|
Config::default(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let handle = tokio::spawn(Self::handle_watch_events(
|
|
||||||
location.id,
|
|
||||||
library_ctx,
|
|
||||||
events_rx,
|
|
||||||
stop_rx,
|
|
||||||
));
|
|
||||||
let path = PathBuf::from(
|
let path = PathBuf::from(
|
||||||
location
|
location
|
||||||
.local_path
|
.local_path
|
||||||
|
@ -102,10 +103,19 @@ impl LocationWatcher {
|
||||||
.ok_or(LocationManagerError::LocationMissingLocalPath(location.id))?,
|
.ok_or(LocationManagerError::LocationMissingLocalPath(location.id))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let handle = tokio::spawn(Self::handle_watch_events(
|
||||||
|
location.id,
|
||||||
|
library_ctx,
|
||||||
|
events_rx,
|
||||||
|
ignore_path_rx,
|
||||||
|
stop_rx,
|
||||||
|
));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
location,
|
location,
|
||||||
path,
|
path,
|
||||||
watcher,
|
watcher,
|
||||||
|
ignore_path_tx,
|
||||||
handle: Some(handle),
|
handle: Some(handle),
|
||||||
stop_tx: Some(stop_tx),
|
stop_tx: Some(stop_tx),
|
||||||
})
|
})
|
||||||
|
@ -115,10 +125,13 @@ impl LocationWatcher {
|
||||||
location_id: LocationId,
|
location_id: LocationId,
|
||||||
library_ctx: LibraryContext,
|
library_ctx: LibraryContext,
|
||||||
mut events_rx: mpsc::UnboundedReceiver<notify::Result<Event>>,
|
mut events_rx: mpsc::UnboundedReceiver<notify::Result<Event>>,
|
||||||
|
mut ignore_path_rx: mpsc::UnboundedReceiver<IgnorePath>,
|
||||||
mut stop_rx: oneshot::Receiver<()>,
|
mut stop_rx: oneshot::Receiver<()>,
|
||||||
) {
|
) {
|
||||||
let mut event_handler = Handler::new();
|
let mut event_handler = Handler::new();
|
||||||
|
|
||||||
|
let mut paths_to_ignore = HashSet::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
Some(event) = events_rx.recv() => {
|
Some(event) = events_rx.recv() => {
|
||||||
|
@ -128,7 +141,8 @@ impl LocationWatcher {
|
||||||
location_id,
|
location_id,
|
||||||
event,
|
event,
|
||||||
&mut event_handler,
|
&mut event_handler,
|
||||||
&library_ctx
|
&library_ctx,
|
||||||
|
&paths_to_ignore,
|
||||||
).await {
|
).await {
|
||||||
error!("Failed to handle location file system event: \
|
error!("Failed to handle location file system event: \
|
||||||
<id='{location_id}', error='{e:#?}'>",
|
<id='{location_id}', error='{e:#?}'>",
|
||||||
|
@ -140,6 +154,15 @@ impl LocationWatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some((path, ignore)) = ignore_path_rx.recv() => {
|
||||||
|
if ignore {
|
||||||
|
paths_to_ignore.insert(path);
|
||||||
|
} else {
|
||||||
|
paths_to_ignore.remove(&path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ = &mut stop_rx => {
|
_ = &mut stop_rx => {
|
||||||
debug!("Stop Location Manager event handler for location: <id='{}'>", location_id);
|
debug!("Stop Location Manager event handler for location: <id='{}'>", location_id);
|
||||||
break
|
break
|
||||||
|
@ -153,8 +176,9 @@ impl LocationWatcher {
|
||||||
event: Event,
|
event: Event,
|
||||||
event_handler: &mut impl EventHandler,
|
event_handler: &mut impl EventHandler,
|
||||||
library_ctx: &LibraryContext,
|
library_ctx: &LibraryContext,
|
||||||
|
ignore_paths: &HashSet<PathBuf>,
|
||||||
) -> Result<(), LocationManagerError> {
|
) -> Result<(), LocationManagerError> {
|
||||||
if check_event(&event) {
|
if check_event(&event, ignore_paths) {
|
||||||
if let Some(location) = fetch_location(library_ctx, location_id)
|
if let Some(location) = fetch_location(library_ctx, location_id)
|
||||||
.include(indexer_job_location::include())
|
.include(indexer_job_location::include())
|
||||||
.exec()
|
.exec()
|
||||||
|
@ -175,6 +199,14 @@ impl LocationWatcher {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn ignore_path(
|
||||||
|
&self,
|
||||||
|
path: PathBuf,
|
||||||
|
ignore: bool,
|
||||||
|
) -> Result<(), LocationManagerError> {
|
||||||
|
self.ignore_path_tx.send((path, ignore)).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn check_path(&self, path: impl AsRef<Path>) -> bool {
|
pub(super) fn check_path(&self, path: impl AsRef<Path>) -> bool {
|
||||||
self.path == path.as_ref()
|
self.path == path.as_ref()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
@ -46,12 +47,13 @@ pub(super) fn check_location_online(location: &indexer_job_location::Data) -> bo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn check_event(event: &Event) -> bool {
|
pub(super) fn check_event(event: &Event, ignore_paths: &HashSet<PathBuf>) -> bool {
|
||||||
// if first path includes .DS_Store, ignore
|
// if first path includes .DS_Store, ignore
|
||||||
if event.paths.iter().any(|p| {
|
if event.paths.iter().any(|p| {
|
||||||
p.to_str()
|
p.to_str()
|
||||||
.expect("Found non-UTF-8 path")
|
.expect("Found non-UTF-8 path")
|
||||||
.contains(".DS_Store")
|
.contains(".DS_Store")
|
||||||
|
|| ignore_paths.contains(p)
|
||||||
}) {
|
}) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
use std::{
|
use std::{collections::VecDeque, fs::File, io::Read, path::PathBuf};
|
||||||
collections::VecDeque,
|
|
||||||
fs::{self, File},
|
|
||||||
io::Read,
|
|
||||||
path::PathBuf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
|
@ -64,8 +59,8 @@ const JOB_NAME: &str = "file_encryptor";
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl StatefulJob for FileEncryptorJob {
|
impl StatefulJob for FileEncryptorJob {
|
||||||
type Data = FileEncryptorJobState;
|
|
||||||
type Init = FileEncryptorJobInit;
|
type Init = FileEncryptorJobInit;
|
||||||
|
type Data = FileEncryptorJobState;
|
||||||
type Step = FileEncryptorJobStep;
|
type Step = FileEncryptorJobStep;
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
|
@ -136,8 +131,18 @@ impl StatefulJob for FileEncryptorJob {
|
||||||
Ok,
|
Ok,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut reader = File::open(info.obj_path.clone())?;
|
let _guard = ctx
|
||||||
let mut writer = File::create(output_path)?;
|
.library_ctx
|
||||||
|
.location_manager()
|
||||||
|
.temporary_ignore_events_for_path(
|
||||||
|
state.init.location_id,
|
||||||
|
ctx.library_ctx.clone(),
|
||||||
|
&output_path,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut reader = task::block_in_place(|| File::open(&info.obj_path))?;
|
||||||
|
let mut writer = task::block_in_place(|| File::create(output_path))?;
|
||||||
|
|
||||||
let master_key = generate_master_key();
|
let master_key = generate_master_key();
|
||||||
|
|
||||||
|
@ -202,7 +207,7 @@ impl StatefulJob for FileEncryptorJob {
|
||||||
.join("thumbnails")
|
.join("thumbnails")
|
||||||
.join(object.cas_id + ".webp");
|
.join(object.cas_id + ".webp");
|
||||||
|
|
||||||
if fs::metadata(pvm_path.clone()).is_ok() {
|
if tokio::fs::metadata(&pvm_path).await.is_ok() {
|
||||||
let mut pvm_bytes = Vec::new();
|
let mut pvm_bytes = Vec::new();
|
||||||
task::block_in_place(|| {
|
task::block_in_place(|| {
|
||||||
let mut pvm_file = File::open(pvm_path)?;
|
let mut pvm_file = File::open(pvm_path)?;
|
||||||
|
|
|
@ -7,12 +7,11 @@ module.exports = {
|
||||||
find: /^(~\/.+)/,
|
find: /^(~\/.+)/,
|
||||||
replacement: '$1',
|
replacement: '$1',
|
||||||
async customResolver(source, importer) {
|
async customResolver(source, importer) {
|
||||||
const [repo, filePath] = importer.split('/packages/');
|
const [pkg] = importer.split('/src/');
|
||||||
const [pkg] = filePath.split('/src/');
|
|
||||||
|
|
||||||
const sourcePath = source.substring(2);
|
const [_, sourcePath] = source.split('~/');
|
||||||
|
|
||||||
const absolutePath = `${repo}/packages/${pkg}/src/${sourcePath}`;
|
const absolutePath = `${pkg}/src/${sourcePath}`;
|
||||||
|
|
||||||
const folderItems = await fs.readdir(path.join(absolutePath, '../'));
|
const folderItems = await fs.readdir(path.join(absolutePath, '../'));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue