datasource db { provider = "sqlite" url = "file:dev.db" } generator client { provider = "cargo prisma" output = "../../crates/prisma/src/prisma" module_path = "prisma" client_format = "folder" } generator sync { provider = "cargo prisma-sync" output = "../../crates/prisma/src/prisma_sync" client_format = "folder" } /// @local model CRDTOperation { id Int @id @default(autoincrement()) timestamp BigInt model Int record_id Bytes // Enum: ?? kind String data Bytes instance_id Int instance Instance @relation(fields: [instance_id], references: [id]) @@map("crdt_operation") } /// @local model CloudCRDTOperation { id Int @id @default(autoincrement()) timestamp BigInt model Int record_id Bytes // Enum: ?? kind String data Bytes instance_id Int instance Instance @relation(fields: [instance_id], references: [id]) @@map("cloud_crdt_operation") } /// @deprecated: This model has to exist solely for backwards compatibility. /// @local model Node { id Int @id @default(autoincrement()) pub_id Bytes @unique name String // Enum: sd_core::node::Platform platform Int date_created DateTime identity Bytes? // TODO: Change to required field in future @@map("node") } // represents a single `.db` file (SQLite DB) that is paired to the current library. // A `LibraryInstance` is always owned by a single `Node` but it's possible for that node to change (or two to be owned by a single node). /// @local(id: pub_id) model Instance { id Int @id @default(autoincrement()) // This is is NOT globally unique pub_id Bytes @unique // This UUID is meaningless and exists soley cause the `uhlc::ID` must be 16-bit. Really this should be derived from the `identity` field. // Enum: sd_p2p::Identity (or sd_core::p2p::IdentityOrRemoteIdentity in early versions) identity Bytes? // Enum: sd_core::node::RemoteIdentity remote_identity Bytes // Enum: uuid::Uuid node_id Bytes // Enum: sd_core::node::RemoteIdentity node_remote_identity Bytes? // TODO: This should not be optional metadata Bytes? // TODO: This should not be optional last_seen DateTime // Time core started for owner, last P2P message for P2P node date_created DateTime // clock timestamp for sync timestamp BigInt? locations Location[] CRDTOperation CRDTOperation[] CloudCRDTOperation CloudCRDTOperation[] storage_statistics StorageStatistics? @@map("instance") } /// @local model Statistics { id Int @id @default(autoincrement()) date_captured DateTime @default(now()) total_object_count Int @default(0) library_db_size String @default("0") // local calulations total_local_bytes_used String @default("0") total_local_bytes_capacity String @default("0") total_local_bytes_free String @default("0") // library calculations total_library_bytes String @default("0") total_library_unique_bytes String @default("0") total_library_preview_media_bytes String @default("0") @@map("statistics") } /// @local model ObjectKindStatistics { kind Int @id total_bytes BigInt @default(0) files_count BigInt @default(0) @@map("object_kind_statistics") } /// @local model Volume { id Int @id @default(autoincrement()) name String mount_point String total_bytes_capacity String @default("0") total_bytes_available String @default("0") disk_type String? filesystem String? is_system Boolean @default(false) date_modified DateTime @default(now()) @@unique([mount_point, name]) @@map("volume") } /// @shared(id: pub_id, modelId: 1) model Location { id Int @id @default(autoincrement()) pub_id Bytes @unique name String? path String? total_capacity Int? available_capacity Int? size_in_bytes Bytes? is_archived Boolean? generate_preview_media Boolean? sync_preview_media Boolean? hidden Boolean? date_created DateTime? scan_state Int @default(0) // Enum: sd_core::location::ScanState // this should just be a local-only cache but it's too much effort to broadcast online locations rn (@brendan) instance_id Int? instance Instance? @relation(fields: [instance_id], references: [id], onDelete: SetNull) file_paths FilePath[] indexer_rules IndexerRulesInLocation[] @@map("location") } /// @shared(id: pub_id, modelId: 2) model FilePath { id Int @id @default(autoincrement()) pub_id Bytes @unique is_dir Boolean? // content addressable storage id - blake3 sampled checksum cas_id String? // full byte contents digested into blake3 checksum integrity_checksum String? // location that owns this path location_id Int? location Location? @relation(fields: [location_id], references: [id], onDelete: SetNull) // the path of the file relative to its location materialized_path String? // the name and extension, MUST have 'COLLATE NOCASE' in migration name String? extension String? hidden Boolean? size_in_bytes String? // deprecated size_in_bytes_bytes Bytes? inode Bytes? // This is actually an unsigned 64 bit integer, but we don't have this type in SQLite // the unique Object for this file path object_id Int? object Object? @relation(fields: [object_id], references: [id], onDelete: SetNull) key_id Int? // replacement for encryption // permissions String? date_created DateTime? date_modified DateTime? date_indexed DateTime? // key Key? @relation(fields: [key_id], references: [id]) @@unique([location_id, materialized_path, name, extension]) @@unique([location_id, inode]) @@index([location_id]) @@index([location_id, materialized_path]) @@map("file_path") } /// @shared(id: pub_id, modelId: 3) model Object { id Int @id @default(autoincrement()) pub_id Bytes @unique // Enum: sd_file_ext::kind::ObjectKind kind Int? key_id Int? // handy ways to mark an object hidden Boolean? favorite Boolean? important Boolean? // if we have generated preview media for this object on at least one Node // commented out for now by @brendonovich since they they're irrelevant to the sync system // has_thumbnail Boolean? // has_thumbstrip Boolean? // has_video_preview Boolean? // TODO: change above to: // has_generated_thumbnail Boolean @default(false) // has_generated_thumbstrip Boolean @default(false) // has_generated_video_preview Boolean @default(false) // integration with ipfs // ipfs_id String? // plain text note note String? // the original known creation date of this object date_created DateTime? date_accessed DateTime? tags TagOnObject[] labels LabelOnObject[] albums ObjectInAlbum[] spaces ObjectInSpace[] file_paths FilePath[] // comments Comment[] exif_data ExifData? ffmpeg_data FfmpegData? // key Key? @relation(fields: [key_id], references: [id]) @@map("object") } // // keys allow us to know exactly which files can be decrypted with a given key // // they can be "mounted" to a client, and then used to decrypt files automatically // /// @shared(id: uuid) // model Key { // id Int @id @default(autoincrement()) // // uuid to identify the key // uuid String @unique // version String // key_type String // // the name that the user sets // name String? // // is this key the default for encryption? // // was not tagged as unique as i'm not too sure if PCR will handle it // // can always be tagged as unique, the keys API will need updating to use `find_unique()` // default Boolean @default(false) // // nullable if concealed for security // date_created DateTime? @default(now()) // // encryption algorithm used to encrypt the key // algorithm String // // hashing algorithm used for hashing the key with the content salt // hashing_algorithm String // // salt used for encrypting data with this key // content_salt Bytes // // the *encrypted* master key (48 bytes) // master_key Bytes // // the nonce used for encrypting the master key // master_key_nonce Bytes // // the nonce used for encrypting the key // key_nonce Bytes // // the *encrypted* key // key Bytes // // the salt used for deriving the KEK (used for encrypting the master key) from the root key // salt Bytes // automount Boolean @default(false) // objects Object[] // file_paths FilePath[] // @@map("key") // } /// @shared(id: object, modelId: 4) model ExifData { id Int @id @default(autoincrement()) resolution Bytes? media_date Bytes? media_location Bytes? camera_data Bytes? artist String? description String? copyright String? exif_version String? // purely for sorting/ordering, never sent to the frontend as they'd be useless // these are also usually one-way, and not reversible // (e.g. we can't get `MediaDate::Utc(2023-09-26T22:04:37+01:00)` from `1695758677` as we don't store the TZ) epoch_time BigInt? // time since unix epoch object_id Int @unique object Object @relation(fields: [object_id], references: [id], onDelete: Cascade) @@map("exif_data") } model FfmpegData { id Int @id @default(autoincrement()) // Internal FFmpeg properties formats String bit_rate Bytes // Actually a i64 in the backend duration Bytes? // Actually a i64 in the backend start_time Bytes? // Actually a i64 in the backend chapters FfmpegMediaChapter[] programs FfmpegMediaProgram[] // Metadata for search title String? creation_time DateTime? date DateTime? album_artist String? disc String? track String? album String? artist String? metadata Bytes? object Object @relation(fields: [object_id], references: [id], onDelete: Cascade) object_id Int @unique @@map("ffmpeg_data") } model FfmpegMediaChapter { chapter_id Int start Bytes // Actually a i64 in the backend end Bytes // Actually a i64 in the backend time_base_den Int time_base_num Int // Metadata for search title String? metadata Bytes? ffmpeg_data FfmpegData @relation(fields: [ffmpeg_data_id], references: [id], onDelete: Cascade) ffmpeg_data_id Int @@id(name: "likeId", [ffmpeg_data_id, chapter_id]) @@map("ffmpeg_media_chapter") } model FfmpegMediaProgram { program_id Int streams FfmpegMediaStream[] // Metadata for search name String? metadata Bytes? ffmpeg_data FfmpegData @relation(fields: [ffmpeg_data_id], references: [id], onDelete: Cascade) ffmpeg_data_id Int @@id(name: "likeId", [ffmpeg_data_id, program_id]) @@map("ffmpeg_media_program") } model FfmpegMediaStream { stream_id Int name String? codec FfmpegMediaCodec? aspect_ratio_num Int aspect_ratio_den Int frames_per_second_num Int frames_per_second_den Int time_base_real_den Int time_base_real_num Int dispositions String? // Metadata for search title String? encoder String? language String? duration Bytes? // Actually a i64 in the backend metadata Bytes? program FfmpegMediaProgram @relation(fields: [ffmpeg_data_id, program_id], references: [ffmpeg_data_id, program_id], onDelete: Cascade) program_id Int ffmpeg_data_id Int @@id(name: "likeId", [ffmpeg_data_id, program_id, stream_id]) @@map("ffmpeg_media_stream") } model FfmpegMediaCodec { id Int @id @default(autoincrement()) kind String? sub_kind String? tag String? name String? profile String? bit_rate Int video_props FfmpegMediaVideoProps? audio_props FfmpegMediaAudioProps? stream FfmpegMediaStream @relation(fields: [ffmpeg_data_id, program_id, stream_id], references: [ffmpeg_data_id, program_id, stream_id], onDelete: Cascade) stream_id Int program_id Int ffmpeg_data_id Int @@unique([ffmpeg_data_id, program_id, stream_id]) @@map("ffmpeg_media_codec") } model FfmpegMediaVideoProps { id Int @id @default(autoincrement()) pixel_format String? color_range String? bits_per_channel Int? color_space String? color_primaries String? color_transfer String? field_order String? chroma_location String? width Int height Int aspect_ratio_num Int? aspect_ratio_Den Int? properties String? codec FfmpegMediaCodec @relation(fields: [codec_id], references: [id], onDelete: Cascade) codec_id Int @unique @@map("ffmpeg_media_video_props") } model FfmpegMediaAudioProps { id Int @id @default(autoincrement()) delay Int padding Int sample_rate Int? sample_format String? bit_per_sample Int? channel_layout String? codec FfmpegMediaCodec @relation(fields: [codec_id], references: [id], onDelete: Cascade) codec_id Int @unique @@map("ffmpeg_media_audio_props") } //// Tag //// /// @shared(id: pub_id, modelId: 5) model Tag { id Int @id @default(autoincrement()) pub_id Bytes @unique name String? color String? is_hidden Boolean? // user hidden entire tag date_created DateTime? date_modified DateTime? tag_objects TagOnObject[] @@map("tag") } /// @relation(item: object, group: tag, modelId: 6) model TagOnObject { object_id Int object Object @relation(fields: [object_id], references: [id], onDelete: Restrict) tag_id Int tag Tag @relation(fields: [tag_id], references: [id], onDelete: Restrict) date_created DateTime? @@id([tag_id, object_id]) @@map("tag_on_object") } //// Label //// /// @shared(id: name, modelId: 7) model Label { id Int @id @default(autoincrement()) name String @unique date_created DateTime? date_modified DateTime? label_objects LabelOnObject[] @@map("label") } /// @relation(item: object, group: label, modelId: 8) model LabelOnObject { date_created DateTime @default(now()) object_id Int object Object @relation(fields: [object_id], references: [id], onDelete: Restrict) label_id Int label Label @relation(fields: [label_id], references: [id], onDelete: Restrict) @@id([label_id, object_id]) @@map("label_on_object") } //// Space //// model Space { id Int @id @default(autoincrement()) pub_id Bytes @unique name String? description String? date_created DateTime? date_modified DateTime? objects ObjectInSpace[] @@map("space") } model ObjectInSpace { space_id Int space Space @relation(fields: [space_id], references: [id], onDelete: Restrict) object_id Int object Object @relation(fields: [object_id], references: [id], onDelete: Restrict) @@id([space_id, object_id]) @@map("object_in_space") } //// StorageStatistics //// /// @shared(id: pub_id, modelId: 11) model StorageStatistics { id Int @id @default(autoincrement()) pub_id Bytes @unique total_capacity BigInt @default(0) available_capacity BigInt @default(0) instance_pub_id Bytes? @unique instance Instance? @relation(fields: [instance_pub_id], references: [pub_id], onDelete: Cascade) @@map("storage_statistics") } //// Job //// model Job { id Bytes @id name String? action String? // Will be composed of "{action_description}(-{children_order})*" // Enum: sd_core::job::job_manager:JobStatus status Int? // 0 = Queued // List of errors, separated by "\n\n" in case of failed jobs or completed with errors errors_text String? // Deprecated, use `critical_error` or `non_critical_errors` instead critical_error String? // Serialized error field with info about the failed job after completion non_critical_errors Bytes? // Serialized non-critical errors field with info about the completed job with errors after completion data Bytes? // Deprecated metadata Bytes? // Serialized metadata field with info about the job after completion parent_id Bytes? task_count Int? completed_task_count Int? date_estimated_completion DateTime? // Estimated timestamp that the job will be complete at date_created DateTime? date_started DateTime? // Started execution date_completed DateTime? // Finished execution parent Job? @relation("jobs_dependency", fields: [parent_id], references: [id], onDelete: SetNull) children Job[] @relation("jobs_dependency") @@map("job") } //// Album //// model Album { id Int @id pub_id Bytes @unique name String? is_hidden Boolean? date_created DateTime? date_modified DateTime? objects ObjectInAlbum[] @@map("album") } model ObjectInAlbum { date_created DateTime? album_id Int album Album @relation(fields: [album_id], references: [id], onDelete: NoAction) object_id Int object Object @relation(fields: [object_id], references: [id], onDelete: NoAction) @@id([album_id, object_id]) @@map("object_in_album") } //// Indexer Rules //// model IndexerRule { id Int @id @default(autoincrement()) pub_id Bytes @unique name String? @unique default Boolean? rules_per_kind Bytes? date_created DateTime? date_modified DateTime? locations IndexerRulesInLocation[] @@map("indexer_rule") } model IndexerRulesInLocation { location_id Int location Location @relation(fields: [location_id], references: [id], onDelete: Restrict) indexer_rule_id Int indexer_rule IndexerRule @relation(fields: [indexer_rule_id], references: [id], onDelete: Restrict) @@id([location_id, indexer_rule_id]) @@map("indexer_rule_in_location") } /// @shared(id: key, modelId: 9) model Preference { key String @id value Bytes? @@map("preference") } model Notification { id Int @id @default(autoincrement()) read Boolean @default(false) // Enum: crate::api::notifications::NotificationData data Bytes expires_at DateTime? @@map("notification") } /// @shared(id: pub_id, modelId: 10) model SavedSearch { id Int @id @default(autoincrement()) pub_id Bytes @unique // enum: crate::api::search::saved::SearchTarget target String? search String? filters String? name String? icon String? description String? // order Int? // Add this line to include ordering date_created DateTime? date_modified DateTime? @@map("saved_search") }