// 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 = "postgresql" url = env("DATABASE_URL") } // ============================================ // Core Models // ============================================ model User { id String @id @default(uuid()) email String @unique password String firstName String? lastName String? isActive Boolean @default(true) // Relations roles UserRole[] refreshTokens RefreshToken[] subscriptions Subscription[] // Multi-tenancy (optional) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id]) // Timestamps & Soft Delete createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? @@index([email]) @@index([tenantId]) } model Role { id String @id @default(uuid()) name String @unique description String? isSystem Boolean @default(false) // Relations users UserRole[] permissions RolePermission[] // Timestamps & Soft Delete createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? @@index([name]) } model Permission { id String @id @default(uuid()) name String @unique description String? resource String // e.g., "users", "posts" action String // e.g., "create", "read", "update", "delete" // Relations roles RolePermission[] // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([resource, action]) @@index([resource]) } // Many-to-many: User <-> Role model UserRole { id String @id @default(uuid()) userId String roleId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) @@unique([userId, roleId]) @@index([userId]) @@index([roleId]) } // Many-to-many: Role <-> Permission model RolePermission { id String @id @default(uuid()) roleId String permissionId String role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) @@unique([roleId, permissionId]) @@index([roleId]) @@index([permissionId]) } // ============================================ // Authentication // ============================================ model RefreshToken { id String @id @default(uuid()) token String @unique userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) expiresAt DateTime createdAt DateTime @default(now()) @@index([token]) @@index([userId]) } // ============================================ // Multi-tenancy (Optional) // ============================================ model Tenant { id String @id @default(uuid()) name String slug String @unique isActive Boolean @default(true) // Relations users User[] // Timestamps & Soft Delete createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? @@index([slug]) } // ============================================ // i18n / Translations (Optional - DB driven) // ============================================ model Translation { id String @id @default(uuid()) key String locale String // e.g., "en", "tr", "de" value String namespace String @default("common") // e.g., "common", "errors", "validation" // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([key, locale, namespace]) @@index([key]) @@index([locale]) @@index([namespace]) } // ============================================ // Game Calendar Domain Models // ============================================ model Game { id String @id @default(uuid()) slug String @unique // igdb slug or generated title String coverImage String? // URL to cover art description String? @db.Text releaseDate DateTime? // Nullable if TBD isTBD Boolean @default(false) releaseDateText String? // "Q3 2026", "2026", etc for display if exact date unknown // External Data igdbId Int? @unique rawgId Int? @unique sourceUrl String? // URL where this game was scraped from // Details rating Float? developer String? publisher String? // Relations platforms GamePlatform[] genres GameGenre[] screenshots GameScreenshot[] subscriptions Subscription[] // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? @@index([releaseDate]) @@index([slug]) } model Genre { id String @id @default(uuid()) name String @unique slug String @unique games GameGenre[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model GameGenre { gameId String genreId String game Game @relation(fields: [gameId], references: [id], onDelete: Cascade) genre Genre @relation(fields: [genreId], references: [id], onDelete: Cascade) @@id([gameId, genreId]) } model GameScreenshot { id String @id @default(uuid()) url String gameId String game Game @relation(fields: [gameId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) } model Platform { id String @id @default(uuid()) name String @unique // "PlayStation 5", "PC", "Xbox Series X" slug String @unique icon String? // URL/Phosphor icon name // Relations games GamePlatform[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model GamePlatform { gameId String platformId String game Game @relation(fields: [gameId], references: [id], onDelete: Cascade) platform Platform @relation(fields: [platformId], references: [id], onDelete: Cascade) @@id([gameId, platformId]) } model Event { id String @id @default(uuid()) title String slug String @unique description String? @db.Text startTime DateTime endTime DateTime? streamUrl String? coverImage String? type EventType @default(SHOWCASE) // External source String? // "igdb", "manual" // Relations subscriptions Subscription[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? @@index([startTime]) } enum EventType { SHOWCASE RELEASE TOURNAMENT OTHER } model Subscription { id String @id @default(uuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) // Target (Either Game or Event) gameId String? game Game? @relation(fields: [gameId], references: [id], onDelete: Cascade) eventId String? event Event? @relation(fields: [eventId], references: [id], onDelete: Cascade) // Notification Preferences for this subscription notifyEmail Boolean @default(false) notifyPush Boolean @default(true) createdAt DateTime @default(now()) @@index([userId]) @@unique([userId, gameId]) // One sub per game @@unique([userId, eventId]) // One sub per event } model ThemeConfig { id String @id @default(uuid()) key String @unique @default("current_theme") // Singleton-ish isActive Boolean @default(true) // Visuals gameTitle String // "Elden Ring" primaryColor String // Hex secondaryColor String // Hex backgroundColor String // Hex backgroundImage String? // URL logoImage String? // URL createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }