// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Song { id Int @id @default(autoincrement()) title String artist String filename String // Filename in public/uploads coverImage String? // Filename in public/uploads/covers releaseYear Int? // Release year from iTunes createdAt DateTime @default(now()) puzzles DailyPuzzle[] genres Genre[] specials SpecialSong[] averageRating Float @default(0) ratingCount Int @default(0) excludeFromGlobal Boolean @default(false) } model Genre { id Int @id @default(autoincrement()) name Json // Multilingual: { "de": "Rock", "en": "Rock" } subtitle Json? // Multilingual active Boolean @default(true) songs Song[] dailyPuzzles DailyPuzzle[] curatorGenres CuratorGenre[] } model Special { id Int @id @default(autoincrement()) name Json // Multilingual subtitle Json? // Multilingual maxAttempts Int @default(7) unlockSteps String // JSON string: e.g. "[2, 4, 7, 11, 16, 30]" createdAt DateTime @default(now()) launchDate DateTime? endDate DateTime? curator String? songs SpecialSong[] puzzles DailyPuzzle[] news News[] curatorSpecials CuratorSpecial[] } model SpecialSong { id Int @id @default(autoincrement()) specialId Int special Special @relation(fields: [specialId], references: [id], onDelete: Cascade) songId Int song Song @relation(fields: [songId], references: [id], onDelete: Cascade) startTime Int @default(0) // Start time in seconds order Int? // For manual ordering @@unique([specialId, songId]) } model DailyPuzzle { id Int @id @default(autoincrement()) date String // Format: YYYY-MM-DD songId Int song Song @relation(fields: [songId], references: [id], onDelete: Cascade) genreId Int? genre Genre? @relation(fields: [genreId], references: [id]) specialId Int? special Special? @relation(fields: [specialId], references: [id]) @@unique([date, genreId, specialId]) } model News { id Int @id @default(autoincrement()) title Json // Multilingual content Json // Multilingual author String? // Optional: curator/admin name publishedAt DateTime @default(now()) updatedAt DateTime @updatedAt featured Boolean @default(false) // Highlight important news specialId Int? // Optional: link to a special special Special? @relation(fields: [specialId], references: [id], onDelete: SetNull) @@index([publishedAt]) } model PlayerState { id Int @id @default(autoincrement()) identifier String // UUID des Spielers (für Cross-Domain-Sync) genreKey String // Genre-Name oder "global" oder "special:" gameState String // JSON-String des GameState statistics String // JSON-String der Statistics lastPlayed DateTime @updatedAt createdAt DateTime @default(now()) @@unique([identifier, genreKey]) @@index([identifier]) } model Curator { id Int @id @default(autoincrement()) username String @unique passwordHash String isGlobalCurator Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt genres CuratorGenre[] specials CuratorSpecial[] } model CuratorGenre { id Int @id @default(autoincrement()) curatorId Int genreId Int curator Curator @relation(fields: [curatorId], references: [id], onDelete: Cascade) genre Genre @relation(fields: [genreId], references: [id], onDelete: Cascade) @@unique([curatorId, genreId]) } model CuratorSpecial { id Int @id @default(autoincrement()) curatorId Int specialId Int curator Curator @relation(fields: [curatorId], references: [id], onDelete: Cascade) special Special @relation(fields: [specialId], references: [id], onDelete: Cascade) @@unique([curatorId, specialId]) } model PoliticalStatement { id Int @id @default(autoincrement()) locale String text String active Boolean @default(true) source String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([locale, active]) }