velody/backend/prisma/schema.prisma
2026-05-31 01:20:56 +02:00

193 lines
7.3 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid()) @db.Uuid
slug String @unique
displayName String @map("display_name")
isDefault Boolean @default(false) @map("is_default")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
devices Device[]
tracks Track[]
audioAssets AudioAsset[]
artworkAssets ArtworkAsset[]
uploadSessions UploadSession[]
libraryEvents LibraryEvent[]
@@map("users")
}
model Device {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid @map("user_id")
platform DevicePlatform
deviceName String @map("device_name")
appVersion String @map("app_version")
installTokenHash String @map("install_token_hash")
lastSeenAt DateTime @map("last_seen_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
uploadSessions UploadSession[]
syncCursor DeviceSyncCursor?
audioAssets AudioAsset[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@index([userId])
@@map("devices")
}
model Track {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid @map("user_id")
primaryAudioAssetId String? @unique @db.Uuid @map("primary_audio_asset_id")
artworkAssetId String? @db.Uuid @map("artwork_asset_id")
title String
artist String
album String?
albumArtist String? @map("album_artist")
genre String?
discNumber Int? @map("disc_number")
trackNumber Int? @map("track_number")
year Int?
durationMs Int? @map("duration_ms")
status TrackStatus @default(ACTIVE)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
primaryAudioAsset AudioAsset? @relation("PrimaryAudioAsset", fields: [primaryAudioAssetId], references: [id], onDelete: SetNull, onUpdate: Cascade)
artworkAsset ArtworkAsset? @relation("TrackArtwork", fields: [artworkAssetId], references: [id], onDelete: SetNull, onUpdate: Cascade)
audioAssets AudioAsset[] @relation("TrackAudioAssets")
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@index([userId])
@@map("tracks")
}
model AudioAsset {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid @map("user_id")
trackId String? @db.Uuid @map("track_id")
sha256 String
storageKey String @unique @map("storage_key")
originalFilename String @map("original_filename")
mimeType String @map("mime_type")
fileExtension String @map("file_extension")
fileSizeBytes BigInt @map("file_size_bytes")
bitRateKbps Int? @map("bit_rate_kbps")
sampleRateHz Int? @map("sample_rate_hz")
channels Int?
durationMs Int? @map("duration_ms")
sourceDeviceId String? @db.Uuid @map("source_device_id")
createdAt DateTime @default(now()) @map("created_at")
track Track? @relation("TrackAudioAssets", fields: [trackId], references: [id], onDelete: SetNull, onUpdate: Cascade)
primaryForTrack Track? @relation("PrimaryAudioAsset")
sourceDevice Device? @relation(fields: [sourceDeviceId], references: [id], onDelete: SetNull, onUpdate: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@unique([userId, sha256])
@@index([userId])
@@map("audio_assets")
}
model ArtworkAsset {
userId String? @db.Uuid @map("user_id")
id String @id @default(uuid()) @db.Uuid
sha256 String
storageKey String @unique @map("storage_key")
mimeType String @map("mime_type")
width Int?
height Int?
fileSizeBytes BigInt @map("file_size_bytes")
createdAt DateTime @default(now()) @map("created_at")
tracks Track[] @relation("TrackArtwork")
user User? @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@unique([userId, sha256])
@@index([userId])
@@map("artwork_assets")
}
model UploadSession {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid @map("user_id")
deviceId String @db.Uuid @map("device_id")
trackId String? @db.Uuid @map("track_id")
audioAssetId String? @db.Uuid @map("audio_asset_id")
expectedSha256 String @map("expected_sha256")
originalFilename String @map("original_filename")
expectedSizeBytes BigInt @map("expected_size_bytes")
receivedBytes BigInt @default(0) @map("received_bytes")
tempStoragePath String @map("temp_storage_path")
status UploadSessionStatus @default(PENDING)
completedAt DateTime? @map("completed_at")
finalizedAt DateTime? @map("finalized_at")
expiresAt DateTime @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade, onUpdate: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@index([userId])
@@map("upload_sessions")
}
model LibraryEvent {
id BigInt @id @default(autoincrement())
userId String @db.Uuid @map("user_id")
entityType EntityType @map("entity_type")
entityId String @db.Uuid @map("entity_id")
action EventAction
payloadVersion Int @default(1) @map("payload_version")
createdAt DateTime @default(now()) @map("created_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@index([userId])
@@map("library_events")
}
model DeviceSyncCursor {
deviceId String @id @db.Uuid @map("device_id")
lastEventId BigInt @default(0) @map("last_event_id")
lastFullSyncAt DateTime? @map("last_full_sync_at")
updatedAt DateTime @updatedAt @map("updated_at")
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade, onUpdate: Cascade)
@@map("device_sync_cursors")
}
enum DevicePlatform {
MACOS
IPHONE
}
enum TrackStatus {
ACTIVE
DELETED
}
enum UploadSessionStatus {
PENDING
READY_TO_UPLOAD
COMPLETED
FAILED
}
enum EntityType {
TRACK
AUDIO_ASSET
ARTWORK_ASSET
}
enum EventAction {
CREATED
UPDATED
DELETED
}