project-standalo-sonic-cloud/prisma/schema.prisma

355 lines
9.5 KiB
Plaintext

// Prisma schema for Sonic Cloud - Music Platform
// Models: User, Artist, Label, Genre, Album, Song, SongGenre, Playlist, PlaylistSong
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
// User model - base authentication and profile
model User {
id String @id @default(uuid())
email String @unique
passwordHash String
username String @unique
displayName String?
avatarUrl String?
bio String?
role String @default("user") // user, artist, label, admin
resetToken String?
resetExpires DateTime?
emailVerified Boolean @default(false)
emailToken String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
artist Artist?
label Label?
playlists Playlist[]
shares Share[]
refreshTokens RefreshToken[]
sessions Session[]
playHistory PlayHistory[]
queue Queue?
uploadSessions UploadSession[]
@@map("users")
}
// Artist model - for musicians who upload music
model Artist {
id String @id @default(uuid())
userId String @unique
name String
slug String @unique
bio String?
avatarUrl String?
bannerUrl String?
website String?
twitter String?
instagram String?
spotify String?
verified Boolean @default(false)
labelId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
label Label? @relation(fields: [labelId], references: [id])
songs Song[]
albums Album[]
invitations LabelInvitation[]
@@map("artists")
}
// Label model - record labels that manage artists
model Label {
id String @id @default(uuid())
userId String @unique
name String
slug String @unique
description String?
logoUrl String?
website String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
artists Artist[]
invitations LabelInvitation[]
@@map("labels")
}
// Genre model - music genres for categorization
model Genre {
id String @id @default(uuid())
name String @unique
slug String @unique
description String?
color String? // hex color for UI
createdAt DateTime @default(now())
// Relations
songs SongGenre[]
@@map("genres")
}
// Album model - collection of songs
model Album {
id String @id @default(uuid())
artistId String
title String
slug String
description String?
coverUrl String?
releaseDate DateTime?
albumType String @default("album") // album, single, ep
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
artist Artist @relation(fields: [artistId], references: [id], onDelete: Cascade)
songs Song[]
@@unique([artistId, slug])
@@map("albums")
}
// Song model - individual music tracks
model Song {
id String @id @default(uuid())
artistId String
albumId String?
title String
slug String
description String?
audioUrl String
coverUrl String?
duration Int // duration in seconds
waveformUrl String? // waveform visualization data
playCount Int @default(0)
isPublic Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
artist Artist @relation(fields: [artistId], references: [id], onDelete: Cascade)
album Album? @relation(fields: [albumId], references: [id], onDelete: SetNull)
genres SongGenre[]
playlists PlaylistSong[]
playHistory PlayHistory[]
@@unique([artistId, slug])
@@map("songs")
}
// SongGenre model - many-to-many relation between songs and genres
model SongGenre {
songId String
genreId String
song Song @relation(fields: [songId], references: [id], onDelete: Cascade)
genre Genre @relation(fields: [genreId], references: [id], onDelete: Cascade)
@@id([songId, genreId])
@@map("song_genres")
}
// Playlist model - user-created playlists
model Playlist {
id String @id @default(uuid())
userId String
title String
slug String
description String?
coverUrl String?
isPublic Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
songs PlaylistSong[]
@@unique([userId, slug])
@@map("playlists")
}
// PlaylistSong model - many-to-many relation with ordering
model PlaylistSong {
playlistId String
songId String
position Int
addedAt DateTime @default(now())
playlist Playlist @relation(fields: [playlistId], references: [id], onDelete: Cascade)
song Song @relation(fields: [songId], references: [id], onDelete: Cascade)
@@id([playlistId, songId])
@@map("playlist_songs")
}
// Label invitation model - invitations from labels to artists
model LabelInvitation {
id String @id @default(uuid())
labelId String
artistId String
status String @default("pending") // pending, accepted, declined, expired
message String?
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
label Label @relation(fields: [labelId], references: [id], onDelete: Cascade)
artist Artist @relation(fields: [artistId], references: [id], onDelete: Cascade)
@@unique([labelId, artistId])
@@index([artistId, status])
@@map("label_invitations")
}
// Share model - tracks shared content links
model Share {
id String @id @default(cuid())
type String // SONG, PLAYLIST, or ALBUM
targetId String
token String @unique
userId String?
platform String?
clickCount Int @default(0)
createdAt DateTime @default(now())
// Relations
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
@@index([token])
@@index([targetId, type])
@@map("shares")
}
// RefreshToken model - for JWT refresh token rotation
model RefreshToken {
id String @id @default(uuid())
token String @unique
userId String
expiresAt DateTime
isRevoked Boolean @default(false)
createdAt DateTime @default(now())
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([token])
@@index([userId])
@@map("refresh_tokens")
}
// Session model - for tracking active user sessions
model Session {
id String @id @default(uuid())
userId String
token String @unique
deviceInfo String? // JSON string
ipAddress String?
userAgent String?
lastActivity DateTime?
createdAt DateTime @default(now())
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([token])
@@index([userId])
@@map("sessions")
}
// PlayHistory model - for tracking user playback history
model PlayHistory {
id String @id @default(uuid())
userId String
songId String
playedAt DateTime @default(now())
playedDuration Int? // seconds actually played
completed Boolean @default(false)
source String? // where playback was initiated
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
song Song @relation(fields: [songId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([songId])
@@index([playedAt])
@@map("play_history")
}
// Queue model - for user playback queues
model Queue {
id String @id @default(uuid())
userId String @unique
songIds String // JSON array of song IDs in order
currentIndex Int @default(0)
isShuffled Boolean @default(false)
repeatMode String @default("none") // none, one, all
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("queues")
}
// UploadSession model - for chunked file uploads
model UploadSession {
id String @id @default(uuid())
userId String
fileName String
fileSize Int
mimeType String
chunkSize Int
totalChunks Int
uploadedChunks String? // JSON array of uploaded chunk numbers
status String @default("pending") // pending, uploading, completed, failed, expired
fileId String?
metadata String? // JSON string for additional metadata
createdAt DateTime @default(now())
expiresAt DateTime
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([status])
@@index([expiresAt])
@@map("upload_sessions")
}
// SearchIndex model - for full-text search indexing
model SearchIndex {
id String @id @default(uuid())
entityType String // song, album, artist, playlist
entityId String
title String
content String? // full text content
metadata String? // JSON string for additional searchable metadata
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([entityType, entityId])
@@index([title])
@@unique([entityType, entityId])
@@map("search_index")
}