Legacy 6h configs migrate to watch + nightly 03:30; master pushes schedule via HELLO_ACK/CONFIG_UPDATE. Co-authored-by: Cursor <cursoragent@cursor.com>
285 lines
8.9 KiB
Text
285 lines
8.9 KiB
Text
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum NodeStatus {
|
|
ONLINE
|
|
OFFLINE
|
|
DEGRADED
|
|
REVOKED
|
|
UPDATING
|
|
SCANNING
|
|
ERROR
|
|
}
|
|
|
|
enum PlaybackSessionStatus {
|
|
ACTIVE
|
|
IDLE
|
|
EXPIRED
|
|
REVOKED
|
|
COMPLETED
|
|
}
|
|
|
|
enum LocationType {
|
|
LOCAL
|
|
REMOTE
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
email String @unique
|
|
passwordHash String @map("password_hash")
|
|
name String?
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
addonTokens AddonToken[]
|
|
sessions AdminSession[]
|
|
|
|
@@map("users")
|
|
}
|
|
|
|
model AdminSession {
|
|
id String @id @default(uuid())
|
|
userId String @map("user_id")
|
|
tokenHash String @unique @map("token_hash")
|
|
expiresAt DateTime @map("expires_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("admin_sessions")
|
|
}
|
|
|
|
model AddonToken {
|
|
id String @id @default(uuid())
|
|
userId String @map("user_id")
|
|
tokenHash String @unique @map("token_hash")
|
|
label String?
|
|
revoked Boolean @default(false)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("addon_tokens")
|
|
}
|
|
|
|
model Location {
|
|
id String @id @default(uuid())
|
|
name String @unique
|
|
description String?
|
|
priority Int @default(0)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
nodes Node[]
|
|
|
|
@@map("locations")
|
|
}
|
|
|
|
model Node {
|
|
id String @id @default(uuid())
|
|
name String
|
|
hostname String?
|
|
locationId String? @map("location_id")
|
|
locationType LocationType @default(LOCAL) @map("location_type")
|
|
status NodeStatus @default(OFFLINE)
|
|
version String?
|
|
architecture String?
|
|
publicStreamUrl String @map("public_stream_url")
|
|
lastSeen DateTime? @map("last_seen")
|
|
totalStorage BigInt @default(0) @map("total_storage")
|
|
freeStorage BigInt @default(0) @map("free_storage")
|
|
activeStreams Int @default(0) @map("active_streams")
|
|
currentBandwidth BigInt @default(0) @map("current_bandwidth")
|
|
libraryFileCount Int @default(0) @map("library_file_count")
|
|
lastRevision Int @default(0) @map("last_revision")
|
|
/** Node scanner: "off" or duration like "24h" / "6h" */
|
|
fullScanInterval String @default("off") @map("full_scan_interval")
|
|
/** Nightly catch-up time HH:MM in node local TZ */
|
|
fullScanAt String @default("03:30") @map("full_scan_at")
|
|
revoked Boolean @default(false)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
location Location? @relation(fields: [locationId], references: [id])
|
|
credentials NodeCredential?
|
|
mediaFiles MediaFile[]
|
|
playbackSessions PlaybackSession[]
|
|
|
|
@@map("nodes")
|
|
}
|
|
|
|
model NodeCredential {
|
|
id String @id @default(uuid())
|
|
nodeId String @unique @map("node_id")
|
|
apiKeyHash String @map("api_key_hash")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
rotatedAt DateTime? @map("rotated_at")
|
|
|
|
node Node @relation(fields: [nodeId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("node_credentials")
|
|
}
|
|
|
|
model NodeEnrollmentToken {
|
|
id String @id @default(uuid())
|
|
tokenHash String @unique @map("token_hash")
|
|
name String?
|
|
locationId String? @map("location_id")
|
|
expiresAt DateTime @map("expires_at")
|
|
usedAt DateTime? @map("used_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@map("node_enrollment_tokens")
|
|
}
|
|
|
|
model Movie {
|
|
id String @id @default(uuid())
|
|
title String
|
|
originalTitle String? @map("original_title")
|
|
year Int?
|
|
overview String?
|
|
posterUrl String? @map("poster_url")
|
|
backdropUrl String? @map("backdrop_url")
|
|
genres String[] @default([])
|
|
runtime Int?
|
|
imdbId String? @unique @map("imdb_id")
|
|
tmdbId Int? @unique @map("tmdb_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
mediaFiles MediaFile[]
|
|
matches LibraryMatch[]
|
|
|
|
@@map("movies")
|
|
}
|
|
|
|
model Series {
|
|
id String @id @default(uuid())
|
|
title String
|
|
year Int?
|
|
overview String?
|
|
posterUrl String? @map("poster_url")
|
|
backdropUrl String? @map("backdrop_url")
|
|
genres String[] @default([])
|
|
imdbId String? @unique @map("imdb_id")
|
|
tmdbId Int? @unique @map("tmdb_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
seasons Season[]
|
|
episodes Episode[]
|
|
matches LibraryMatch[]
|
|
|
|
@@map("series")
|
|
}
|
|
|
|
model Season {
|
|
id String @id @default(uuid())
|
|
seriesId String @map("series_id")
|
|
seasonNumber Int @map("season_number")
|
|
|
|
series Series @relation(fields: [seriesId], references: [id], onDelete: Cascade)
|
|
episodes Episode[]
|
|
|
|
@@unique([seriesId, seasonNumber])
|
|
@@map("seasons")
|
|
}
|
|
|
|
model Episode {
|
|
id String @id @default(uuid())
|
|
seriesId String @map("series_id")
|
|
seasonId String @map("season_id")
|
|
seasonNumber Int @map("season_number")
|
|
episodeNumber Int @map("episode_number")
|
|
title String?
|
|
overview String?
|
|
stillUrl String? @map("still_url")
|
|
runtime Int?
|
|
airDate DateTime? @map("air_date")
|
|
imdbId String? @map("imdb_id")
|
|
tmdbId Int? @map("tmdb_id")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
series Series @relation(fields: [seriesId], references: [id], onDelete: Cascade)
|
|
season Season @relation(fields: [seasonId], references: [id], onDelete: Cascade)
|
|
mediaFiles MediaFile[]
|
|
matches LibraryMatch[]
|
|
|
|
@@unique([seriesId, seasonNumber, episodeNumber])
|
|
@@map("episodes")
|
|
}
|
|
|
|
model MediaFile {
|
|
id String @id @default(uuid())
|
|
nodeId String @map("node_id")
|
|
movieId String? @map("movie_id")
|
|
episodeId String? @map("episode_id")
|
|
localFileId String @map("local_file_id")
|
|
sizeBytes BigInt @map("size_bytes")
|
|
container String?
|
|
resolution String?
|
|
videoCodec String? @map("video_codec")
|
|
videoBitrate Int? @map("video_bitrate")
|
|
hdrType String? @map("hdr_type")
|
|
audioCodec String? @map("audio_codec")
|
|
audioChannels String? @map("audio_channels")
|
|
audioLanguage String? @map("audio_language")
|
|
releaseName String @map("release_name")
|
|
available Boolean @default(true)
|
|
lastSeen DateTime @default(now()) @map("last_seen")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
node Node @relation(fields: [nodeId], references: [id], onDelete: Cascade)
|
|
movie Movie? @relation(fields: [movieId], references: [id])
|
|
episode Episode? @relation(fields: [episodeId], references: [id])
|
|
playbackSessions PlaybackSession[]
|
|
|
|
@@unique([nodeId, localFileId])
|
|
@@map("media_files")
|
|
}
|
|
|
|
model LibraryMatch {
|
|
id String @id @default(uuid())
|
|
movieId String? @map("movie_id")
|
|
seriesId String? @map("series_id")
|
|
episodeId String? @map("episode_id")
|
|
releaseName String @map("release_name")
|
|
ignored Boolean @default(false)
|
|
manual Boolean @default(false)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
movie Movie? @relation(fields: [movieId], references: [id])
|
|
series Series? @relation(fields: [seriesId], references: [id])
|
|
episode Episode? @relation(fields: [episodeId], references: [id])
|
|
|
|
@@map("library_matches")
|
|
}
|
|
|
|
model PlaybackSession {
|
|
id String @id @default(uuid())
|
|
tokenHash String @unique @map("token_hash")
|
|
userId String? @map("user_id")
|
|
nodeId String @map("node_id")
|
|
mediaFileId String @map("media_file_id")
|
|
status PlaybackSessionStatus @default(ACTIVE)
|
|
idleTimeoutSeconds Int @map("idle_timeout_seconds")
|
|
absoluteExpiresAt DateTime @map("absolute_expires_at")
|
|
lastActivity DateTime @default(now()) @map("last_activity")
|
|
revoked Boolean @default(false)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
node Node @relation(fields: [nodeId], references: [id])
|
|
mediaFile MediaFile @relation(fields: [mediaFileId], references: [id])
|
|
|
|
@@map("playback_sessions")
|
|
}
|