mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-16 12:26:42 -04:00
* Working External Storage Locked Locations We can now add Downloads (A location locked by MANGE_EXTERNAL_STORAGE) on Android. * Navigate to added page Adding location now navigates to the added location explorer. * Way simpler solution Found a way simpler solution, that doesn't require query calls. * Clean up Remove unused import calls. * Attempt to get Location Watcher working Well, Location watcher doesn't want to run. But I wanted to push the `xcrun` command to the README. Also, locations now actually show files, but break on displaying recursive folders (folder content in a folder). * Quick clean up + Working full rescan locations.fullRescan works now on mobile with 0 issues. It can recognize orphan paths and when new files are added. But, Location-Watcher for some reason doesn't want to run automatically when the file system change occurs. * Working iOS Watcher Location Watcher now works for iOS when files are created. However, it can't understand the file delete event yet. * Functional watcher for iOS The watcher system is now fully functional on iOS, with a few bugs to patch regarding the delete function freezing the system up. * Update ios.rs * See Logs on Android Finally figured out how to see the logs from Android. * Update CONTRIBUTING.md * Updated to Stable & Working * Working Android Build + Explorer working again * wip * wip * Squashed commit of the following: commita7bc3b908dMerge:201913a1898a81daAuthor: Arnab Chakraborty <11457760+Rocky43007@users.noreply.github.com> Date: Tue Feb 6 16:18:42 2024 -0500 Merge branch 'main' of https://github.com/Rocky43007/spacedrive commit898a81dae4Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue Feb 6 19:30:53 2024 +0300 [ENG-1594] Change online to connected (#2060) : This is a combination of 3 commits. Change online to connected remove offline json commit581cbf6a27Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue Feb 6 17:49:16 2024 +0300 [MOB-54] UI Fixes (#2059) * UI fixes - rive animation - SD version in settings - and more * twStyle commitc178b91fb0Author: Jesse Rodrigo <39565615+JSSRDRG@users.noreply.github.com> Date: Tue Feb 6 11:41:05 2024 +0100 Dutch locale (#2054) * nl locale * add nl entry * improve some wording commit18ab7cc1a4Author: Brendan Allan <brendonovich@outlook.com> Date: Tue Feb 6 17:13:47 2024 +0800 [ENG-1548] use in-memory instances when sending messages to cloud (#2057) * use in-memory instances when sending messages to cloud * comments commit8dd7e3586eAuthor: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue Feb 6 06:03:33 2024 +0300 [MOB-47] Location screen and header updates (#2056) * Location screen and header updates * use tw sizing * remove un-necessary prop * Nit: change name commita7bffc4a4eAuthor: Arnab Chakraborty <11457760+Rocky43007@users.noreply.github.com> Date: Mon Feb 5 07:06:47 2024 -0500 Update to Expo 50 and Fix to Rive Crashing (#2049) * Update Mobile App to Expo SDK 50 + Fix to Rive Crashing * Added `metro-react-native-babel-transformer` to fix CI commit72c51d5649Author: Utku <74243531+utkubakir@users.noreply.github.com> Date: Sun Feb 4 23:52:26 2024 +0300 Fix Chinese language (#2050) * fix chinese * remove console.log * Squashed commit of the following: commitec55d9f182Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Wed Feb 7 20:25:04 2024 +0300 [MOB-55] Video animation for onboarding on mobile and desktop (#2065) * video animation for onboarding on mobile and desktop run assets gen cleanup declare mp4 type * update metro config to transform video files from sd assets * test ci without native video exclude * casing? * remove to add back again due to github * add videos back * versions * no need to transform --------- Co-authored-by: Utku Bakir <74243531+utkubakir@users.noreply.github.com> commitcd97c73cf4Author: Utku <74243531+utkubakir@users.noreply.github.com> Date: Wed Feb 7 16:47:55 2024 +0300 More translations (#2051) * translations * more translation keys * all the translations * Added Cleaning Script * Prep for PR * Clean up + `cargo fmt` * Update mod.rs * Squashed commit of the following: commitd51773caafAuthor: Utku <74243531+utkubakir@users.noreply.github.com> Date: Fri Feb 9 18:42:42 2024 +0300 Update readme & contributing guide & language stuff (#2071) * updates * keep common errors * fix selector being empty for english * sort by label * update contributing * update ndk and docs * Update CONTRIBUTING.md commit76de9d5fa4Author: Brendan Allan <brendonovich@outlook.com> Date: Fri Feb 9 21:20:51 2024 +0800 sync support for labels (#2070) * more sync support for file paths + saved searches * sync support for labels * update sync prisma generator to support more than tags * workey * don't do illegal db migration * use name as label id in explorer commitd3236ae735Author: Brendan Allan <brendonovich@outlook.com> Date: Fri Feb 9 16:17:04 2024 +0800 More sync support for file paths + saved searches (#2067) more sync support for file paths + saved searches * Update build-rust.sh Fix script so it doesn't build debug apps always now. * Add Tests for iOS * Update android.rs PR 1812 was closed, therefore changing the message to mention the branch instead. * Change `--debug` to `--release` Oops * Remove debug cargo crate for `notify-rs` * Spelling Fix on `android.rs` * Squashed commit of the following: commit8c3462a27aAuthor: Ericson "Fogo" Soares <ericson.ds999@gmail.com> Date: Mon Feb 26 16:45:58 2024 -0300 [ENG-1513] Better integration between Jobs and processing Actors (#1974) * First draft on new task system * Removing save to disk from task system * Bunch of concurrency issues * Solving Future impl issue when pausing tasks * Fix cancel and abort * Bunch of fixes on pause, suspend, resume, cancel and abort Also better error handling on task completion for the user * New capabilities to return an output on a task * Introducing a simple way to linear backoff on failed steal * Sample actor where tasks can dispatch more tasks * Rustfmt * Steal test to make sure * Stale deps cleanup * Removing unused utils * Initial lib docs * Docs ok * Memory cleanup on idle --------- Co-authored-by: Vítor Vasconcellos <vasconcellos.dev@gmail.com> commit9da5fed74cAuthor: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Mon Feb 26 18:53:37 2024 +0300 [ENG-1625] Spacedrop UI correct hover condition & spacing (#2127) * improve spacedrop ui with correct hover & spacing * remove commited037df4c1Author: Matteo Galiazzo <50683509+gekoxyz@users.noreply.github.com> Date: Mon Feb 26 16:20:41 2024 +0100 added it locale, added it entry (#2066) * added it locale, added it entry * Apply suggestions from code review Co-authored-by: Matteo Martellini <matteo@mercxry.me> * add missing keys and a readme about the script --------- Co-authored-by: Utku <74243531+utkubakir@users.noreply.github.com> Co-authored-by: Matteo Martellini <matteo@mercxry.me> commit7cae4cb78bAuthor: jake <77554505+brxken128@users.noreply.github.com> Date: Mon Feb 26 15:17:28 2024 +0000 Clean-up MacOS window closing behaviour code (#2124) * fix: delete dead/unused file * refactor: add the window event handler with the rest of them * refactor: formatting commit4ef515d974Author: Oscar Beaumont <oscar@otbeaumont.me> Date: Mon Feb 26 15:23:48 2024 +0800 rspc over P2P (#2112) * wip: rspc over p2p * wip * rspc over P2P * Cleanup + error handling * slight cleanup * Using Hyper for HTTP streaming + websockets commit3eb8cfe4afAuthor: Utku <74243531+utkubakir@users.noreply.github.com> Date: Fri Feb 23 11:26:58 2024 -0500 Fix android thumbs (#2121) * replace react-native-fs with an active fork * time sink * fix commitca10749a9cAuthor: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Fri Feb 23 00:28:35 2024 +0300 Mob: cleanup warning (#2122) Update Categories.tsx commit0e3b4d84e5Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Thu Feb 22 16:08:41 2024 +0300 MOB: Settings paddings (#2120) padding tweaks commite92d96ff05Author: nikec <43032218+niikeec@users.noreply.github.com> Date: Thu Feb 22 13:53:02 2024 +0100 [ENG-1619] Add spacedrop to the context menu (#2119) add spacedrop to context menu commitedfbdeb9d7Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Thu Feb 22 15:47:08 2024 +0300 [ENG-1618] Spacedrop UI (#2118) * spacedrop ui update * i18n and description * glitch: remove ! * already in progress i18n * more i18n commitb4edf5b210Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Thu Feb 22 15:20:31 2024 +0300 [MOB-62] Spacing & padding tweaks (#2117) Spacing & padding tweaks commitf4e68497edAuthor: Vítor Vasconcellos <vasconcellos.dev@gmail.com> Date: Thu Feb 22 03:15:36 2024 -0300 Fix core test and CI breaking (#2116) Fix core test passing inverted arguments to sync_db_entry macro commitf2ce41b9b4Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Wed Feb 21 17:19:40 2024 +0300 Mob: better visually width fitting for categories (#2114) * Visually width fitting for categories * remove padding commita0b7cf21fcAuthor: Vítor Vasconcellos <vasconcellos.dev@gmail.com> Date: Wed Feb 21 11:18:15 2024 -0300 Fix mobile CI + Some small CI improvements (#2113) Fix mobile CI - Use rust envvars in all workflows - Use rust envvars and mold when build sd-server docker commitbadae3c75aAuthor: Utku <74243531+utkubakir@users.noreply.github.com> Date: Wed Feb 21 08:26:05 2024 -0500 [MOB-37, MOB-38, MOB-39] Preview for PDF, Text and Media files (#2098) * version & microphonePermission text & eslint * remove polyfill as hermes supports intl now * why do we have solid on mobile? * cleanup * add solid back =_= * pnpm lock * we hate relative paths here * android config * open file logic * visual tweaks --------- Co-authored-by: ameer2468 <33054370+ameer2468@users.noreply.github.com> commitbfd6e813c0Author: Brendan Allan <brendonovich@outlook.com> Date: Wed Feb 21 22:42:10 2024 +1100 media data sync (#2102) * basic sync operation backfill * media data sync * sync entry helpers * fix sync generator * nicer * re-add key_id commitc1e98dcb0dAuthor: Vítor Vasconcellos <vasconcellos.dev@gmail.com> Date: Wed Feb 21 06:27:40 2024 -0300 Update github actions due to nodejs 16 deprecation (#2107) * Update github actions due to nodejs 16 deprecation - Replace archived actions-rs/clippy-check with maintained fork actions-rs-plus/clippy-check - Replace redhat-actions/push-to-registry with updated fork Eusebiotrigo/push-to-registry - Point redhat-actions/buildah-build and softprops/action-gh-release to current master to fix nodejs deprecation * Build the correct ios core rust arch for CI runs * Build ios app for the same arch as the host in Mobile CI * Some changes to try and make cache-factory faster and avoid failing so much * Add trigger to run cache-factory on pull requests when there are changes to itself * Attempt to fix sed usage on macOS * Don't treat warning as errors * Fix windows * Fix windows 2 * Use target ad cache key for rust to differentiate between macOS x86_64 and arm64 * Use faster/better linkers to compile for macOS, Linux and Windows * Fix missing shell in action * Fix typo * Fix missing shell in action 2 * Fix mold download - Replace bsdtar with plain tar * Fix permission denied when extracting mold * Remove zld * Don't restore cache for rustfmt - Remove target symlink to C:/ in an attempt to speed-up windows CI * Fix typo * Restore target symlink on windows - Removing it didn't make CI faster * Run Mobile on macos-14 commita7d836a922Author: Oscar Beaumont <oscar@otbeaumont.me> Date: Wed Feb 21 16:13:40 2024 +0800 Fix P2P not working for libraries (#2031) * P2P Debug route * Remove legacy peer to peer pairing process * Fix error typo * Sync instances with cloud * Upgrade deps + extended instance data * Create instance with extended metadata * Auto sync instances * Actually `.await` * bruh * sync library info * this isn't gonna work * only sleep cloud receiver when no more messages (#1985) * [ENG-1567] Fix renaming (#1986) fix rename * only sleep cloud receiver when no more messages * use in memory instances during cloud receive (#1995) * use in memory instances during cloud receive * is_empty --------- Co-authored-by: nikec <43032218+niikeec@users.noreply.github.com> * fix type error * wip * make mdns mdns better * rebuild state * Add hooks + listeners + discovered state * Split into crates * wip fixing core + wip merging Spacetime into `sd-p2p2` * `SmartLockGuard` + `Listener` * Make `sd-core` compile * Reenable all operation receivers * Fix all broken code within `sd-core` * minor fixes found in review * Bring in `libp2p` + restructure `sd-p2p` for the gazillion-th time * whoops * Compile no matter the (runtime) cost * fixing merge issues * wip * a * b * C * Handle port betterer * c * Migrate node config * a * no crash on startup * wip * a * jdfhskjfsg * a * fix discovery * a bunch of fixes * getting Spacedrop working * I don't get why it no worky * debug example * a * wip * wip * removing logging from stream impl * wip: shit is fucked * Redo quic integration + Spacedrop working * Fix shutdown - deadlocks + shutdown peers * Add Prisma migrations * Fix shutdown * a * fix * cleanup * The lord clippy hath spoken * disable P2P settings for now --------- Co-authored-by: Brendan Allan <brendonovich@outlook.com> Co-authored-by: nikec <43032218+niikeec@users.noreply.github.com> commit146838d19eAuthor: Julian Braha <julianbraha@gmail.com> Date: Tue Feb 20 20:06:05 2024 +0000 Improve error handling by using decode::Error instead of io::Result (#2078) Use decode::Error instead of io::Result commit145cd20805Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue Feb 20 22:59:11 2024 +0300 [MOB-59] Empty UI for locations and tags screen (#2110) Empty UI for locations and tags screen commit704d03a329Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue Feb 20 22:47:14 2024 +0300 [MOB-60] locations & tags query invalidation on updates (#2111) Trigger UI updates on location adding/delete and tags lint name commit45b9e35744Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue Feb 20 13:05:53 2024 +0300 mousedown fix (#2108) quick mistake fix commit25f9f03010Author: Brendan Allan <brendonovich@outlook.com> Date: Tue Feb 20 20:22:34 2024 +1100 Basic sync operation backfill (#2101) * basic sync operation backfill * no changes commitce0203ff74Author: Arnab Chakraborty <11457760+Rocky43007@users.noreply.github.com> Date: Tue Feb 20 01:33:52 2024 -0500 New Android Build Script (#2096) * New Android Build Script * Clean up + Works for CI now * Simplify android build.sh - Fix /var/home/vitor fallback for Linux systems - Run a single cargo ndk for all targets (not parallel build, but a bit faster) - Fix android target s/x86/x86_64/ - Format setup.sh - Minor improvements to rust mobile targets installation step in setup.sh * Add notice to CONTRIBUTING that only Java <= 17 is supported for building android - Make prettier ignore some mobile build artifacts * When in CI, Fix build android core for host architecture --------- Co-authored-by: Vítor Vasconcellos <vasconcellos.dev@gmail.com> commitd280895d28Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Mon Feb 19 23:45:29 2024 +0300 [ENG-1615] bg intro video fixed (#2104) * video intro bg * test hsl * test video bg * run tests * comment * mob intro * git glitch * git * webm type commit9742dc7ab4Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Mon Feb 19 19:12:11 2024 +0300 [MOB-58] Settings routes new design & more (#2103) * wip: redesigning settings pages * Edit location redesign & more * right actions * cleanup commit94008de7f3Author: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Mon Feb 19 18:23:20 2024 +0300 [ENG-1612] Fix mouse nav forwards and backwards (#2105) Fix mouse nav forwards and backwards * Clean up commented code * Clean up + Remove `LocationOnboarding.tsx`
617 lines
16 KiB
Rust
617 lines
16 KiB
Rust
use crate::{
|
|
invalidate_query,
|
|
job::StatefulJob,
|
|
location::{
|
|
delete_location, find_location,
|
|
indexer::{rules::IndexerRuleCreateArgs, IndexerJobInit},
|
|
light_scan_location, location_with_indexer_rules,
|
|
non_indexed::NonIndexedPathItem,
|
|
relink_location, scan_location, scan_location_sub_path, LocationCreateArgs, LocationError,
|
|
LocationUpdateArgs,
|
|
},
|
|
object::file_identifier::file_identifier_job::FileIdentifierJobInit,
|
|
p2p::PeerMetadata,
|
|
util::AbortOnDrop,
|
|
};
|
|
|
|
use sd_cache::{CacheNode, Model, Normalise, NormalisedResult, NormalisedResults, Reference};
|
|
use sd_prisma::prisma::{
|
|
file_path, indexer_rule, indexer_rules_in_location, location, object, SortOrder,
|
|
};
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use chrono::{DateTime, FixedOffset, Utc};
|
|
use directories::UserDirs;
|
|
use rspc::{self, alpha::AlphaRouter, ErrorCode};
|
|
use serde::{Deserialize, Serialize};
|
|
use specta::Type;
|
|
use tracing::{debug, error};
|
|
|
|
use super::{labels::label_with_objects, utils::library, Ctx, R};
|
|
|
|
// it includes the shard hex formatted as ([["f02", "cab34a76fbf3469f"]])
|
|
// Will be None if no thumbnail exists
|
|
pub type ThumbnailKey = Vec<String>;
|
|
|
|
#[derive(Serialize, Type, Debug)]
|
|
#[serde(tag = "type")]
|
|
pub enum ExplorerItem {
|
|
Path {
|
|
thumbnail: Option<ThumbnailKey>,
|
|
item: file_path_with_object::Data,
|
|
},
|
|
Object {
|
|
thumbnail: Option<ThumbnailKey>,
|
|
item: object_with_file_paths::Data,
|
|
},
|
|
Location {
|
|
item: location::Data,
|
|
},
|
|
NonIndexedPath {
|
|
thumbnail: Option<ThumbnailKey>,
|
|
item: NonIndexedPathItem,
|
|
},
|
|
SpacedropPeer {
|
|
item: PeerMetadata,
|
|
},
|
|
Label {
|
|
thumbnails: Vec<ThumbnailKey>,
|
|
item: label_with_objects::Data,
|
|
},
|
|
}
|
|
|
|
// TODO: Really this shouldn't be a `Model` but it's easy for now.
|
|
// In the future we should store the inner data of the variant on behalf of it's existing model so it works cross queries.
|
|
impl Model for ExplorerItem {
|
|
fn name() -> &'static str {
|
|
"ExplorerItem"
|
|
}
|
|
}
|
|
|
|
impl ExplorerItem {
|
|
pub fn id(&self) -> String {
|
|
let ty = match self {
|
|
ExplorerItem::Path { .. } => "FilePath",
|
|
ExplorerItem::Object { .. } => "Object",
|
|
ExplorerItem::Location { .. } => "Location",
|
|
ExplorerItem::NonIndexedPath { .. } => "NonIndexedPath",
|
|
ExplorerItem::SpacedropPeer { .. } => "SpacedropPeer",
|
|
ExplorerItem::Label { .. } => "Label",
|
|
};
|
|
match self {
|
|
ExplorerItem::Path { item, .. } => format!("{ty}:{}", item.id),
|
|
ExplorerItem::Object { item, .. } => format!("{ty}:{}", item.id),
|
|
ExplorerItem::Location { item, .. } => format!("{ty}:{}", item.id),
|
|
ExplorerItem::NonIndexedPath { item, .. } => format!("{ty}:{}", item.path),
|
|
ExplorerItem::SpacedropPeer { item, .. } => format!("{ty}:{}", item.name), // TODO: Use a proper primary key
|
|
ExplorerItem::Label { item, .. } => format!("{ty}:{}", item.name),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Type, Debug)]
|
|
pub struct SystemLocations {
|
|
desktop: Option<PathBuf>,
|
|
documents: Option<PathBuf>,
|
|
downloads: Option<PathBuf>,
|
|
pictures: Option<PathBuf>,
|
|
music: Option<PathBuf>,
|
|
videos: Option<PathBuf>,
|
|
}
|
|
|
|
impl From<UserDirs> for SystemLocations {
|
|
fn from(value: UserDirs) -> Self {
|
|
Self {
|
|
desktop: value.desktop_dir().map(Path::to_path_buf),
|
|
documents: value.document_dir().map(Path::to_path_buf),
|
|
downloads: value.download_dir().map(Path::to_path_buf),
|
|
pictures: value.picture_dir().map(Path::to_path_buf),
|
|
music: value.audio_dir().map(Path::to_path_buf),
|
|
videos: value.video_dir().map(Path::to_path_buf),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ExplorerItem {
|
|
pub fn name(&self) -> &str {
|
|
match self {
|
|
ExplorerItem::Path {
|
|
item: file_path_with_object::Data { name, .. },
|
|
..
|
|
}
|
|
| ExplorerItem::Location {
|
|
item: location::Data { name, .. },
|
|
..
|
|
} => name.as_deref().unwrap_or(""),
|
|
ExplorerItem::NonIndexedPath { item, .. } => item.name.as_str(),
|
|
_ => "",
|
|
}
|
|
}
|
|
|
|
pub fn size_in_bytes(&self) -> u64 {
|
|
match self {
|
|
ExplorerItem::Path {
|
|
item: file_path_with_object::Data {
|
|
size_in_bytes_bytes,
|
|
..
|
|
},
|
|
..
|
|
} => size_in_bytes_bytes
|
|
.as_ref()
|
|
.map(|size| {
|
|
u64::from_be_bytes([
|
|
size[0], size[1], size[2], size[3], size[4], size[5], size[6], size[7],
|
|
])
|
|
})
|
|
.unwrap_or(0),
|
|
|
|
ExplorerItem::NonIndexedPath {
|
|
item: NonIndexedPathItem {
|
|
size_in_bytes_bytes,
|
|
..
|
|
},
|
|
..
|
|
} => u64::from_be_bytes([
|
|
size_in_bytes_bytes[0],
|
|
size_in_bytes_bytes[1],
|
|
size_in_bytes_bytes[2],
|
|
size_in_bytes_bytes[3],
|
|
size_in_bytes_bytes[4],
|
|
size_in_bytes_bytes[5],
|
|
size_in_bytes_bytes[6],
|
|
size_in_bytes_bytes[7],
|
|
]),
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
pub fn date_created(&self) -> DateTime<Utc> {
|
|
match self {
|
|
ExplorerItem::Path {
|
|
item: file_path_with_object::Data { date_created, .. },
|
|
..
|
|
}
|
|
| ExplorerItem::Object {
|
|
item: object_with_file_paths::Data { date_created, .. },
|
|
..
|
|
}
|
|
| ExplorerItem::Location {
|
|
item: location::Data { date_created, .. },
|
|
..
|
|
} => date_created.map(Into::into).unwrap_or_default(),
|
|
|
|
ExplorerItem::NonIndexedPath { item, .. } => item.date_created,
|
|
_ => Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn date_modified(&self) -> DateTime<Utc> {
|
|
match self {
|
|
ExplorerItem::Path { item, .. } => {
|
|
item.date_modified.map(Into::into).unwrap_or_default()
|
|
}
|
|
ExplorerItem::NonIndexedPath { item, .. } => item.date_modified,
|
|
_ => Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
file_path::include!(file_path_with_object { object });
|
|
object::include!(object_with_file_paths { file_paths });
|
|
|
|
pub(crate) fn mount() -> AlphaRouter<Ctx> {
|
|
R.router()
|
|
.procedure("list", {
|
|
R.with2(library()).query(|(_, library), _: ()| async move {
|
|
let locations = library
|
|
.db
|
|
.location()
|
|
.find_many(vec![])
|
|
.order_by(location::date_created::order(SortOrder::Desc))
|
|
.exec()
|
|
.await?;
|
|
|
|
let (nodes, items) = locations.normalise(|i| i.id.to_string());
|
|
|
|
Ok(NormalisedResults { items, nodes })
|
|
})
|
|
})
|
|
.procedure("get", {
|
|
R.with2(library())
|
|
.query(|(_, library), location_id: location::id::Type| async move {
|
|
Ok(library
|
|
.db
|
|
.location()
|
|
.find_unique(location::id::equals(location_id))
|
|
.exec()
|
|
.await?
|
|
.map(|i| NormalisedResult::from(i, |i| i.id.to_string())))
|
|
})
|
|
})
|
|
.procedure("getWithRules", {
|
|
#[derive(Type, Serialize)]
|
|
struct LocationWithIndexerRule {
|
|
pub id: i32,
|
|
pub pub_id: Vec<u8>,
|
|
pub name: Option<String>,
|
|
pub path: Option<String>,
|
|
pub total_capacity: Option<i32>,
|
|
pub available_capacity: Option<i32>,
|
|
pub size_in_bytes: Option<Vec<u8>>,
|
|
pub is_archived: Option<bool>,
|
|
pub generate_preview_media: Option<bool>,
|
|
pub sync_preview_media: Option<bool>,
|
|
pub hidden: Option<bool>,
|
|
pub date_created: Option<DateTime<FixedOffset>>,
|
|
pub instance_id: Option<i32>,
|
|
pub indexer_rules: Vec<Reference<indexer_rule::Data>>,
|
|
}
|
|
|
|
impl Model for LocationWithIndexerRule {
|
|
fn name() -> &'static str {
|
|
"Location" // This is a duplicate identifier as `location::Data` but it's fine because because they are the same entity
|
|
}
|
|
}
|
|
|
|
impl LocationWithIndexerRule {
|
|
pub fn from_db(
|
|
nodes: &mut Vec<CacheNode>,
|
|
value: location_with_indexer_rules::Data,
|
|
) -> Reference<Self> {
|
|
let this = Self {
|
|
id: value.id,
|
|
pub_id: value.pub_id,
|
|
name: value.name,
|
|
path: value.path,
|
|
total_capacity: value.total_capacity,
|
|
available_capacity: value.available_capacity,
|
|
size_in_bytes: value.size_in_bytes,
|
|
is_archived: value.is_archived,
|
|
generate_preview_media: value.generate_preview_media,
|
|
sync_preview_media: value.sync_preview_media,
|
|
hidden: value.hidden,
|
|
date_created: value.date_created,
|
|
instance_id: value.instance_id,
|
|
indexer_rules: value
|
|
.indexer_rules
|
|
.into_iter()
|
|
.map(|i| {
|
|
let id = i.indexer_rule.id.to_string();
|
|
|
|
nodes.push(CacheNode::new(id.clone(), i.indexer_rule));
|
|
Reference::new(id)
|
|
})
|
|
.collect(),
|
|
};
|
|
|
|
let id = this.id.to_string();
|
|
nodes.push(CacheNode::new(id.clone(), this));
|
|
Reference::new(id)
|
|
}
|
|
}
|
|
|
|
R.with2(library())
|
|
.query(|(_, library), location_id: location::id::Type| async move {
|
|
Ok(library
|
|
.db
|
|
.location()
|
|
.find_unique(location::id::equals(location_id))
|
|
.include(location_with_indexer_rules::include())
|
|
.exec()
|
|
.await?
|
|
.map(|location| {
|
|
let mut nodes = Vec::new();
|
|
NormalisedResult {
|
|
item: LocationWithIndexerRule::from_db(&mut nodes, location),
|
|
nodes,
|
|
}
|
|
}))
|
|
})
|
|
})
|
|
.procedure("create", {
|
|
R.with2(library())
|
|
.mutation(|(node, library), args: LocationCreateArgs| async move {
|
|
if let Some(location) = args.create(&node, &library).await? {
|
|
let id = Some(location.id);
|
|
scan_location(&node, &library, location).await?;
|
|
invalidate_query!(library, "locations.list");
|
|
Ok(id)
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
})
|
|
})
|
|
.procedure("update", {
|
|
R.with2(library())
|
|
.mutation(|(node, library), args: LocationUpdateArgs| async move {
|
|
let ret = args.update(&node, &library).await.map_err(Into::into);
|
|
invalidate_query!(library, "locations.list");
|
|
ret
|
|
})
|
|
})
|
|
.procedure("delete", {
|
|
R.with2(library()).mutation(
|
|
|(node, library), location_id: location::id::Type| async move {
|
|
delete_location(&node, &library, location_id).await?;
|
|
invalidate_query!(library, "locations.list");
|
|
Ok(())
|
|
},
|
|
)
|
|
})
|
|
.procedure("relink", {
|
|
R.with2(library())
|
|
.mutation(|(_, library), location_path: PathBuf| async move {
|
|
relink_location(&library, location_path)
|
|
.await
|
|
.map_err(Into::into)
|
|
})
|
|
})
|
|
.procedure("addLibrary", {
|
|
R.with2(library())
|
|
.mutation(|(node, library), args: LocationCreateArgs| async move {
|
|
if let Some(location) = args.add_library(&node, &library).await? {
|
|
let id = location.id;
|
|
scan_location(&node, &library, location).await?;
|
|
invalidate_query!(library, "locations.list");
|
|
Ok(Some(id))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
})
|
|
})
|
|
.procedure("fullRescan", {
|
|
#[derive(Type, Deserialize)]
|
|
pub struct FullRescanArgs {
|
|
pub location_id: location::id::Type,
|
|
pub reidentify_objects: bool,
|
|
}
|
|
R.with2(library()).mutation(
|
|
|(node, library),
|
|
FullRescanArgs {
|
|
location_id,
|
|
reidentify_objects,
|
|
}| async move {
|
|
if reidentify_objects {
|
|
let count = library
|
|
.db
|
|
.file_path()
|
|
.update_many(
|
|
vec![
|
|
file_path::location_id::equals(Some(location_id)),
|
|
file_path::object_id::not(None),
|
|
file_path::cas_id::not(None),
|
|
],
|
|
vec![
|
|
file_path::object::disconnect(),
|
|
file_path::cas_id::set(None),
|
|
],
|
|
)
|
|
.exec()
|
|
.await?;
|
|
|
|
debug!("Disconnected {count} file paths from objects");
|
|
|
|
// library.orphan_remover.invoke().await;
|
|
}
|
|
|
|
// rescan location
|
|
scan_location(
|
|
&node,
|
|
&library,
|
|
find_location(&library, location_id)
|
|
.include(location_with_indexer_rules::include())
|
|
.exec()
|
|
.await?
|
|
.ok_or(LocationError::IdNotFound(location_id))?,
|
|
)
|
|
.await
|
|
.map_err(Into::into)
|
|
},
|
|
)
|
|
})
|
|
.procedure("subPathRescan", {
|
|
#[derive(Clone, Serialize, Deserialize, Type, Debug)]
|
|
pub struct RescanArgs {
|
|
pub location_id: location::id::Type,
|
|
pub sub_path: String,
|
|
}
|
|
|
|
R.with2(library()).mutation(
|
|
|(node, library),
|
|
RescanArgs {
|
|
location_id,
|
|
sub_path,
|
|
}: RescanArgs| async move {
|
|
scan_location_sub_path(
|
|
&node,
|
|
&library,
|
|
find_location(&library, location_id)
|
|
.include(location_with_indexer_rules::include())
|
|
.exec()
|
|
.await?
|
|
.ok_or(LocationError::IdNotFound(location_id))?,
|
|
sub_path,
|
|
)
|
|
.await
|
|
.map_err(Into::into)
|
|
},
|
|
)
|
|
})
|
|
.procedure("quickRescan", {
|
|
#[derive(Clone, Serialize, Deserialize, Type, Debug)]
|
|
pub struct LightScanArgs {
|
|
pub location_id: location::id::Type,
|
|
pub sub_path: String,
|
|
}
|
|
|
|
R.with2(library()).subscription(
|
|
|(node, library),
|
|
LightScanArgs {
|
|
location_id,
|
|
sub_path,
|
|
}: LightScanArgs| async move {
|
|
if node
|
|
.jobs
|
|
.has_job_running(|job_identity| {
|
|
job_identity.target_location == location_id
|
|
&& (job_identity.name == <IndexerJobInit as StatefulJob>::NAME
|
|
|| job_identity.name
|
|
== <FileIdentifierJobInit as StatefulJob>::NAME)
|
|
})
|
|
.await
|
|
{
|
|
return Err(rspc::Error::new(
|
|
ErrorCode::Conflict,
|
|
"We're still indexing this location, pleases wait a bit...".to_string(),
|
|
));
|
|
}
|
|
|
|
let location = find_location(&library, location_id)
|
|
.include(location_with_indexer_rules::include())
|
|
.exec()
|
|
.await?
|
|
.ok_or(LocationError::IdNotFound(location_id))?;
|
|
|
|
let handle = tokio::spawn(async move {
|
|
if let Err(e) = light_scan_location(node, library, location, sub_path).await
|
|
{
|
|
error!("light scan error: {e:#?}");
|
|
}
|
|
});
|
|
|
|
Ok(AbortOnDrop(handle))
|
|
},
|
|
)
|
|
})
|
|
.procedure(
|
|
"online",
|
|
R.subscription(|node, _: ()| async move {
|
|
let mut rx = node.locations.online_rx();
|
|
|
|
async_stream::stream! {
|
|
let online = node.locations.get_online().await;
|
|
|
|
yield online;
|
|
|
|
while let Ok(locations) = rx.recv().await {
|
|
yield locations;
|
|
}
|
|
}
|
|
}),
|
|
)
|
|
.procedure("systemLocations", {
|
|
R.query(|_, _: ()| async move {
|
|
UserDirs::new().map(SystemLocations::from).ok_or_else(|| {
|
|
rspc::Error::new(
|
|
ErrorCode::NotFound,
|
|
"Didn't find any system locations".to_string(),
|
|
)
|
|
})
|
|
})
|
|
})
|
|
.merge("indexer_rules.", mount_indexer_rule_routes())
|
|
}
|
|
|
|
fn mount_indexer_rule_routes() -> AlphaRouter<Ctx> {
|
|
R.router()
|
|
.procedure("create", {
|
|
R.with2(library())
|
|
.mutation(|(_, library), args: IndexerRuleCreateArgs| async move {
|
|
if args.create(&library).await?.is_some() {
|
|
invalidate_query!(library, "locations.indexer_rules.list");
|
|
}
|
|
|
|
Ok(())
|
|
})
|
|
})
|
|
.procedure("delete", {
|
|
R.with2(library())
|
|
.mutation(|(_, library), indexer_rule_id: i32| async move {
|
|
let indexer_rule_db = library.db.indexer_rule();
|
|
|
|
if let Some(indexer_rule) = indexer_rule_db
|
|
.to_owned()
|
|
.find_unique(indexer_rule::id::equals(indexer_rule_id))
|
|
.exec()
|
|
.await?
|
|
{
|
|
if indexer_rule.default.unwrap_or_default() {
|
|
return Err(rspc::Error::new(
|
|
ErrorCode::Forbidden,
|
|
format!("Indexer rule <id={indexer_rule_id}> can't be deleted"),
|
|
));
|
|
}
|
|
} else {
|
|
return Err(rspc::Error::new(
|
|
ErrorCode::NotFound,
|
|
format!("Indexer rule <id={indexer_rule_id}> not found"),
|
|
));
|
|
}
|
|
|
|
library
|
|
.db
|
|
.indexer_rules_in_location()
|
|
.delete_many(vec![indexer_rules_in_location::indexer_rule_id::equals(
|
|
indexer_rule_id,
|
|
)])
|
|
.exec()
|
|
.await?;
|
|
|
|
indexer_rule_db
|
|
.delete(indexer_rule::id::equals(indexer_rule_id))
|
|
.exec()
|
|
.await?;
|
|
|
|
invalidate_query!(library, "locations.indexer_rules.list");
|
|
|
|
Ok(())
|
|
})
|
|
})
|
|
.procedure("get", {
|
|
R.with2(library())
|
|
.query(|(_, library), indexer_rule_id: i32| async move {
|
|
library
|
|
.db
|
|
.indexer_rule()
|
|
.find_unique(indexer_rule::id::equals(indexer_rule_id))
|
|
.exec()
|
|
.await?
|
|
.ok_or_else(|| {
|
|
rspc::Error::new(
|
|
ErrorCode::NotFound,
|
|
format!("Indexer rule <id={indexer_rule_id}> not found"),
|
|
)
|
|
})
|
|
.map(|i| NormalisedResult::from(i, |i| i.id.to_string()))
|
|
})
|
|
})
|
|
.procedure("list", {
|
|
R.with2(library()).query(|(_, library), _: ()| async move {
|
|
let rules = library.db.indexer_rule().find_many(vec![]).exec().await?;
|
|
|
|
let (nodes, items) = rules.normalise(|i| i.id.to_string());
|
|
|
|
Ok(NormalisedResults { items, nodes })
|
|
})
|
|
})
|
|
// list indexer rules for location, returning the indexer rule
|
|
.procedure("listForLocation", {
|
|
R.with2(library())
|
|
.query(|(_, library), location_id: location::id::Type| async move {
|
|
let rules = library
|
|
.db
|
|
.indexer_rule()
|
|
.find_many(vec![indexer_rule::locations::some(vec![
|
|
indexer_rules_in_location::location_id::equals(location_id),
|
|
])])
|
|
.exec()
|
|
.await?;
|
|
|
|
let (nodes, items) = rules.normalise(|i| i.id.to_string());
|
|
|
|
Ok(NormalisedResults { items, nodes })
|
|
})
|
|
})
|
|
}
|