mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-10-16 05:54:39 -04:00
121 lines
3.4 KiB
Rust
121 lines
3.4 KiB
Rust
use std::{
|
|
sync::Arc,
|
|
time::{Duration, Instant},
|
|
};
|
|
|
|
use rspc::{Config, Type};
|
|
use serde::{Deserialize, Serialize};
|
|
use tokio::sync::broadcast;
|
|
|
|
use crate::{
|
|
job::JobManager,
|
|
library::LibraryManager,
|
|
node::{NodeConfig, NodeConfigManager},
|
|
};
|
|
|
|
use utils::{InvalidRequests, InvalidateOperationEvent};
|
|
|
|
pub type Router = rspc::Router<Ctx>;
|
|
pub(crate) type RouterBuilder = rspc::RouterBuilder<Ctx>;
|
|
|
|
/// Represents an internal core event, these are exposed to client via a rspc subscription.
|
|
#[derive(Debug, Clone, Serialize, Type)]
|
|
pub enum CoreEvent {
|
|
NewThumbnail { cas_id: String },
|
|
InvalidateOperation(InvalidateOperationEvent),
|
|
InvalidateOperationDebounced(InvalidateOperationEvent),
|
|
}
|
|
|
|
/// Is provided when executing the router from the request.
|
|
pub struct Ctx {
|
|
pub library_manager: Arc<LibraryManager>,
|
|
pub config: Arc<NodeConfigManager>,
|
|
pub jobs: Arc<JobManager>,
|
|
pub event_bus: broadcast::Sender<CoreEvent>,
|
|
}
|
|
|
|
mod files;
|
|
mod jobs;
|
|
mod libraries;
|
|
mod locations;
|
|
mod tags;
|
|
pub mod utils;
|
|
pub mod volumes;
|
|
|
|
pub use files::*;
|
|
pub use jobs::*;
|
|
pub use libraries::*;
|
|
pub use tags::*;
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Type)]
|
|
struct NodeState {
|
|
#[serde(flatten)]
|
|
config: NodeConfig,
|
|
data_path: String,
|
|
}
|
|
|
|
pub(crate) fn mount() -> Arc<Router> {
|
|
let r = <Router>::new()
|
|
.config(
|
|
Config::new()
|
|
// TODO: This messes with Tauri's hot reload so we can't use it until their is a solution upstream. https://github.com/tauri-apps/tauri/issues/4617
|
|
// .export_ts_bindings(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("./index.ts")),
|
|
.set_ts_bindings_header("/* eslint-disable */"),
|
|
)
|
|
.query("version", |_, _: ()| env!("CARGO_PKG_VERSION"))
|
|
.query("getNode", |ctx, _: ()| async move {
|
|
Ok(NodeState {
|
|
config: ctx.config.get().await,
|
|
// We are taking the assumption here that this value is only used on the frontend for display purposes
|
|
data_path: ctx.config.data_directory().to_string_lossy().into_owned(),
|
|
})
|
|
})
|
|
.merge("library.", libraries::mount())
|
|
.merge("volumes.", volumes::mount())
|
|
.merge("tags.", tags::mount())
|
|
.merge("locations.", locations::mount())
|
|
.merge("files.", files::mount())
|
|
.merge("jobs.", jobs::mount())
|
|
// TODO: Scope the invalidate queries to a specific library (filtered server side)
|
|
.subscription("invalidateQuery", |ctx, _: ()| {
|
|
let mut event_bus_rx = ctx.event_bus.subscribe();
|
|
let mut last = Instant::now();
|
|
async_stream::stream! {
|
|
while let Ok(event) = event_bus_rx.recv().await {
|
|
match event {
|
|
CoreEvent::InvalidateOperation(op) => yield op,
|
|
CoreEvent::InvalidateOperationDebounced(op) => {
|
|
let current = Instant::now();
|
|
if current.duration_since(last) > Duration::from_millis(1000 / 60) {
|
|
last = current;
|
|
yield op;
|
|
}
|
|
},
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.build()
|
|
.arced();
|
|
InvalidRequests::validate(r.clone()); // This validates all invalidation calls.
|
|
r
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::path::PathBuf;
|
|
|
|
/// This test will ensure the rspc router and all calls to `invalidate_query` are valid and also export an updated version of the Typescript bindings.
|
|
#[test]
|
|
fn test_and_export_rspc_bindings() {
|
|
let r = super::mount();
|
|
r.export_ts(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("./index.ts"))
|
|
.expect("Error exporting rspc Typescript bindings!");
|
|
r.export_ts(
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../apps/mobile/src/types/bindings.ts"),
|
|
)
|
|
.expect("Error exporting rspc Typescript bindings!");
|
|
}
|
|
}
|