331 lines
8.1 KiB
Plaintext
331 lines
8.1 KiB
Plaintext
// 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")
|
||
}
|
||
|
||
enum UserRole {
|
||
USER
|
||
MOD
|
||
ADMIN
|
||
}
|
||
|
||
model User {
|
||
id Int @id @default(autoincrement())
|
||
username String @unique
|
||
email String @unique
|
||
passwordHash String
|
||
avatarUrl String? @db.VarChar(512)
|
||
role UserRole @default(USER)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @default(now()) @updatedAt
|
||
|
||
Deal Deal[]
|
||
votes DealVote[]
|
||
comments Comment[]
|
||
companies Seller[]
|
||
domains SellerDomain[]
|
||
dealVoteHistory DealVoteHistory[]
|
||
dealNotices DealNotice[] @relation("UserDealNotices")
|
||
|
||
refreshTokens RefreshToken[] // <-- bunu ekle
|
||
commentLikes CommentLike[]
|
||
}
|
||
|
||
model RefreshToken {
|
||
id String @id @default(cuid()) // token kaydı id
|
||
userId Int
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
tokenHash String @unique // refresh token hash (örn sha256)
|
||
familyId String // rotation zinciri için aynı aile
|
||
jti String // token id (JWT jti / random)
|
||
expiresAt DateTime
|
||
createdAt DateTime @default(now())
|
||
|
||
revokedAt DateTime?
|
||
replacedById String? @unique
|
||
replacedBy RefreshToken? @relation("TokenRotation", fields: [replacedById], references: [id])
|
||
replaces RefreshToken? @relation("TokenRotation")
|
||
|
||
createdByIp String?
|
||
userAgent String?
|
||
|
||
@@index([userId])
|
||
@@index([familyId])
|
||
@@index([expiresAt])
|
||
}
|
||
|
||
enum DealStatus {
|
||
PENDING
|
||
ACTIVE
|
||
EXPIRED
|
||
REJECTED
|
||
}
|
||
|
||
enum SaleType {
|
||
ONLINE
|
||
OFFLINE
|
||
CODE
|
||
}
|
||
|
||
enum AffiliateType {
|
||
AFFILIATE
|
||
NON_AFFILIATE
|
||
USER_AFFILIATE
|
||
}
|
||
|
||
model SellerDomain {
|
||
id Int @id @default(autoincrement())
|
||
domain String @unique
|
||
sellerId Int
|
||
seller Seller @relation(fields: [sellerId], references: [id])
|
||
createdAt DateTime @default(now())
|
||
createdById Int
|
||
createdBy User @relation(fields: [createdById], references: [id])
|
||
}
|
||
|
||
model Seller {
|
||
id Int @id @default(autoincrement())
|
||
name String @unique
|
||
url String @default("")
|
||
sellerLogo String @default("")
|
||
isActive Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
createdById Int
|
||
|
||
deals Deal[]
|
||
createdBy User @relation(fields: [createdById], references: [id])
|
||
domains SellerDomain[]
|
||
}
|
||
|
||
/**
|
||
* NEW: Category (self-parent tree)
|
||
* NOTE: You want Deal.categoryId default 0 -> you MUST create a Category row with id=0 ("Undefined")
|
||
*/
|
||
model Category {
|
||
id Int @id @default(autoincrement())
|
||
name String
|
||
slug String @unique
|
||
description String @default("")
|
||
parentId Int?
|
||
parent Category? @relation("CategoryParent", fields: [parentId], references: [id])
|
||
children Category[] @relation("CategoryParent")
|
||
|
||
deals Deal[]
|
||
|
||
@@index([parentId])
|
||
}
|
||
|
||
/**
|
||
* NEW: Tag (canonical)
|
||
*/
|
||
model Tag {
|
||
id Int @id @default(autoincrement())
|
||
slug String @unique
|
||
name String
|
||
|
||
dealTags DealTag[]
|
||
}
|
||
|
||
/**
|
||
* NEW: Join table Deal <-> Tag (many-to-many)
|
||
*/
|
||
model DealTag {
|
||
dealId Int
|
||
tagId Int
|
||
|
||
deal Deal @relation(fields: [dealId], references: [id], onDelete: Cascade)
|
||
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@id([dealId, tagId])
|
||
@@index([tagId, dealId]) // tag -> deals hızlı
|
||
@@index([dealId]) // deal -> tags hızlı
|
||
}
|
||
|
||
model Deal {
|
||
id Int @id @default(autoincrement())
|
||
title String
|
||
description String?
|
||
url String?
|
||
price Float?
|
||
originalPrice Float?
|
||
shippingPrice Float?
|
||
percentOff Float?
|
||
userId Int
|
||
score Int @default(0)
|
||
commentCount Int @default(0)
|
||
status DealStatus @default(PENDING)
|
||
saletype SaleType @default(ONLINE)
|
||
affiliateType AffiliateType @default(NON_AFFILIATE)
|
||
|
||
sellerId Int?
|
||
customSeller String?
|
||
|
||
seller Seller? @relation(fields: [sellerId], references: [id])
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
user User @relation(fields: [userId], references: [id])
|
||
votes DealVote[]
|
||
voteHistory DealVoteHistory[]
|
||
notices DealNotice[] @relation("DealNotices")
|
||
comments Comment[]
|
||
images DealImage[]
|
||
|
||
// NEW: category (single)
|
||
categoryId Int @default(0)
|
||
category Category @relation(fields: [categoryId], references: [id])
|
||
|
||
// NEW: tags (multiple, optional)
|
||
dealTags DealTag[]
|
||
aiReview DealAiReview?
|
||
@@index([categoryId, createdAt])
|
||
@@index([userId, createdAt])
|
||
}
|
||
|
||
enum DealNoticeSeverity {
|
||
INFO
|
||
WARNING
|
||
DANGER
|
||
SUCCESS
|
||
}
|
||
|
||
model DealNotice {
|
||
id Int @id @default(autoincrement())
|
||
dealId Int
|
||
title String
|
||
body String?
|
||
|
||
severity DealNoticeSeverity @default(INFO)
|
||
isActive Boolean @default(true)
|
||
|
||
createdBy Int
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
deal Deal @relation("DealNotices", fields: [dealId], references: [id], onDelete: Cascade)
|
||
creator User @relation("UserDealNotices", fields: [createdBy], references: [id])
|
||
|
||
@@index([dealId, isActive, createdAt])
|
||
@@index([createdBy])
|
||
}
|
||
|
||
model DealImage {
|
||
id Int @id @default(autoincrement())
|
||
imageUrl String @db.VarChar(512)
|
||
order Int @default(0)
|
||
createdAt DateTime @default(now())
|
||
dealId Int
|
||
|
||
deal Deal @relation(fields: [dealId], references: [id], onDelete: Cascade)
|
||
}
|
||
|
||
model DealVote {
|
||
id Int @id @default(autoincrement())
|
||
dealId Int
|
||
userId Int
|
||
voteType Int @default(0) // -1,0,1
|
||
createdAt DateTime @default(now())
|
||
lastVotedAt DateTime @default(now()) // her vote değişiminde set edeceğiz
|
||
|
||
deal Deal @relation(fields: [dealId], references: [id])
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
@@unique([dealId, userId])
|
||
@@index([dealId])
|
||
}
|
||
|
||
model DealVoteHistory {
|
||
id Int @id @default(autoincrement())
|
||
dealId Int
|
||
userId Int
|
||
voteType Int
|
||
createdAt DateTime @default(now())
|
||
|
||
deal Deal @relation(fields: [dealId], references: [id])
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
@@index([dealId])
|
||
@@index([userId])
|
||
@@index([createdAt])
|
||
}
|
||
|
||
model Comment {
|
||
id Int @id @default(autoincrement())
|
||
text String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
userId Int
|
||
dealId Int
|
||
|
||
parentId Int?
|
||
likeCount Int @default(0)
|
||
|
||
deletedAt DateTime?
|
||
|
||
user User @relation(fields: [userId], references: [id])
|
||
deal Deal @relation(fields: [dealId], references: [id])
|
||
|
||
parent Comment? @relation("CommentReplies", fields: [parentId], references: [id], onDelete: SetNull)
|
||
replies Comment[] @relation("CommentReplies")
|
||
likes CommentLike[]
|
||
|
||
@@index([dealId, createdAt])
|
||
@@index([parentId, createdAt])
|
||
@@index([dealId, parentId, createdAt])
|
||
@@index([deletedAt])
|
||
}
|
||
|
||
model CommentLike {
|
||
id Int @id @default(autoincrement())
|
||
commentId Int
|
||
userId Int
|
||
createdAt DateTime @default(now())
|
||
|
||
comment Comment @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
@@unique([commentId, userId])
|
||
@@index([commentId])
|
||
@@index([userId])
|
||
}
|
||
|
||
enum DealAiIssueType {
|
||
NONE
|
||
PROFANITY
|
||
PHONE_NUMBER
|
||
PERSONAL_DATA
|
||
SPAM
|
||
OTHER
|
||
}
|
||
|
||
model DealAiReview {
|
||
id Int @id @default(autoincrement())
|
||
|
||
dealId Int @unique
|
||
deal Deal @relation(fields: [dealId], references: [id], onDelete: Cascade)
|
||
|
||
bestCategoryId Int
|
||
needsReview Boolean @default(false)
|
||
|
||
hasIssue Boolean @default(false)
|
||
issueType DealAiIssueType @default(NONE)
|
||
issueReason String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([needsReview, hasIssue, updatedAt])
|
||
}
|