// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init generator client { provider = "prisma-client" output = "../src/generated/prisma" } datasource db { provider = "postgresql" } /** Artworks **/ model Artwork { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String @unique slug String @unique altText String? description String? notes String? month Int? year Int? sortKey Int? okLabL Float? okLabA Float? okLabB Float? creationDate DateTime? needsWork Boolean @default(true) nsfw Boolean @default(false) published Boolean @default(false) setAsHeader Boolean @default(false) colorStatus String @default("PENDING") // PENDING | PROCESSING | READY | FAILED colorError String? colorsGeneratedAt DateTime? fileId String @unique file FileData @relation(fields: [fileId], references: [id]) galleryId String? gallery Gallery? @relation(fields: [galleryId], references: [id]) metadata ArtworkMetadata? albums Album[] categories ArtCategory[] colors ArtworkColor[] tags ArtTag[] variants FileVariant[] @@index([colorStatus]) @@index([published, sortKey, id]) @@index([year, published, sortKey, id]) } model Album { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String @unique slug String @unique description String? artworks Artwork[] } model Gallery { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String @unique slug String @unique description String? artworks Artwork[] } model ArtCategory { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String @unique slug String @unique description String? artworks Artwork[] tags ArtTag[] } model ArtTag { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String @unique slug String @unique isParent Boolean @default(false) showOnAnimalPage Boolean @default(false) description String? aliases ArtTagAlias[] artworks Artwork[] categories ArtCategory[] parentId String? parent ArtTag? @relation("TagHierarchy", fields: [parentId], references: [id], onDelete: SetNull) children ArtTag[] @relation("TagHierarchy") } model ArtTagAlias { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt alias String @unique tagId String tag ArtTag @relation(fields: [tagId], references: [id], onDelete: Cascade) @@unique([tagId, alias]) @@index([alias]) } model Color { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String @unique type String hex String? blue Int? green Int? red Int? artworks ArtworkColor[] } model ArtworkColor { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt artworkId String colorId String type String artwork Artwork @relation(fields: [artworkId], references: [id]) color Color @relation(fields: [colorId], references: [id]) @@unique([artworkId, type]) } model ArtworkMetadata { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt artworkId String @unique depth String format String space String channels Int height Int width Int autoOrientH Int? autoOrientW Int? bitsPerSample Int? density Int? hasAlpha Boolean? hasProfile Boolean? isPalette Boolean? isProgressive Boolean? artwork Artwork @relation(fields: [artworkId], references: [id]) } model FileData { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt fileKey String @unique originalFile String @unique fileType String name String fileSize Int uploadDate DateTime artwork Artwork? } model FileVariant { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt s3Key String type String height Int width Int fileExtension String? mimeType String? url String? sizeBytes Int? artworkId String artwork Artwork @relation(fields: [artworkId], references: [id]) @@unique([artworkId, type]) } /** Commissions **/ model Commission { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) } model CommissionType { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String description String? options CommissionTypeOption[] extras CommissionTypeExtra[] customInputs CommissionTypeCustomInput[] requests CommissionRequest[] } model CommissionOption { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String description String? types CommissionTypeOption[] requests CommissionRequest[] } model CommissionTypeOption { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) typeId String optionId String priceRange String? pricePercent Float? price Float? type CommissionType @relation(fields: [typeId], references: [id]) option CommissionOption @relation(fields: [optionId], references: [id]) @@unique([typeId, optionId]) } model CommissionExtra { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String description String? requests CommissionRequest[] types CommissionTypeExtra[] } model CommissionTypeExtra { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) typeId String extraId String priceRange String? pricePercent Float? price Float? type CommissionType @relation(fields: [typeId], references: [id]) extra CommissionExtra @relation(fields: [extraId], references: [id]) @@unique([typeId, extraId]) } model CommissionCustomInput { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) name String @unique fieldId String types CommissionTypeCustomInput[] } model CommissionTypeCustomInput { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sortIndex Int @default(0) typeId String customInputId String inputType String label String required Boolean @default(false) type CommissionType @relation(fields: [typeId], references: [id]) customInput CommissionCustomInput @relation(fields: [customInputId], references: [id]) @@unique([typeId, customInputId]) } model CommissionRequest { id String @id @default(cuid()) index Int @default(autoincrement()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt customerName String customerEmail String message String status String @default("NEW") customerSocials String? ipAddress String? userAgent String? customFields Json? optionId String? typeId String? option CommissionOption? @relation(fields: [optionId], references: [id]) type CommissionType? @relation(fields: [typeId], references: [id]) extras CommissionExtra[] files CommissionRequestFile[] } model CommissionGuidelines { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt markdown String isActive Boolean @default(true) @@index([isActive]) } model CommissionRequestFile { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt fileKey String @unique originalFile String fileType String fileSize Int uploadDate DateTime requestId String request CommissionRequest @relation(fields: [requestId], references: [id], onDelete: Cascade) } model TermsOfService { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt markdown String version Int @default(autoincrement()) } /** Voting **/ model Animal { id String @id @default(cuid()) name String slug String @unique description String? imageUrl String? isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt votes Vote[] } model Vote { id String @id @default(cuid()) animalId String voterId String? // Twitch user ID (nullable for anon) source VoteSource createdAt DateTime @default(now()) animal Animal @relation(fields: [animalId], references: [id]) @@index([animalId]) } enum VoteSource { TWITCH_CHAT CHANNEL_POINTS ADMIN } /** User management **/ model User { id String @id name String email String emailVerified Boolean @default(false) image String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions Session[] accounts Account[] role String @default("user") banned Boolean? @default(false) banReason String? banExpires DateTime? @@unique([email]) @@map("user") } model Session { id String @id expiresAt DateTime token String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ipAddress String? userAgent String? userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) impersonatedBy String? @@unique([token]) @@index([userId]) @@map("session") } model Account { id String @id accountId String providerId String userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) accessToken String? refreshToken String? idToken String? accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? password String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) @@map("account") } model Verification { id String @id identifier String value String expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([identifier]) @@map("verification") }