Files
spacedrive/core/src/api/locations.rs
Arnab Chakraborty baf5a3ba67 Working Location-Watcher system for iOS app (#2073)
* 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:

commit a7bc3b908d
Merge: 201913a1 898a81da
Author: 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

commit 898a81dae4
Author: 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

commit 581cbf6a27
Author: 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

commit c178b91fb0
Author: 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

commit 18ab7cc1a4
Author: 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

commit 8dd7e3586e
Author: 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

commit a7bffc4a4e
Author: 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

commit 72c51d5649
Author: 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:

commit ec55d9f182
Author: 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>

commit cd97c73cf4
Author: 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:

commit d51773caaf
Author: 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

commit 76de9d5fa4
Author: 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

commit d3236ae735
Author: 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:

commit 8c3462a27a
Author: 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>

commit 9da5fed74c
Author: 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

commit ed037df4c1
Author: 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>

commit 7cae4cb78b
Author: 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

commit 4ef515d974
Author: 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

commit 3eb8cfe4af
Author: 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

commit ca10749a9c
Author: ameer2468 <33054370+ameer2468@users.noreply.github.com>
Date:   Fri Feb 23 00:28:35 2024 +0300

    Mob: cleanup warning (#2122)

    Update Categories.tsx

commit 0e3b4d84e5
Author: ameer2468 <33054370+ameer2468@users.noreply.github.com>
Date:   Thu Feb 22 16:08:41 2024 +0300

    MOB: Settings paddings (#2120)

    padding tweaks

commit e92d96ff05
Author: 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

commit edfbdeb9d7
Author: 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

commit b4edf5b210
Author: 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

commit f4e68497ed
Author: 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

commit f2ce41b9b4
Author: 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

commit a0b7cf21fc
Author: 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

commit badae3c75a
Author: 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>

commit bfd6e813c0
Author: 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

commit c1e98dcb0d
Author: 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

commit a7d836a922
Author: 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>

commit 146838d19e
Author: 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

commit 145cd20805
Author: 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

commit 704d03a329
Author: 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

commit 45b9e35744
Author: ameer2468 <33054370+ameer2468@users.noreply.github.com>
Date:   Tue Feb 20 13:05:53 2024 +0300

    mousedown fix (#2108)

    quick mistake fix

commit 25f9f03010
Author: 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

commit ce0203ff74
Author: 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>

commit d280895d28
Author: 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

commit 9742dc7ab4
Author: 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

commit 94008de7f3
Author: 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`
2024-02-29 16:14:50 +00:00

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 })
})
})
}