Lb1/packages/database/prisma/schema.prisma

686 lines
19 KiB
Plaintext

// Luxe Platform Production Database Schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
// ==========================================
// ENUMS
// ==========================================
enum RoleType {
GUEST
USER
ADVERTISER
AGENCY
MODERATOR
SUPPORT
CONTENT_MODERATOR
FINANCE
ADMIN
SUPER_ADMIN
}
enum VerificationStatus {
UNVERIFIED
PENDING
VERIFIED
REJECTED
}
enum ListingStatus {
DRAFT
PENDING_APPROVAL
ACTIVE
REJECTED
EXPIRED
ARCHIVED
SUSPENDED
}
enum TransactionType {
RECHARGE
DEBIT
CREDIT
WITHDRAWAL
REFUND
BONUS
}
enum TransactionStatus {
PENDING
SUCCESS
FAILED
CANCELLED
}
enum PaymentProvider {
STRIPE
RAZORPAY
PAYPAL
WALLET
}
enum AdType {
BANNER
SPONSORED_LISTING
BOOST_LISTING
}
enum AdTargetType {
CITY
CATEGORY
GLOBAL
}
enum NotificationChannel {
EMAIL
SMS
PUSH
WHATSAPP
TELEGRAM
IN_APP
}
enum ReportStatus {
OPEN
UNDER_REVIEW
RESOLVED
DISMISSED
}
enum TicketPriority {
LOW
MEDIUM
HIGH
URGENT
}
enum TicketStatus {
OPEN
IN_PROGRESS
WAITING_ON_USER
RESOLVED
CLOSED
}
// ==========================================
// AUTH & USER IDENTITY
// ==========================================
model User {
id String @id @default(uuid()) @db.Uuid
email String @unique
phone String? @unique
username String @unique
passwordHash String
role RoleType @default(USER)
status String @default("ACTIVE") // ACTIVE, SUSPENDED, BANNED
emailVerifiedAt DateTime?
phoneVerifiedAt DateTime?
twoFactorEnabled Boolean @default(false)
twoFactorSecret String?
avatar String?
bio String?
language String @default("en")
currency String @default("USD")
lastLoginAt DateTime?
lastLoginIp String?
failedLoginCount Int @default(0)
lockedUntil DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
// Relations
sessions Session[]
devices Device[]
userRoles UserRole[]
profile AdvertiserProfile?
wallet Wallet?
reviews Review[]
favorites Favorite[]
savedSearches SavedSearch[]
notifications Notification[]
sentMessages Message[] @relation("SentMessages")
receivedMessages Message[] @relation("ReceivedMessages")
blogComments BlogComment[]
forumThreads ForumThread[]
forumReplies ForumReply[]
reportsSubmitted Report[] @relation("ReporterUser")
supportTickets SupportTicket[]
auditLogs AuditLog[]
@@index([email])
@@index([phone])
@@index([username])
@@index([role, status])
}
model Role {
id String @id @default(uuid()) @db.Uuid
name String @unique
code RoleType @unique
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
rolePermissions RolePermission[]
userRoles UserRole[]
}
model Permission {
id String @id @default(uuid()) @db.Uuid
resource String // e.g. listing, user, invoice
action String // e.g. create, read, update, delete, approve
description String?
createdAt DateTime @default(now())
rolePermissions RolePermission[]
@@unique([resource, action])
}
model RolePermission {
roleId String @db.Uuid
permissionId String @db.Uuid
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
@@id([roleId, permissionId])
}
model UserRole {
userId String @db.Uuid
roleId String @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
@@id([userId, roleId])
}
model Session {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
refreshToken String @unique
ipAddress String?
userAgent String?
isRevoked Boolean @default(false)
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([refreshToken])
}
model Device {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
deviceToken String @unique
platform String // ios, android, web
ipAddress String?
lastSeenAt DateTime @default(now())
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
// ==========================================
// ADVERTISER & LISTING DOMAIN
// ==========================================
model AdvertiserProfile {
id String @id @default(uuid()) @db.Uuid
userId String @unique @db.Uuid
type String @default("INDIVIDUAL") // INDIVIDUAL, AGENCY
agencyName String?
contactEmail String?
contactPhone String?
whatsapp String?
telegram String?
website String?
verificationStatus VerificationStatus @default(UNVERIFIED)
rating Float @default(0.0)
reviewCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
listings Listing[]
verifications VerificationRequest[]
}
model Category {
id String @id @default(uuid()) @db.Uuid
name String
slug String @unique
icon String?
description String?
parentId String? @db.Uuid
isFeatured Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
parent Category? @relation("CategoryHierarchy", fields: [parentId], references: [id])
children Category[] @relation("CategoryHierarchy")
listings Listing[]
@@index([slug])
}
model City {
id String @id @default(uuid()) @db.Uuid
name String
slug String @unique
state String?
country String @default("US")
image String?
latitude Float?
longitude Float?
createdAt DateTime @default(now())
areas Area[]
listings Listing[]
@@index([slug])
}
model Area {
id String @id @default(uuid()) @db.Uuid
cityId String @db.Uuid
name String
slug String
latitude Float?
longitude Float?
createdAt DateTime @default(now())
city City @relation(fields: [cityId], references: [id], onDelete: Cascade)
listings Listing[]
@@unique([cityId, slug])
}
model Listing {
id String @id @default(uuid()) @db.Uuid
profileId String @db.Uuid
categoryId String @db.Uuid
cityId String @db.Uuid
areaId String? @db.Uuid
title String
slug String @unique
description String
price Float
currency String @default("USD")
age Int?
height String?
tagline String?
isOnline Boolean @default(true)
isFeatured Boolean @default(false)
isPremium Boolean @default(false)
isVerified Boolean @default(false)
status ListingStatus @default(PENDING_APPROVAL)
viewsCount Int @default(0)
callsCount Int @default(0)
languages String[] @default([])
tags String[] @default([])
attributes Json? @default("{}")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
advertiser AdvertiserProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
category Category @relation(fields: [categoryId], references: [id])
city City @relation(fields: [cityId], references: [id])
area Area? @relation(fields: [areaId], references: [id])
gallery ListingGallery[]
reviews Review[]
favorites Favorite[]
ads Advertisement[]
@@index([slug])
@@index([cityId, categoryId, status])
@@index([isFeatured, isOnline])
@@index([price])
}
model ListingGallery {
id String @id @default(uuid()) @db.Uuid
listingId String @db.Uuid
url String
mediaType String @default("IMAGE") // IMAGE, VIDEO
thumbnail String?
isPrimary Boolean @default(false)
order Int @default(0)
createdAt DateTime @default(now())
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
@@index([listingId])
}
model Review {
id String @id @default(uuid()) @db.Uuid
listingId String @db.Uuid
userId String @db.Uuid
rating Int // 1 to 5
comment String
isApproved Boolean @default(true)
createdAt DateTime @default(now())
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([listingId])
}
model Favorite {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
listingId String @db.Uuid
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
@@unique([userId, listingId])
}
model SavedSearch {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
name String
filters Json
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
// ==========================================
// WALLET, FINANCIAL LEDGER & PAYMENTS
// ==========================================
model Wallet {
id String @id @default(uuid()) @db.Uuid
userId String @unique @db.Uuid
balance Float @default(0.0)
credits Float @default(0.0)
currency String @default("USD")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
ledgers WalletLedger[]
invoices Invoice[]
}
model WalletLedger {
id String @id @default(uuid()) @db.Uuid
walletId String @db.Uuid
type TransactionType
amount Float
balanceBefore Float
balanceAfter Float
referenceId String? // Order or Transaction ID
description String
createdAt DateTime @default(now())
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
@@index([walletId])
}
model Transaction {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
provider PaymentProvider
type TransactionType
status TransactionStatus @default(PENDING)
amount Float
currency String @default("USD")
gatewayTxnId String? @unique
metadata Json? @default("{}")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([gatewayTxnId])
}
model Invoice {
id String @id @default(uuid()) @db.Uuid
walletId String @db.Uuid
invoiceNumber String @unique
amount Float
taxAmount Float @default(0.0)
totalAmount Float
currency String @default("USD")
pdfUrl String?
createdAt DateTime @default(now())
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
}
model Coupon {
id String @id @default(uuid()) @db.Uuid
code String @unique
discountPct Float?
fixedAmount Float?
minPurchase Float @default(0.0)
maxUses Int @default(100)
usedCount Int @default(0)
expiresAt DateTime?
createdAt DateTime @default(now())
}
model Subscription {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
planName String // BASIC, VIP, AGENCY_PRO
price Float
status String @default("ACTIVE")
startsAt DateTime @default(now())
endsAt DateTime
createdAt DateTime @default(now())
}
model Advertisement {
id String @id @default(uuid()) @db.Uuid
listingId String? @db.Uuid
type AdType
target AdTargetType @default(GLOBAL)
targetId String? // City ID or Category ID
imageUrl String?
linkUrl String?
impressions Int @default(0)
clicks Int @default(0)
startsAt DateTime
endsAt DateTime
status String @default("ACTIVE")
createdAt DateTime @default(now())
listing Listing? @relation(fields: [listingId], references: [id], onDelete: Cascade)
}
// ==========================================
// MESSAGING & NOTIFICATIONS
// ==========================================
model Message {
id String @id @default(uuid()) @db.Uuid
senderId String @db.Uuid
receiverId String @db.Uuid
content String
isRead Boolean @default(false)
createdAt DateTime @default(now())
sender User @relation("SentMessages", fields: [senderId], references: [id], onDelete: Cascade)
receiver User @relation("ReceivedMessages", fields: [receiverId], references: [id], onDelete: Cascade)
@@index([senderId, receiverId])
}
model Notification {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
channel NotificationChannel @default(IN_APP)
title String
body String
data Json?
isRead Boolean @default(false)
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, isRead])
}
// ==========================================
// BLOG, FORUM & CONTENT
// ==========================================
model BlogPost {
id String @id @default(uuid()) @db.Uuid
title String
slug String @unique
excerpt String
content String
coverImage String?
authorName String @default("Luxe Editorial")
category String
tags String[] @default([])
readingTime Int @default(3)
likesCount Int @default(0)
isPublished Boolean @default(true)
publishedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
comments BlogComment[]
@@index([slug])
}
model BlogComment {
id String @id @default(uuid()) @db.Uuid
postId String @db.Uuid
userId String @db.Uuid
comment String
createdAt DateTime @default(now())
post BlogPost @relation(fields: [postId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model ForumThread {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
title String
slug String @unique
content String
category String
isPinned Boolean @default(false)
viewsCount Int @default(0)
likesCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
replies ForumReply[]
@@index([slug])
}
model ForumReply {
id String @id @default(uuid()) @db.Uuid
threadId String @db.Uuid
userId String @db.Uuid
content String
createdAt DateTime @default(now())
thread ForumThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
// ==========================================
// MODERATION, VERIFICATION & AUDIT LOGS
// ==========================================
model VerificationRequest {
id String @id @default(uuid()) @db.Uuid
profileId String @db.Uuid
idProofUrl String
selfieUrl String
notes String?
status VerificationStatus @default(PENDING)
rejectionReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
profile AdvertiserProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
}
model Report {
id String @id @default(uuid()) @db.Uuid
reporterId String @db.Uuid
targetType String // LISTING, USER, REVIEW, FORUM
targetId String
reason String
description String?
status ReportStatus @default(OPEN)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
reporter User @relation("ReporterUser", fields: [reporterId], references: [id], onDelete: Cascade)
}
model SupportTicket {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
ticketNum String @unique
subject String
description String
priority TicketPriority @default(MEDIUM)
status TicketStatus @default(OPEN)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model AuditLog {
id String @id @default(uuid()) @db.Uuid
userId String? @db.Uuid
action String // e.g. USER_BAN, PROFILE_VERIFY, ROLE_UPDATE
resource String // e.g. User, Listing
resourceId String?
ipAddress String?
userAgent String?
metadata Json? @default("{}")
createdAt DateTime @default(now())
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
@@index([action])
@@index([createdAt])
}
model FeatureFlag {
id String @id @default(uuid()) @db.Uuid
key String @unique
isEnabled Boolean @default(false)
description String?
metadata Json? @default("{}")
updatedAt DateTime @updatedAt
}