job manager wip

This commit is contained in:
Jamie Pine
2022-09-07 20:04:13 -07:00
parent 3aa7285917
commit 39b80eb018
15 changed files with 390 additions and 217 deletions

View File

@@ -2,136 +2,136 @@
export type Operations = {
queries:
{ key: ["tags.get", LibraryArgs<number>], result: Tag | null } |
{ key: ["library.getStatistics", LibraryArgs<null>], result: Statistics } |
{ key: ["locations.indexer_rulesget", LibraryArgs<number>], result: IndexerRule } |
{ key: ["jobs.getHistory", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["tags.getForFile", LibraryArgs<number>], result: Array<Tag> } |
{ key: ["locations.getById", LibraryArgs<number>], result: Location | null } |
{ key: ["locations.indexer_rulesget", LibraryArgs<number>], result: IndexerRule } |
{ key: ["files.readMetadata", LibraryArgs<number>], result: null } |
{ key: ["tags.getAll", LibraryArgs<null>], result: Array<Tag> } |
{ key: ["jobs.getHistory", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["getNode"], result: NodeState } |
{ key: ["library.getStatistics", LibraryArgs<null>], result: Statistics } |
{ key: ["version"], result: string } |
{ key: ["jobs.getRunning", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["library.get"], result: Array<LibraryConfigWrapped> } |
{ key: ["locations.get", LibraryArgs<null>], result: Array<Location> } |
{ key: ["volumes.get"], result: Array<Volume> } |
{ key: ["locations.indexer_ruleslist", LibraryArgs<null>], result: Array<IndexerRule> } |
{ key: ["locations.get", LibraryArgs<null>], result: Array<Location> } |
{ key: ["locations.getExplorerData", LibraryArgs<LocationExplorerArgs>], result: ExplorerData } |
{ key: ["tags.getExplorerData", LibraryArgs<number>], result: ExplorerData },
{ key: ["tags.get", LibraryArgs<number>], result: Tag | null } |
{ key: ["library.get"], result: Array<LibraryConfigWrapped> } |
{ key: ["getNode"], result: NodeState } |
{ key: ["tags.getExplorerData", LibraryArgs<number>], result: ExplorerData } |
{ key: ["version"], result: string } |
{ key: ["files.readMetadata", LibraryArgs<number>], result: null } |
{ key: ["jobs.getRunning", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["locations.indexer_ruleslist", LibraryArgs<null>], result: Array<IndexerRule> },
mutations:
{ key: ["files.setFavorite", LibraryArgs<SetFavoriteArgs>], result: null } |
{ key: ["library.edit", EditLibraryArgs], result: null } |
{ key: ["tags.assign", LibraryArgs<TagAssignArgs>], result: null } |
{ key: ["jobs.generateThumbsForLocation", LibraryArgs<GenerateThumbsForLocationArgs>], result: null } |
{ key: ["files.setNote", LibraryArgs<SetNoteArgs>], result: null } |
{ key: ["locations.create", LibraryArgs<LocationCreateArgs>], result: Location } |
{ key: ["tags.update", LibraryArgs<TagUpdateArgs>], result: null } |
{ key: ["locations.fullRescan", LibraryArgs<number>], result: null } |
{ key: ["locations.quickRescan", LibraryArgs<null>], result: null } |
{ key: ["locations.indexer_rulesdelete", LibraryArgs<number>], result: null } |
{ key: ["library.create", string], result: null } |
{ key: ["locations.quickRescan", LibraryArgs<null>], result: null } |
{ key: ["locations.update", LibraryArgs<LocationUpdateArgs>], result: null } |
{ key: ["tags.assign", LibraryArgs<TagAssignArgs>], result: null } |
{ key: ["locations.fullRescan", LibraryArgs<number>], result: null } |
{ key: ["files.delete", LibraryArgs<number>], result: null } |
{ key: ["files.setFavorite", LibraryArgs<SetFavoriteArgs>], result: null } |
{ key: ["files.setNote", LibraryArgs<SetNoteArgs>], result: null } |
{ key: ["tags.delete", LibraryArgs<number>], result: null } |
{ key: ["tags.update", LibraryArgs<TagUpdateArgs>], result: null } |
{ key: ["locations.indexer_rulescreate", LibraryArgs<IndexerRuleCreateArgs>], result: IndexerRule } |
{ key: ["locations.create", LibraryArgs<LocationCreateArgs>], result: Location } |
{ key: ["jobs.generateThumbsForLocation", LibraryArgs<GenerateThumbsForLocationArgs>], result: null } |
{ key: ["library.edit", EditLibraryArgs], result: null } |
{ key: ["library.delete", string], result: null } |
{ key: ["locations.delete", LibraryArgs<number>], result: null } |
{ key: ["files.delete", LibraryArgs<number>], result: null } |
{ key: ["locations.update", LibraryArgs<LocationUpdateArgs>], result: null } |
{ key: ["jobs.identifyUniqueFiles", LibraryArgs<IdentifyUniqueFilesArgs>], result: null } |
{ key: ["library.delete", string], result: null } |
{ key: ["tags.create", LibraryArgs<TagCreateArgs>], result: Tag } |
{ key: ["locations.delete", LibraryArgs<number>], result: null },
{ key: ["tags.delete", LibraryArgs<number>], result: null },
subscriptions:
{ key: ["invalidateQuery"], result: InvalidateOperationEvent } |
{ key: ["jobs.newThumbnail", LibraryArgs<null>], result: string }
};
export interface Node { id: number, pub_id: Array<number>, name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string, sync_events: Array<SyncEvent> | null, jobs: Array<Job> | null, Location: Array<Location> | null }
export interface Job { id: Array<number>, name: string, node_id: number, action: number, status: number, data: Array<number> | null, task_count: number, completed_task_count: number, date_created: string, date_modified: string, seconds_elapsed: number, nodes: Node | null }
export interface IndexerRuleCreateArgs { kind: RuleKind, name: string, parameters: Array<number> }
export interface LibraryConfig { version: string | null, name: string, description: string }
export interface LocationUpdateArgs { id: number, name: string | null, indexer_rules_ids: Array<number> }
export type RuleKind = "AcceptFilesByGlob" | "RejectFilesByGlob" | "AcceptIfChildrenDirectoriesArePresent" | "RejectIfChildrenDirectoriesArePresent"
export interface TagCreateArgs { name: string, color: string }
export interface EditLibraryArgs { id: string, name: string | null, description: string | null }
export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused"
export interface GenerateThumbsForLocationArgs { id: number, path: string }
export interface IndexerRule { id: number, kind: number, name: string, parameters: Array<number>, date_created: string, date_modified: string, locations: Array<IndexerRulesInLocation> | null }
export interface JobReport { id: string, name: string, data: Array<number> | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number }
export interface Location { id: number, pub_id: Array<number>, node_id: number | null, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, filesystem: string | null, disk_type: number | null, is_removable: boolean | null, is_online: boolean, is_archived: boolean, date_created: string, node: Node | null | null, file_paths: Array<FilePath> | null, indexer_rules: Array<IndexerRulesInLocation> | null }
export interface TagUpdateArgs { id: number, name: string | null, color: string | null }
export interface Tag { id: number, pub_id: Array<number>, name: string | null, color: string | null, total_files: number | null, redundancy_goal: number | null, date_created: string, date_modified: string, tag_files: Array<TagOnFile> | null }
export interface Key { id: number, checksum: string, name: string | null, date_created: string | null, algorithm: number | null, files: Array<File> | null, file_paths: Array<FilePath> | null }
export interface File { id: number, cas_id: string, integrity_checksum: string | null, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, tags: Array<TagOnFile> | null, labels: Array<LabelOnFile> | null, albums: Array<FileInAlbum> | null, spaces: Array<FileInSpace> | null, paths: Array<FilePath> | null, comments: Array<Comment> | null, media_data: MediaData | null | null, key: Key | null | null }
export interface LibraryArgs<T> { library_id: string, arg: T }
export interface InvalidateOperationEvent { key: string, arg: any }
export interface FileInSpace { date_created: string, space_id: number, space: Space | null, file_id: number, file: File | null }
export interface TagOnFile { date_created: string, tag_id: number, tag: Tag | null, file_id: number, file: File | null }
export interface Space { id: number, pub_id: Array<number>, name: string | null, description: string | null, date_created: string, date_modified: string, files: Array<FileInSpace> | null }
export interface Comment { id: number, pub_id: Array<number>, content: string, date_created: string, date_modified: string, file_id: number | null, file: File | null | null }
export interface IdentifyUniqueFilesArgs { id: number, path: string }
export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null, files: File | null | null }
export type ExplorerContext = { type: "Location" } & Location | { type: "Tag" } & Tag
export interface NodeConfig { version: string | null, id: string, name: string, p2p_port: number | null }
export interface LabelOnFile { date_created: string, label_id: number, label: Label | null, file_id: number, file: File | null }
export interface SetFavoriteArgs { id: number, favorite: boolean }
export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean }
export interface SetNoteArgs { id: number, note: string | null }
export interface LocationCreateArgs { path: string, indexer_rules_ids: Array<number> }
export interface LibraryConfigWrapped { uuid: string, config: LibraryConfig }
export interface Label { id: number, pub_id: Array<number>, name: string | null, date_created: string, date_modified: string, label_files: Array<LabelOnFile> | null }
export interface Album { id: number, pub_id: Array<number>, name: string, is_hidden: boolean, date_created: string, date_modified: string, files: Array<FileInAlbum> | null }
export interface SyncEvent { id: number, node_id: number, timestamp: string, record_id: Array<number>, kind: number, column: string | null, value: string, node: Node | null }
export interface FileInAlbum { date_created: string, album_id: number, album: Album | null, file_id: number, file: File | null }
export interface IndexerRuleCreateArgs { kind: RuleKind, name: string, parameters: Array<number> }
export type ExplorerItem = { type: "Path" } & FilePath | { type: "Object" } & File
export interface TagCreateArgs { name: string, color: string }
export interface LocationExplorerArgs { location_id: number, path: string, limit: number, cursor: string | null }
export interface Statistics { id: number, date_captured: string, total_file_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string }
export interface IndexerRulesInLocation { date_created: string, location_id: number, location: Location | null, indexer_rule_id: number, indexer_rule: IndexerRule | null }
export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string }
export interface Album { id: number, pub_id: Array<number>, name: string, is_hidden: boolean, date_created: string, date_modified: string, files: Array<FileInAlbum> | null }
export interface FilePath { id: number, is_dir: boolean, location_id: number, materialized_path: string, name: string, extension: string | null, file_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, file: File | null | null, location: Location | null | null, key: Key | null | null }
export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean }
export interface LibraryArgs<T> { library_id: string, arg: T }
export interface IndexerRule { id: number, kind: number, name: string, parameters: Array<number>, date_created: string, date_modified: string, locations: Array<IndexerRulesInLocation> | null }
export interface ExplorerData { context: ExplorerContext, items: Array<ExplorerItem> }
export interface InvalidateOperationEvent { key: string, arg: any }
export interface Label { id: number, pub_id: Array<number>, name: string | null, date_created: string, date_modified: string, label_files: Array<LabelOnFile> | null }
export interface Tag { id: number, pub_id: Array<number>, name: string | null, color: string | null, total_files: number | null, redundancy_goal: number | null, date_created: string, date_modified: string, tag_files: Array<TagOnFile> | null }
export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null, files: File | null | null }
export interface FileInSpace { date_created: string, space_id: number, space: Space | null, file_id: number, file: File | null }
export interface IdentifyUniqueFilesArgs { id: number, path: string }
export interface NodeConfig { version: string | null, id: string, name: string, p2p_port: number | null }
export interface Key { id: number, checksum: string, name: string | null, date_created: string | null, algorithm: number | null, files: Array<File> | null, file_paths: Array<FilePath> | null }
export interface Comment { id: number, pub_id: Array<number>, content: string, date_created: string, date_modified: string, file_id: number | null, file: File | null | null }
export interface Node { id: number, pub_id: Array<number>, name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string, sync_events: Array<SyncEvent> | null, jobs: Array<Job> | null, Location: Array<Location> | null }
export type RuleKind = "AcceptFilesByGlob" | "RejectFilesByGlob" | "AcceptIfChildrenDirectoriesArePresent" | "RejectIfChildrenDirectoriesArePresent"
export interface SetFavoriteArgs { id: number, favorite: boolean }
export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string }
export interface LocationExplorerArgs { location_id: number, path: string, limit: number, cursor: string | null }
export type ExplorerItem = { type: "Path" } & FilePath | { type: "Object" } & File
export interface SetNoteArgs { id: number, note: string | null }
export type ExplorerContext = { type: "Location" } & Location | { type: "Tag" } & Tag
export interface Statistics { id: number, date_captured: string, total_file_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string }
export interface LibraryConfig { version: string | null, name: string, description: string }
export interface Job { id: Array<number>, name: string, node_id: number, action: number, status: number, data: Array<number> | null, metadata: Array<number> | null, task_count: number, completed_task_count: number, date_created: string, date_modified: string, seconds_elapsed: number, nodes: Node | null }
export interface TagOnFile { date_created: string, tag_id: number, tag: Tag | null, file_id: number, file: File | null }
export interface Location { id: number, pub_id: Array<number>, node_id: number | null, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, filesystem: string | null, disk_type: number | null, is_removable: boolean | null, is_online: boolean, is_archived: boolean, date_created: string, node: Node | null | null, file_paths: Array<FilePath> | null, indexer_rules: Array<IndexerRulesInLocation> | null }
export interface IndexerRulesInLocation { date_created: string, location_id: number, location: Location | null, indexer_rule_id: number, indexer_rule: IndexerRule | null }
export interface LocationUpdateArgs { id: number, name: string | null, indexer_rules_ids: Array<number> }
export interface JobReport { id: string, name: string, data: Array<number> | null, metadata: Array<number> | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number }
export interface GenerateThumbsForLocationArgs { id: number, path: string }
export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused"
export interface LabelOnFile { date_created: string, label_id: number, label: Label | null, file_id: number, file: File | null }
export interface ConfigMetadata { version: string | null }
export interface LibraryConfigWrapped { uuid: string, config: LibraryConfig }
export interface LocationCreateArgs { path: string, indexer_rules_ids: Array<number> }
export interface FileInAlbum { date_created: string, album_id: number, album: Album | null, file_id: number, file: File | null }
export interface TagAssignArgs { file_id: number, tag_id: number, unassign: boolean }
export interface ExplorerData { context: ExplorerContext, items: Array<ExplorerItem> }
export interface Space { id: number, pub_id: Array<number>, name: string | null, description: string | null, date_created: string, date_modified: string, files: Array<FileInSpace> | null }
export interface File { id: number, cas_id: string, integrity_checksum: string | null, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, tags: Array<TagOnFile> | null, labels: Array<LabelOnFile> | null, albums: Array<FileInAlbum> | null, spaces: Array<FileInSpace> | null, paths: Array<FilePath> | null, comments: Array<Comment> | null, media_data: MediaData | null | null, key: Key | null | null }
export interface TagUpdateArgs { id: number, name: string | null, color: string | null }

View File

@@ -2,136 +2,136 @@
export type Operations = {
queries:
{ key: ["tags.get", LibraryArgs<number>], result: Tag | null } |
{ key: ["library.getStatistics", LibraryArgs<null>], result: Statistics } |
{ key: ["locations.indexer_rulesget", LibraryArgs<number>], result: IndexerRule } |
{ key: ["jobs.getHistory", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["tags.getForFile", LibraryArgs<number>], result: Array<Tag> } |
{ key: ["locations.getById", LibraryArgs<number>], result: Location | null } |
{ key: ["locations.indexer_rulesget", LibraryArgs<number>], result: IndexerRule } |
{ key: ["files.readMetadata", LibraryArgs<number>], result: null } |
{ key: ["tags.getAll", LibraryArgs<null>], result: Array<Tag> } |
{ key: ["jobs.getHistory", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["getNode"], result: NodeState } |
{ key: ["library.getStatistics", LibraryArgs<null>], result: Statistics } |
{ key: ["version"], result: string } |
{ key: ["jobs.getRunning", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["library.get"], result: Array<LibraryConfigWrapped> } |
{ key: ["locations.get", LibraryArgs<null>], result: Array<Location> } |
{ key: ["volumes.get"], result: Array<Volume> } |
{ key: ["locations.indexer_ruleslist", LibraryArgs<null>], result: Array<IndexerRule> } |
{ key: ["locations.get", LibraryArgs<null>], result: Array<Location> } |
{ key: ["locations.getExplorerData", LibraryArgs<LocationExplorerArgs>], result: ExplorerData } |
{ key: ["tags.getExplorerData", LibraryArgs<number>], result: ExplorerData },
{ key: ["tags.get", LibraryArgs<number>], result: Tag | null } |
{ key: ["library.get"], result: Array<LibraryConfigWrapped> } |
{ key: ["getNode"], result: NodeState } |
{ key: ["tags.getExplorerData", LibraryArgs<number>], result: ExplorerData } |
{ key: ["version"], result: string } |
{ key: ["files.readMetadata", LibraryArgs<number>], result: null } |
{ key: ["jobs.getRunning", LibraryArgs<null>], result: Array<JobReport> } |
{ key: ["locations.indexer_ruleslist", LibraryArgs<null>], result: Array<IndexerRule> },
mutations:
{ key: ["files.setFavorite", LibraryArgs<SetFavoriteArgs>], result: null } |
{ key: ["library.edit", EditLibraryArgs], result: null } |
{ key: ["tags.assign", LibraryArgs<TagAssignArgs>], result: null } |
{ key: ["jobs.generateThumbsForLocation", LibraryArgs<GenerateThumbsForLocationArgs>], result: null } |
{ key: ["files.setNote", LibraryArgs<SetNoteArgs>], result: null } |
{ key: ["locations.create", LibraryArgs<LocationCreateArgs>], result: Location } |
{ key: ["tags.update", LibraryArgs<TagUpdateArgs>], result: null } |
{ key: ["locations.fullRescan", LibraryArgs<number>], result: null } |
{ key: ["locations.quickRescan", LibraryArgs<null>], result: null } |
{ key: ["locations.indexer_rulesdelete", LibraryArgs<number>], result: null } |
{ key: ["library.create", string], result: null } |
{ key: ["locations.quickRescan", LibraryArgs<null>], result: null } |
{ key: ["locations.update", LibraryArgs<LocationUpdateArgs>], result: null } |
{ key: ["tags.assign", LibraryArgs<TagAssignArgs>], result: null } |
{ key: ["locations.fullRescan", LibraryArgs<number>], result: null } |
{ key: ["files.delete", LibraryArgs<number>], result: null } |
{ key: ["files.setFavorite", LibraryArgs<SetFavoriteArgs>], result: null } |
{ key: ["files.setNote", LibraryArgs<SetNoteArgs>], result: null } |
{ key: ["tags.delete", LibraryArgs<number>], result: null } |
{ key: ["tags.update", LibraryArgs<TagUpdateArgs>], result: null } |
{ key: ["locations.indexer_rulescreate", LibraryArgs<IndexerRuleCreateArgs>], result: IndexerRule } |
{ key: ["locations.create", LibraryArgs<LocationCreateArgs>], result: Location } |
{ key: ["jobs.generateThumbsForLocation", LibraryArgs<GenerateThumbsForLocationArgs>], result: null } |
{ key: ["library.edit", EditLibraryArgs], result: null } |
{ key: ["library.delete", string], result: null } |
{ key: ["locations.delete", LibraryArgs<number>], result: null } |
{ key: ["files.delete", LibraryArgs<number>], result: null } |
{ key: ["locations.update", LibraryArgs<LocationUpdateArgs>], result: null } |
{ key: ["jobs.identifyUniqueFiles", LibraryArgs<IdentifyUniqueFilesArgs>], result: null } |
{ key: ["library.delete", string], result: null } |
{ key: ["tags.create", LibraryArgs<TagCreateArgs>], result: Tag } |
{ key: ["locations.delete", LibraryArgs<number>], result: null },
{ key: ["tags.delete", LibraryArgs<number>], result: null },
subscriptions:
{ key: ["invalidateQuery"], result: InvalidateOperationEvent } |
{ key: ["jobs.newThumbnail", LibraryArgs<null>], result: string }
};
export interface Node { id: number, pub_id: Array<number>, name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string, sync_events: Array<SyncEvent> | null, jobs: Array<Job> | null, Location: Array<Location> | null }
export interface Job { id: Array<number>, name: string, node_id: number, action: number, status: number, data: Array<number> | null, task_count: number, completed_task_count: number, date_created: string, date_modified: string, seconds_elapsed: number, nodes: Node | null }
export interface IndexerRuleCreateArgs { kind: RuleKind, name: string, parameters: Array<number> }
export interface LibraryConfig { version: string | null, name: string, description: string }
export interface LocationUpdateArgs { id: number, name: string | null, indexer_rules_ids: Array<number> }
export type RuleKind = "AcceptFilesByGlob" | "RejectFilesByGlob" | "AcceptIfChildrenDirectoriesArePresent" | "RejectIfChildrenDirectoriesArePresent"
export interface TagCreateArgs { name: string, color: string }
export interface EditLibraryArgs { id: string, name: string | null, description: string | null }
export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused"
export interface GenerateThumbsForLocationArgs { id: number, path: string }
export interface IndexerRule { id: number, kind: number, name: string, parameters: Array<number>, date_created: string, date_modified: string, locations: Array<IndexerRulesInLocation> | null }
export interface JobReport { id: string, name: string, data: Array<number> | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number }
export interface Location { id: number, pub_id: Array<number>, node_id: number | null, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, filesystem: string | null, disk_type: number | null, is_removable: boolean | null, is_online: boolean, is_archived: boolean, date_created: string, node: Node | null | null, file_paths: Array<FilePath> | null, indexer_rules: Array<IndexerRulesInLocation> | null }
export interface TagUpdateArgs { id: number, name: string | null, color: string | null }
export interface Tag { id: number, pub_id: Array<number>, name: string | null, color: string | null, total_files: number | null, redundancy_goal: number | null, date_created: string, date_modified: string, tag_files: Array<TagOnFile> | null }
export interface Key { id: number, checksum: string, name: string | null, date_created: string | null, algorithm: number | null, files: Array<File> | null, file_paths: Array<FilePath> | null }
export interface File { id: number, cas_id: string, integrity_checksum: string | null, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, tags: Array<TagOnFile> | null, labels: Array<LabelOnFile> | null, albums: Array<FileInAlbum> | null, spaces: Array<FileInSpace> | null, paths: Array<FilePath> | null, comments: Array<Comment> | null, media_data: MediaData | null | null, key: Key | null | null }
export interface LibraryArgs<T> { library_id: string, arg: T }
export interface InvalidateOperationEvent { key: string, arg: any }
export interface FileInSpace { date_created: string, space_id: number, space: Space | null, file_id: number, file: File | null }
export interface TagOnFile { date_created: string, tag_id: number, tag: Tag | null, file_id: number, file: File | null }
export interface Space { id: number, pub_id: Array<number>, name: string | null, description: string | null, date_created: string, date_modified: string, files: Array<FileInSpace> | null }
export interface Comment { id: number, pub_id: Array<number>, content: string, date_created: string, date_modified: string, file_id: number | null, file: File | null | null }
export interface IdentifyUniqueFilesArgs { id: number, path: string }
export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null, files: File | null | null }
export type ExplorerContext = { type: "Location" } & Location | { type: "Tag" } & Tag
export interface NodeConfig { version: string | null, id: string, name: string, p2p_port: number | null }
export interface LabelOnFile { date_created: string, label_id: number, label: Label | null, file_id: number, file: File | null }
export interface SetFavoriteArgs { id: number, favorite: boolean }
export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean }
export interface SetNoteArgs { id: number, note: string | null }
export interface LocationCreateArgs { path: string, indexer_rules_ids: Array<number> }
export interface LibraryConfigWrapped { uuid: string, config: LibraryConfig }
export interface Label { id: number, pub_id: Array<number>, name: string | null, date_created: string, date_modified: string, label_files: Array<LabelOnFile> | null }
export interface Album { id: number, pub_id: Array<number>, name: string, is_hidden: boolean, date_created: string, date_modified: string, files: Array<FileInAlbum> | null }
export interface SyncEvent { id: number, node_id: number, timestamp: string, record_id: Array<number>, kind: number, column: string | null, value: string, node: Node | null }
export interface FileInAlbum { date_created: string, album_id: number, album: Album | null, file_id: number, file: File | null }
export interface IndexerRuleCreateArgs { kind: RuleKind, name: string, parameters: Array<number> }
export type ExplorerItem = { type: "Path" } & FilePath | { type: "Object" } & File
export interface TagCreateArgs { name: string, color: string }
export interface LocationExplorerArgs { location_id: number, path: string, limit: number, cursor: string | null }
export interface Statistics { id: number, date_captured: string, total_file_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string }
export interface IndexerRulesInLocation { date_created: string, location_id: number, location: Location | null, indexer_rule_id: number, indexer_rule: IndexerRule | null }
export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string }
export interface Album { id: number, pub_id: Array<number>, name: string, is_hidden: boolean, date_created: string, date_modified: string, files: Array<FileInAlbum> | null }
export interface FilePath { id: number, is_dir: boolean, location_id: number, materialized_path: string, name: string, extension: string | null, file_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, file: File | null | null, location: Location | null | null, key: Key | null | null }
export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean }
export interface LibraryArgs<T> { library_id: string, arg: T }
export interface IndexerRule { id: number, kind: number, name: string, parameters: Array<number>, date_created: string, date_modified: string, locations: Array<IndexerRulesInLocation> | null }
export interface ExplorerData { context: ExplorerContext, items: Array<ExplorerItem> }
export interface InvalidateOperationEvent { key: string, arg: any }
export interface Label { id: number, pub_id: Array<number>, name: string | null, date_created: string, date_modified: string, label_files: Array<LabelOnFile> | null }
export interface Tag { id: number, pub_id: Array<number>, name: string | null, color: string | null, total_files: number | null, redundancy_goal: number | null, date_created: string, date_modified: string, tag_files: Array<TagOnFile> | null }
export interface MediaData { id: number, pixel_width: number | null, pixel_height: number | null, longitude: number | null, latitude: number | null, fps: number | null, capture_device_make: string | null, capture_device_model: string | null, capture_device_software: string | null, duration_seconds: number | null, codecs: string | null, streams: number | null, files: File | null | null }
export interface FileInSpace { date_created: string, space_id: number, space: Space | null, file_id: number, file: File | null }
export interface IdentifyUniqueFilesArgs { id: number, path: string }
export interface NodeConfig { version: string | null, id: string, name: string, p2p_port: number | null }
export interface Key { id: number, checksum: string, name: string | null, date_created: string | null, algorithm: number | null, files: Array<File> | null, file_paths: Array<FilePath> | null }
export interface Comment { id: number, pub_id: Array<number>, content: string, date_created: string, date_modified: string, file_id: number | null, file: File | null | null }
export interface Node { id: number, pub_id: Array<number>, name: string, platform: number, version: string | null, last_seen: string, timezone: string | null, date_created: string, sync_events: Array<SyncEvent> | null, jobs: Array<Job> | null, Location: Array<Location> | null }
export type RuleKind = "AcceptFilesByGlob" | "RejectFilesByGlob" | "AcceptIfChildrenDirectoriesArePresent" | "RejectIfChildrenDirectoriesArePresent"
export interface SetFavoriteArgs { id: number, favorite: boolean }
export interface NodeState { version: string | null, id: string, name: string, p2p_port: number | null, data_path: string }
export interface LocationExplorerArgs { location_id: number, path: string, limit: number, cursor: string | null }
export type ExplorerItem = { type: "Path" } & FilePath | { type: "Object" } & File
export interface SetNoteArgs { id: number, note: string | null }
export type ExplorerContext = { type: "Location" } & Location | { type: "Tag" } & Tag
export interface Statistics { id: number, date_captured: string, total_file_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string }
export interface LibraryConfig { version: string | null, name: string, description: string }
export interface Job { id: Array<number>, name: string, node_id: number, action: number, status: number, data: Array<number> | null, metadata: Array<number> | null, task_count: number, completed_task_count: number, date_created: string, date_modified: string, seconds_elapsed: number, nodes: Node | null }
export interface TagOnFile { date_created: string, tag_id: number, tag: Tag | null, file_id: number, file: File | null }
export interface Location { id: number, pub_id: Array<number>, node_id: number | null, name: string | null, local_path: string | null, total_capacity: number | null, available_capacity: number | null, filesystem: string | null, disk_type: number | null, is_removable: boolean | null, is_online: boolean, is_archived: boolean, date_created: string, node: Node | null | null, file_paths: Array<FilePath> | null, indexer_rules: Array<IndexerRulesInLocation> | null }
export interface IndexerRulesInLocation { date_created: string, location_id: number, location: Location | null, indexer_rule_id: number, indexer_rule: IndexerRule | null }
export interface LocationUpdateArgs { id: number, name: string | null, indexer_rules_ids: Array<number> }
export interface JobReport { id: string, name: string, data: Array<number> | null, metadata: Array<number> | null, date_created: string, date_modified: string, status: JobStatus, task_count: number, completed_task_count: number, message: string, seconds_elapsed: number }
export interface GenerateThumbsForLocationArgs { id: number, path: string }
export type JobStatus = "Queued" | "Running" | "Completed" | "Canceled" | "Failed" | "Paused"
export interface LabelOnFile { date_created: string, label_id: number, label: Label | null, file_id: number, file: File | null }
export interface ConfigMetadata { version: string | null }
export interface LibraryConfigWrapped { uuid: string, config: LibraryConfig }
export interface LocationCreateArgs { path: string, indexer_rules_ids: Array<number> }
export interface FileInAlbum { date_created: string, album_id: number, album: Album | null, file_id: number, file: File | null }
export interface TagAssignArgs { file_id: number, tag_id: number, unassign: boolean }
export interface ExplorerData { context: ExplorerContext, items: Array<ExplorerItem> }
export interface Space { id: number, pub_id: Array<number>, name: string | null, description: string | null, date_created: string, date_modified: string, files: Array<FileInSpace> | null }
export interface File { id: number, cas_id: string, integrity_checksum: string | null, name: string | null, extension: string | null, kind: number, size_in_bytes: string, key_id: number | null, hidden: boolean, favorite: boolean, important: boolean, has_thumbnail: boolean, has_thumbstrip: boolean, has_video_preview: boolean, ipfs_id: string | null, note: string | null, date_created: string, date_modified: string, date_indexed: string, tags: Array<TagOnFile> | null, labels: Array<LabelOnFile> | null, albums: Array<FileInAlbum> | null, spaces: Array<FileInSpace> | null, paths: Array<FilePath> | null, comments: Array<Comment> | null, media_data: MediaData | null | null, key: Key | null | null }
export interface TagUpdateArgs { id: number, name: string | null, color: string | null }

View File

@@ -8,6 +8,7 @@ use crate::{
};
use int_enum::IntEnum;
use prisma_client_rust::Direction;
use rspc::Type;
use serde::{Deserialize, Serialize};
use std::{
@@ -127,6 +128,7 @@ impl JobManager {
.db
.job()
.find_many(vec![job::status::not(JobStatus::Running.int_value())])
.order_by(job::date_created::order(Direction::Desc))
.exec()
.await?;

View File

@@ -1,3 +1,7 @@
# Nodes
Nodes are instances of the Spacedrive core running on a device, they are able to connect to each other via a peer-to-peer network. A node is able to run many libraries simultaneously, but must be authorized per-library in order to synchronize.
p2p, connecting nodes, protocols.

View File

@@ -2,6 +2,6 @@
Spacedrive generates compressed preview media for images, videos and text files.
Preview media is stored in the Node's data folder in a single directory. Images are stored as WEBP format with their CAS id as the name.
ffmpeg, syncing, security

View File

@@ -24,6 +24,7 @@
"@radix-ui/react-icons": "^1.1.1",
"@radix-ui/react-progress": "^0.1.4",
"@radix-ui/react-slider": "^0.1.4",
"@radix-ui/react-tabs": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
"@sd/assets": "workspace:*",
"@sd/client": "workspace:*",
@@ -38,6 +39,7 @@
"autoprefixer": "^10.4.7",
"byte-size": "^8.1.0",
"clsx": "^1.2.1",
"date-fns": "^2.29.2",
"immer": "^9.0.15",
"jotai": "^1.7.6",
"lodash": "^4.17.21",

View File

@@ -0,0 +1,99 @@
import { CheckBadgeIcon, KeyIcon, RssIcon } from '@heroicons/react/24/outline';
import { EyeIcon, FolderIcon, PhotoIcon, XMarkIcon } from '@heroicons/react/24/outline';
import { useLibraryQuery } from '@sd/client';
import { JobReport } from '@sd/core';
import { Button } from '@sd/ui';
import clsx from 'clsx';
import { format, formatDistance, formatDistanceToNow, formatDuration } from 'date-fns';
import moment from 'moment';
import { ArrowsClockwise } from 'phosphor-react';
import React from 'react';
import { Tooltip } from '../tooltip/Tooltip';
interface JobNiceData {
name: string;
icon: React.FC<React.ComponentProps<'svg'>>;
}
const NiceData: Record<string, JobNiceData> = {
indexer: {
name: 'Location Indexer',
icon: FolderIcon
},
thumbnailer: {
name: 'Thumbnail Generator',
icon: PhotoIcon
},
file_identifier: {
name: 'File Identifier',
icon: EyeIcon
}
};
const StatusColors: Record<JobReport['status'], string> = {
Running: 'text-blue-500',
Failed: 'text-red-500',
Completed: 'text-green-500',
Queued: 'text-yellow-500',
Canceled: 'text-gray-500',
Paused: 'text-gray-500'
};
function elapsed(seconds: number) {
return new Date(seconds * 1000).toUTCString().match(/(\d\d:\d\d:\d\d)/)?.[0];
}
export function JobsManager() {
const jobs = useLibraryQuery(['jobs.getHistory']);
return (
<div className="h-full">
<div className="flex flex-row w-full h-10 bg-gray-500 border-b border-gray-700 bg-opacity-30"></div>
<div className="h-full mr-1 -mt-10 overflow-x-hidden custom-scroll inspector-scroll">
<div className="px-2 py-4">
{jobs.data?.map((job) => {
const color = StatusColors[job.status];
const niceData = NiceData[job.name];
if (job.metadata) console.log({ job });
return (
<div
className="flex items-center px-2 py-2 border-b border-gray-700 rounded bg-opacity-60"
key={job.id}
>
<Tooltip label={job.status}>
<niceData.icon className={clsx('w-5 mr-3', color)} />
</Tooltip>
<div className="flex flex-col">
<span className="flex mt-0.5 items-center font-semibold">{niceData.name}</span>
<div className="flex items-center">
<span className="text-xs opacity-60">
{job.status === 'Failed' ? 'Failed after' : 'Took'}{' '}
{job.seconds_elapsed
? formatDuration({ seconds: job.seconds_elapsed })
: 'less than a second'}
</span>
<span className="mx-1 opacity-30">&#8226;</span>
<span className="text-xs opacity-60">
{formatDistanceToNow(new Date(job.date_created))} ago
</span>
</div>
<span className="text-xs opacity-60">{job.data}</span>
</div>
<div className="flex-grow" />
<div className="flex space-x-2">
<Button className="!p-0 w-7 h-7 flex items-center" variant="gray">
<ArrowsClockwise className="w-4" />
</Button>
<Button className="!p-0 w-7 h-7 flex items-center" variant="gray">
<XMarkIcon className="w-4" />
</Button>
</div>
</div>
);
})}
</div>
</div>
</div>
);
}

View File

@@ -19,7 +19,7 @@ const MiddleTruncatedText = ({
const endWidth = fontFaceScaleFactor * 4;
return (
<div className="whitespace-nowrap overflow-hidden w-full">
<div className="w-full overflow-hidden whitespace-nowrap">
<span
{...props}
style={{
@@ -66,7 +66,10 @@ export default function RunningJobsWidget() {
leaveFrom="translate-y-0"
leaveTo="translate-y-24"
>
<div key={job.id} className="flex flex-col px-2 pt-1.5 pb-2 bg-gray-700 rounded">
<div
key={job.id}
className="flex flex-col px-2 pt-1.5 pb-2 border bg-gray-600 bg-opacity-50 border-gray-500 rounded"
>
{/* <span className="mb-0.5 text-tiny font-bold text-gray-400">{job.status} Job</span> */}
<MiddleTruncatedText className="mb-1.5 text-gray-450 text-tiny">
{job.message}

View File

@@ -9,15 +9,16 @@ import {
useLibraryQuery
} from '@sd/client';
import { LocationCreateArgs } from '@sd/core';
import { Button, Dropdown } from '@sd/ui';
import { Button, Dropdown, OverlayPanel } from '@sd/ui';
import clsx from 'clsx';
import { CirclesFour, Planet, WaveTriangle } from 'phosphor-react';
import { CheckCircle, CirclesFour, Planet, WaveTriangle } from 'phosphor-react';
import React, { useContext, useEffect } from 'react';
import { NavLink, NavLinkProps, useNavigate } from 'react-router-dom';
import { useSnapshot } from 'valtio';
import CreateLibraryDialog from '../dialog/CreateLibraryDialog';
import { Folder } from '../icons/Folder';
import { JobsManager } from '../jobs/JobManager';
import RunningJobsWidget from '../jobs/RunningJobsWidget';
import { MacTrafficLights } from '../os/TrafficLights';
import { DefaultProps } from '../primitive/types';
@@ -261,7 +262,7 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
)}
<div className="flex-grow" />
<RunningJobsWidget />
<div className="mb-2">
<div className="mt-2 mb-2">
<NavLink to="/settings/general">
{({ isActive }) => (
<Button
@@ -273,6 +274,18 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
</Button>
)}
</NavLink>
<OverlayPanel
className="focus:outline-none"
trigger={
<Button noPadding className={clsx('px-[4px] !outline-none')}>
<CheckCircle className="w-5 h-5" />
</Button>
}
>
<div className="block w-[500px] h-96">
<JobsManager />
</div>
</OverlayPanel>
</div>
</div>
);

View File

@@ -1,14 +1,25 @@
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
import React from 'react';
export const Tooltip = ({ children, label }: { children: React.ReactNode; label: string }) => {
export const Tooltip = ({
children,
label,
position = 'bottom'
}: {
children: React.ReactNode;
label: string;
position?: 'top' | 'right' | 'bottom' | 'left';
}) => {
return (
<TooltipPrimitive.Provider>
<TooltipPrimitive.Root>
<TooltipPrimitive.Trigger asChild>
<span>{children}</span>
</TooltipPrimitive.Trigger>
<TooltipPrimitive.Content className="text-sm rounded px-2 py-1 mb-[2px] bg-gray-300 dark:!bg-gray-500 dark:text-gray-100">
<TooltipPrimitive.Content
side={position}
className="text-sm rounded px-2 py-1 mb-[2px] bg-gray-300 dark:!bg-gray-500 dark:text-gray-100"
>
<TooltipPrimitive.Arrow className="fill-gray-300 dark:!fill-gray-500" />
{label}
</TooltipPrimitive.Content>

View File

@@ -20,6 +20,7 @@
"@headlessui/react": "^1.6.6",
"@heroicons/react": "^2.0.10",
"@radix-ui/react-context-menu": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@tailwindcss/forms": "^0.5.2",
"class-variance-authority": "^0.2.3",
"clsx": "^1.2.1",

View File

@@ -26,7 +26,7 @@ export const ContextMenu = ({
}: PropsWithChildren<Props>) => {
return (
<RadixCM.Root>
<RadixCM.Trigger>{trigger}</RadixCM.Trigger>
<RadixCM.Trigger asChild>{trigger}</RadixCM.Trigger>
<RadixCM.Portal>
<RadixCM.Content {...props} className={clsx(MENU_CLASSES, className)}>
{children}

View File

@@ -0,0 +1,37 @@
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { VariantProps, cva } from 'class-variance-authority';
import clsx from 'clsx';
import { Icon } from 'phosphor-react';
import React, { PropsWithChildren } from 'react';
interface Props extends DropdownMenu.MenuContentProps {
trigger: React.ReactNode;
}
const MENU_CLASSES = `
flex flex-col
min-w-[11rem] m-2 space-y-1
text-left text-sm dark:text-gray-100 text-gray-800
bg-gray-50 border-gray-200 dark:bg-gray-600
border border-gray-300 dark:border-gray-500
shadow-2xl shadow-gray-300 dark:shadow-gray-950
select-none cursor-default rounded-lg
`;
export const OverlayPanel = ({
trigger,
children,
className,
...props
}: PropsWithChildren<Props>) => {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>{trigger}</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content {...props} className={clsx(MENU_CLASSES, className)}>
{children}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
};

View File

@@ -1,4 +1,5 @@
export * from './Button';
export * from './Dropdown';
export * as ContextMenu from './ContextMenu';
export * from './OverlayPanel';
export * from './Input';

BIN
pnpm-lock.yaml generated
View File

Binary file not shown.