diff --git a/packages/core/bindings/CoreResponse.ts b/packages/core/bindings/CoreResponse.ts index e3d962121..48d377889 100644 --- a/packages/core/bindings/CoreResponse.ts +++ b/packages/core/bindings/CoreResponse.ts @@ -1,4 +1,5 @@ +import type { Directory } from "./Directory"; import type { LocationResource } from "./LocationResource"; import type { Volume } from "./Volume"; -export type CoreResponse = { key: "Success" } | { key: "SysGetVolumes", data: Array } | { key: "SysGetLocations", data: LocationResource }; \ No newline at end of file +export type CoreResponse = { key: "Success" } | { key: "SysGetVolumes", data: Array } | { key: "SysGetLocations", data: LocationResource } | { key: "LibGetExplorerDir", data: Directory }; \ No newline at end of file diff --git a/packages/core/bindings/Directory.ts b/packages/core/bindings/Directory.ts new file mode 100644 index 000000000..a7ee02d28 --- /dev/null +++ b/packages/core/bindings/Directory.ts @@ -0,0 +1,3 @@ +import type { FileResource } from "./FileResource"; + +export interface Directory { directory: FileResource, contents: Array, } \ No newline at end of file diff --git a/packages/core/bindings/FileResource.ts b/packages/core/bindings/FileResource.ts new file mode 100644 index 000000000..f78a709d0 --- /dev/null +++ b/packages/core/bindings/FileResource.ts @@ -0,0 +1,3 @@ +import type { LocationResource } from "./LocationResource"; + +export interface FileResource { id: bigint, is_dir: boolean, location_id: bigint, stem: string, name: string, extension: string | null, quick_checksum: string | null, full_checksum: string | null, size_in_bytes: string, encryption: bigint, date_created: string, date_modified: string, date_indexed: string, ipfs_id: string | null, location: LocationResource | null, parent_id: bigint | null, } \ No newline at end of file diff --git a/packages/core/index.ts b/packages/core/index.ts index 12fbf654c..ffab81076 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,8 +1,9 @@ export * from './bindings/ClientCommand'; -export * from './bindings/ClientEvent'; export * from './bindings/ClientQuery'; -export * from './bindings/ClientResponse'; export * from './bindings/CoreEvent'; export * from './bindings/CoreResource'; export * from './bindings/CoreResponse'; +export * from './bindings/Directory'; +export * from './bindings/FileResource'; +export * from './bindings/LocationResource'; export * from './bindings/Volume'; diff --git a/packages/core/scripts/bindingsIndex.ts b/packages/core/scripts/bindingsIndex.ts index 3948d200a..dfc78432f 100644 --- a/packages/core/scripts/bindingsIndex.ts +++ b/packages/core/scripts/bindingsIndex.ts @@ -10,6 +10,7 @@ import * as path from 'path'; for (let binding of bindings) { str += `export * from './bindings/${binding.split('.')[0]}';\n`; } + await fs.rm(path.join(__dirname, '../index.ts')); await fs.writeFile(path.join(__dirname, '../index.ts'), str); })(); diff --git a/packages/core/src/file/indexer.rs b/packages/core/src/file/indexer.rs index 979a4a69b..f28f187de 100644 --- a/packages/core/src/file/indexer.rs +++ b/packages/core/src/file/indexer.rs @@ -2,6 +2,7 @@ use std::{collections::HashMap, ffi::OsStr, fs, path::Path, path::PathBuf, time: use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; use walkdir::{DirEntry, WalkDir}; use super::watcher::watch_dir; @@ -31,10 +32,15 @@ pub async fn scan(path: &str) -> Result<()> { let location = create_location(&path).await?; // query db to highers id, so we can increment it for the new files indexed - let mut next_file_id = match db.file().find_first(vec![]).exec().await { - Some(file) => file.id, - None => 0, + #[derive(Deserialize, Serialize, Debug)] + struct QueryRes { + id: i64, + } + let mut next_file_id = match db._query_raw::(r#"SELECT MAX(id) id FROM files"#).await { + Ok(rows) => rows[0].id, + Err(e) => Err(anyhow!("Error querying for next file id: {}", e))?, }; + let mut get_id = || { next_file_id += 1; // increment id next_file_id @@ -129,8 +135,7 @@ fn prepare_model(file_path: &PathBuf, id: i64, location: &LocationResource, pare Some(p) => p .clone() .strip_prefix(&location_path) - .unwrap_or_default() - .strip_suffix(format!("{}{}", name, extension).as_str()) + .and_then(|p| p.strip_suffix(format!("{}{}", name, extension).as_str())) .unwrap_or_default(), None => return Err(anyhow!("{}", file_path.to_str().unwrap_or_default())), }; diff --git a/packages/core/src/file/mod.rs b/packages/core/src/file/mod.rs index bbace0953..2718001fe 100644 --- a/packages/core/src/file/mod.rs +++ b/packages/core/src/file/mod.rs @@ -10,6 +10,7 @@ pub mod thumb; pub mod watcher; #[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[ts(export)] pub struct FileResource { pub id: i64, pub is_dir: bool, diff --git a/packages/core/src/prisma.rs b/packages/core/src/prisma.rs index d9584d5cd..31e183320 100644 --- a/packages/core/src/prisma.rs +++ b/packages/core/src/prisma.rs @@ -13,7 +13,7 @@ pub struct PrismaClient { query_schema: Arc, } pub async fn new_client() -> PrismaClient { - let datamodel_str = "datasource db {\n provider = \"sqlite\"\n url = \"file:dev.db\"\n}\n\ngenerator client {\n provider = \"prisma-client-rust\"\n output = \"../src/prisma.rs\"\n}\n\ngenerator js {\n provider = \"prisma-client-js\"\n output = \"../types\"\n}\n\nmodel Migration {\n id Int @id @default(autoincrement())\n name String\n checksum String @unique\n steps_applied Int @default(0)\n applied_at DateTime @default(now())\n\n @@map(\"_migrations\")\n}\n\nmodel Library {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n remote_id String?\n is_primary Boolean @default(true)\n encryption Int @default(0)\n date_created DateTime @default(now())\n timezone String?\n spaces Space[]\n\n @@map(\"libraries\")\n}\n\nmodel LibraryStatistics {\n id Int @id @default(autoincrement())\n date_captured DateTime @default(now())\n library_id Int @unique\n total_file_count Int @default(0)\n total_bytes_used String @default(\"0\")\n total_byte_capacity String @default(\"0\")\n total_unique_bytes String @default(\"0\")\n\n @@map(\"library_statistics\")\n}\n\nmodel Client {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n platform Int @default(0)\n version String?\n online Boolean? @default(true)\n last_seen DateTime @default(now())\n timezone String?\n date_created DateTime @default(now())\n jobs Job[]\n\n @@map(\"clients\")\n}\n\nmodel Location {\n id Int @id @default(autoincrement())\n name String?\n path String?\n total_capacity Int?\n available_capacity Int?\n is_removable Boolean @default(true)\n is_ejectable Boolean @default(true)\n is_root_filesystem Boolean @default(true)\n is_online Boolean @default(true)\n date_created DateTime @default(now())\n files File[]\n\n @@map(\"locations\")\n}\n\nmodel File {\n id Int @id @default(autoincrement())\n is_dir Boolean @default(false)\n location_id Int\n stem String\n name String\n extension String?\n quick_checksum String? // 100 * 100 byte samples\n full_checksum String? // full byte to byte hash\n size_in_bytes String\n encryption Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n date_indexed DateTime @default(now())\n ipfs_id String?\n\n location Location? @relation(fields: [location_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n parent File? @relation(\"directory_files\", fields: [parent_id], references: [id])\n parent_id Int?\n children File[] @relation(\"directory_files\")\n\n file_tags TagOnFile[]\n @@unique([location_id, stem, name, extension])\n @@map(\"files\")\n}\n\nmodel Tag {\n id Int @id @default(autoincrement())\n name String?\n encryption Int? @default(0)\n total_files Int? @default(0)\n redundancy_goal Int? @default(1)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n tag_files TagOnFile[]\n\n @@map(\"tags\")\n}\n\nmodel TagOnFile {\n date_created DateTime @default(now())\n\n tag_id Int\n tag Tag @relation(fields: [tag_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n file_id Int\n file File @relation(fields: [file_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@id([tag_id, file_id])\n @@map(\"tags_on_files\")\n}\n\nmodel Job {\n id Int @id @default(autoincrement())\n client_id Int\n action Int\n status Int @default(0)\n percentage_complete Int @default(0)\n task_count Int @default(1)\n completed_task_count Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n clients Client @relation(fields: [client_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@map(\"jobs\")\n}\n\nmodel Space {\n id Int @id @default(autoincrement())\n name String\n encryption Int? @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n Library Library? @relation(fields: [libraryId], references: [id])\n libraryId Int?\n @@map(\"spaces\")\n}\n" ; + let datamodel_str = "datasource db {\n provider = \"sqlite\"\n url = \"file:dev.db\"\n}\n\ngenerator client {\n provider = \"prisma-client-rust\"\n output = \"../src/prisma.rs\"\n}\n\n// generator js {\n// provider = \"prisma-client-js\"\n// output = \"../types\"\n// }\n\nmodel Migration {\n id Int @id @default(autoincrement())\n name String\n checksum String @unique\n steps_applied Int @default(0)\n applied_at DateTime @default(now())\n\n @@map(\"_migrations\")\n}\n\nmodel Library {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n remote_id String?\n is_primary Boolean @default(true)\n encryption Int @default(0)\n date_created DateTime @default(now())\n timezone String?\n spaces Space[]\n\n @@map(\"libraries\")\n}\n\nmodel LibraryStatistics {\n id Int @id @default(autoincrement())\n date_captured DateTime @default(now())\n library_id Int @unique\n total_file_count Int @default(0)\n total_bytes_used String @default(\"0\")\n total_byte_capacity String @default(\"0\")\n total_unique_bytes String @default(\"0\")\n\n @@map(\"library_statistics\")\n}\n\nmodel Client {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n platform Int @default(0)\n version String?\n online Boolean? @default(true)\n last_seen DateTime @default(now())\n timezone String?\n date_created DateTime @default(now())\n jobs Job[]\n\n @@map(\"clients\")\n}\n\nmodel Location {\n id Int @id @default(autoincrement())\n name String?\n path String?\n total_capacity Int?\n available_capacity Int?\n is_removable Boolean @default(true)\n is_ejectable Boolean @default(true)\n is_root_filesystem Boolean @default(true)\n is_online Boolean @default(true)\n date_created DateTime @default(now())\n files File[]\n\n @@map(\"locations\")\n}\n\nmodel File {\n id Int @id @default(autoincrement())\n is_dir Boolean @default(false)\n location_id Int\n stem String\n name String\n extension String?\n quick_checksum String? // 100 * 100 byte samples\n full_checksum String? // full byte to byte hash\n size_in_bytes String\n encryption Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n date_indexed DateTime @default(now())\n ipfs_id String?\n\n location Location? @relation(fields: [location_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n parent File? @relation(\"directory_files\", fields: [parent_id], references: [id])\n parent_id Int?\n children File[] @relation(\"directory_files\")\n\n file_tags TagOnFile[]\n @@unique([location_id, stem, name, extension])\n @@map(\"files\")\n}\n\nmodel Tag {\n id Int @id @default(autoincrement())\n name String?\n encryption Int? @default(0)\n total_files Int? @default(0)\n redundancy_goal Int? @default(1)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n tag_files TagOnFile[]\n\n @@map(\"tags\")\n}\n\nmodel TagOnFile {\n date_created DateTime @default(now())\n\n tag_id Int\n tag Tag @relation(fields: [tag_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n file_id Int\n file File @relation(fields: [file_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@id([tag_id, file_id])\n @@map(\"tags_on_files\")\n}\n\nmodel Job {\n id Int @id @default(autoincrement())\n client_id Int\n action Int\n status Int @default(0)\n percentage_complete Int @default(0)\n task_count Int @default(1)\n completed_task_count Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n clients Client @relation(fields: [client_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@map(\"jobs\")\n}\n\nmodel Space {\n id Int @id @default(autoincrement())\n name String\n encryption Int? @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n Library Library? @relation(fields: [libraryId], references: [id])\n libraryId Int?\n @@map(\"spaces\")\n}\n" ; let config = parse_configuration(datamodel_str).unwrap().subject; let source = config.datasources.first().expect("Pleasy supply a datasource in your schema.prisma file"); let url = if let Some(url) = source.load_shadow_database_url().unwrap() { @@ -36,7 +36,7 @@ pub async fn new_client() -> PrismaClient { new_client_with_url(&url).await } pub async fn new_client_with_url(url: &str) -> PrismaClient { - let datamodel_str = "datasource db {\n provider = \"sqlite\"\n url = \"file:dev.db\"\n}\n\ngenerator client {\n provider = \"prisma-client-rust\"\n output = \"../src/prisma.rs\"\n}\n\ngenerator js {\n provider = \"prisma-client-js\"\n output = \"../types\"\n}\n\nmodel Migration {\n id Int @id @default(autoincrement())\n name String\n checksum String @unique\n steps_applied Int @default(0)\n applied_at DateTime @default(now())\n\n @@map(\"_migrations\")\n}\n\nmodel Library {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n remote_id String?\n is_primary Boolean @default(true)\n encryption Int @default(0)\n date_created DateTime @default(now())\n timezone String?\n spaces Space[]\n\n @@map(\"libraries\")\n}\n\nmodel LibraryStatistics {\n id Int @id @default(autoincrement())\n date_captured DateTime @default(now())\n library_id Int @unique\n total_file_count Int @default(0)\n total_bytes_used String @default(\"0\")\n total_byte_capacity String @default(\"0\")\n total_unique_bytes String @default(\"0\")\n\n @@map(\"library_statistics\")\n}\n\nmodel Client {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n platform Int @default(0)\n version String?\n online Boolean? @default(true)\n last_seen DateTime @default(now())\n timezone String?\n date_created DateTime @default(now())\n jobs Job[]\n\n @@map(\"clients\")\n}\n\nmodel Location {\n id Int @id @default(autoincrement())\n name String?\n path String?\n total_capacity Int?\n available_capacity Int?\n is_removable Boolean @default(true)\n is_ejectable Boolean @default(true)\n is_root_filesystem Boolean @default(true)\n is_online Boolean @default(true)\n date_created DateTime @default(now())\n files File[]\n\n @@map(\"locations\")\n}\n\nmodel File {\n id Int @id @default(autoincrement())\n is_dir Boolean @default(false)\n location_id Int\n stem String\n name String\n extension String?\n quick_checksum String? // 100 * 100 byte samples\n full_checksum String? // full byte to byte hash\n size_in_bytes String\n encryption Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n date_indexed DateTime @default(now())\n ipfs_id String?\n\n location Location? @relation(fields: [location_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n parent File? @relation(\"directory_files\", fields: [parent_id], references: [id])\n parent_id Int?\n children File[] @relation(\"directory_files\")\n\n file_tags TagOnFile[]\n @@unique([location_id, stem, name, extension])\n @@map(\"files\")\n}\n\nmodel Tag {\n id Int @id @default(autoincrement())\n name String?\n encryption Int? @default(0)\n total_files Int? @default(0)\n redundancy_goal Int? @default(1)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n tag_files TagOnFile[]\n\n @@map(\"tags\")\n}\n\nmodel TagOnFile {\n date_created DateTime @default(now())\n\n tag_id Int\n tag Tag @relation(fields: [tag_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n file_id Int\n file File @relation(fields: [file_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@id([tag_id, file_id])\n @@map(\"tags_on_files\")\n}\n\nmodel Job {\n id Int @id @default(autoincrement())\n client_id Int\n action Int\n status Int @default(0)\n percentage_complete Int @default(0)\n task_count Int @default(1)\n completed_task_count Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n clients Client @relation(fields: [client_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@map(\"jobs\")\n}\n\nmodel Space {\n id Int @id @default(autoincrement())\n name String\n encryption Int? @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n Library Library? @relation(fields: [libraryId], references: [id])\n libraryId Int?\n @@map(\"spaces\")\n}\n" ; + let datamodel_str = "datasource db {\n provider = \"sqlite\"\n url = \"file:dev.db\"\n}\n\ngenerator client {\n provider = \"prisma-client-rust\"\n output = \"../src/prisma.rs\"\n}\n\n// generator js {\n// provider = \"prisma-client-js\"\n// output = \"../types\"\n// }\n\nmodel Migration {\n id Int @id @default(autoincrement())\n name String\n checksum String @unique\n steps_applied Int @default(0)\n applied_at DateTime @default(now())\n\n @@map(\"_migrations\")\n}\n\nmodel Library {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n remote_id String?\n is_primary Boolean @default(true)\n encryption Int @default(0)\n date_created DateTime @default(now())\n timezone String?\n spaces Space[]\n\n @@map(\"libraries\")\n}\n\nmodel LibraryStatistics {\n id Int @id @default(autoincrement())\n date_captured DateTime @default(now())\n library_id Int @unique\n total_file_count Int @default(0)\n total_bytes_used String @default(\"0\")\n total_byte_capacity String @default(\"0\")\n total_unique_bytes String @default(\"0\")\n\n @@map(\"library_statistics\")\n}\n\nmodel Client {\n id Int @id @default(autoincrement())\n uuid String @unique\n name String\n platform Int @default(0)\n version String?\n online Boolean? @default(true)\n last_seen DateTime @default(now())\n timezone String?\n date_created DateTime @default(now())\n jobs Job[]\n\n @@map(\"clients\")\n}\n\nmodel Location {\n id Int @id @default(autoincrement())\n name String?\n path String?\n total_capacity Int?\n available_capacity Int?\n is_removable Boolean @default(true)\n is_ejectable Boolean @default(true)\n is_root_filesystem Boolean @default(true)\n is_online Boolean @default(true)\n date_created DateTime @default(now())\n files File[]\n\n @@map(\"locations\")\n}\n\nmodel File {\n id Int @id @default(autoincrement())\n is_dir Boolean @default(false)\n location_id Int\n stem String\n name String\n extension String?\n quick_checksum String? // 100 * 100 byte samples\n full_checksum String? // full byte to byte hash\n size_in_bytes String\n encryption Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n date_indexed DateTime @default(now())\n ipfs_id String?\n\n location Location? @relation(fields: [location_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n parent File? @relation(\"directory_files\", fields: [parent_id], references: [id])\n parent_id Int?\n children File[] @relation(\"directory_files\")\n\n file_tags TagOnFile[]\n @@unique([location_id, stem, name, extension])\n @@map(\"files\")\n}\n\nmodel Tag {\n id Int @id @default(autoincrement())\n name String?\n encryption Int? @default(0)\n total_files Int? @default(0)\n redundancy_goal Int? @default(1)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n tag_files TagOnFile[]\n\n @@map(\"tags\")\n}\n\nmodel TagOnFile {\n date_created DateTime @default(now())\n\n tag_id Int\n tag Tag @relation(fields: [tag_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n file_id Int\n file File @relation(fields: [file_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@id([tag_id, file_id])\n @@map(\"tags_on_files\")\n}\n\nmodel Job {\n id Int @id @default(autoincrement())\n client_id Int\n action Int\n status Int @default(0)\n percentage_complete Int @default(0)\n task_count Int @default(1)\n completed_task_count Int @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n clients Client @relation(fields: [client_id], references: [id], onDelete: NoAction, onUpdate: NoAction)\n\n @@map(\"jobs\")\n}\n\nmodel Space {\n id Int @id @default(autoincrement())\n name String\n encryption Int? @default(0)\n date_created DateTime @default(now())\n date_modified DateTime @default(now())\n\n Library Library? @relation(fields: [libraryId], references: [id])\n libraryId Int?\n @@map(\"spaces\")\n}\n" ; let config = parse_configuration(datamodel_str).unwrap().subject; let source = config.datasources.first().expect("Pleasy supply a datasource in your schema.prisma file"); let (db_name, executor) = executor::load(&source, &[], &url).await.unwrap();