fix(db): add explicit onDelete strategies to all Prisma FK relations
Audit and update all foreign key relations in schema.prisma with appropriate cascade/restrict/set-null strategies to prevent orphaned records and FK constraint violations on parent deletion. Changes (RESTRICT → CASCADE): - Agent.userId, Listing.propertyId, Transaction.listingId - Inquiry.listingId, Inquiry.userId, Lead.agentId - Subscription.userId, UsageRecord.subscriptionId - Valuation.propertyId, Review.userId Confirmed correct (no change needed): - Listing.agentId (SetNull), Listing.sellerId (Restrict) - Transaction.buyerId (Restrict), Payment.userId (Restrict) - Payment.transactionId (SetNull), Subscription.planId (Restrict) - PropertyMedia, SavedSearch, RefreshToken, OAuthAccount (CASCADE) Migration: 20260411000000_add_cascade_delete_strategies Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,59 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Agent" DROP CONSTRAINT "Agent_userId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Listing" DROP CONSTRAINT "Listing_propertyId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Transaction" DROP CONSTRAINT "Transaction_listingId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Inquiry" DROP CONSTRAINT "Inquiry_listingId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Inquiry" DROP CONSTRAINT "Inquiry_userId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Lead" DROP CONSTRAINT "Lead_agentId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Subscription" DROP CONSTRAINT "Subscription_userId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "UsageRecord" DROP CONSTRAINT "UsageRecord_subscriptionId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Valuation" DROP CONSTRAINT "Valuation_propertyId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Review" DROP CONSTRAINT "Review_userId_fkey";
|
||||||
|
|
||||||
|
-- AddForeignKey: Agent.userId -> User.id (CASCADE)
|
||||||
|
ALTER TABLE "Agent" ADD CONSTRAINT "Agent_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Listing.propertyId -> Property.id (CASCADE)
|
||||||
|
ALTER TABLE "Listing" ADD CONSTRAINT "Listing_propertyId_fkey" FOREIGN KEY ("propertyId") REFERENCES "Property"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Transaction.listingId -> Listing.id (CASCADE)
|
||||||
|
ALTER TABLE "Transaction" ADD CONSTRAINT "Transaction_listingId_fkey" FOREIGN KEY ("listingId") REFERENCES "Listing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Inquiry.listingId -> Listing.id (CASCADE)
|
||||||
|
ALTER TABLE "Inquiry" ADD CONSTRAINT "Inquiry_listingId_fkey" FOREIGN KEY ("listingId") REFERENCES "Listing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Inquiry.userId -> User.id (CASCADE)
|
||||||
|
ALTER TABLE "Inquiry" ADD CONSTRAINT "Inquiry_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Lead.agentId -> Agent.id (CASCADE)
|
||||||
|
ALTER TABLE "Lead" ADD CONSTRAINT "Lead_agentId_fkey" FOREIGN KEY ("agentId") REFERENCES "Agent"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Subscription.userId -> User.id (CASCADE)
|
||||||
|
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: UsageRecord.subscriptionId -> Subscription.id (CASCADE)
|
||||||
|
ALTER TABLE "UsageRecord" ADD CONSTRAINT "UsageRecord_subscriptionId_fkey" FOREIGN KEY ("subscriptionId") REFERENCES "Subscription"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Valuation.propertyId -> Property.id (CASCADE)
|
||||||
|
ALTER TABLE "Valuation" ADD CONSTRAINT "Valuation_propertyId_fkey" FOREIGN KEY ("propertyId") REFERENCES "Property"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey: Review.userId -> User.id (CASCADE)
|
||||||
|
ALTER TABLE "Review" ADD CONSTRAINT "Review_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -32,31 +32,31 @@ enum KYCStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
email String? @unique
|
email String? @unique
|
||||||
phone String @unique
|
phone String @unique
|
||||||
passwordHash String?
|
passwordHash String?
|
||||||
fullName String
|
fullName String
|
||||||
avatarUrl String?
|
avatarUrl String?
|
||||||
role UserRole @default(BUYER)
|
role UserRole @default(BUYER)
|
||||||
kycStatus KYCStatus @default(NONE)
|
kycStatus KYCStatus @default(NONE)
|
||||||
kycData Json?
|
kycData Json?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
deletedAt DateTime?
|
deletedAt DateTime?
|
||||||
deletionScheduledAt DateTime?
|
deletionScheduledAt DateTime?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
agent Agent?
|
agent Agent?
|
||||||
listings Listing[]
|
listings Listing[]
|
||||||
savedSearches SavedSearch[]
|
savedSearches SavedSearch[]
|
||||||
subscription Subscription?
|
subscription Subscription?
|
||||||
payments Payment[]
|
payments Payment[]
|
||||||
reviews Review[]
|
reviews Review[]
|
||||||
inquiriesSent Inquiry[]
|
inquiriesSent Inquiry[]
|
||||||
refreshTokens RefreshToken[]
|
refreshTokens RefreshToken[]
|
||||||
oauthAccounts OAuthAccount[]
|
oauthAccounts OAuthAccount[]
|
||||||
buyerTransactions Transaction[] @relation("BuyerTransactions")
|
buyerTransactions Transaction[] @relation("BuyerTransactions")
|
||||||
|
|
||||||
@@index([role])
|
@@index([role])
|
||||||
@@index([kycStatus])
|
@@index([kycStatus])
|
||||||
@@ -64,7 +64,6 @@ model User {
|
|||||||
@@index([deletedAt])
|
@@index([deletedAt])
|
||||||
@@index([deletionScheduledAt])
|
@@index([deletionScheduledAt])
|
||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
|
|
||||||
// --- Compound indexes (query optimization) ---
|
// --- Compound indexes (query optimization) ---
|
||||||
@@index([role, isActive, createdAt(sort: Desc)])
|
@@index([role, isActive, createdAt(sort: Desc)])
|
||||||
@@index([kycStatus, createdAt])
|
@@index([kycStatus, createdAt])
|
||||||
@@ -76,14 +75,14 @@ enum OAuthProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model RefreshToken {
|
model RefreshToken {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
token String @unique
|
token String @unique
|
||||||
family String
|
family String
|
||||||
expiresAt DateTime
|
expiresAt DateTime
|
||||||
revokedAt DateTime?
|
revokedAt DateTime?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@index([family])
|
@@index([family])
|
||||||
@@ -110,14 +109,14 @@ model OAuthAccount {
|
|||||||
model Agent {
|
model Agent {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String @unique
|
userId String @unique
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
licenseNumber String?
|
licenseNumber String?
|
||||||
agency String?
|
agency String?
|
||||||
qualityScore Float @default(0)
|
qualityScore Float @default(0)
|
||||||
totalDeals Int @default(0)
|
totalDeals Int @default(0)
|
||||||
responseTimeAvg Int?
|
responseTimeAvg Int?
|
||||||
bio String?
|
bio String?
|
||||||
serviceAreas Json // ["quan-1", "quan-7", "thu-duc"]
|
serviceAreas Json // ["quan-1", "quan-7", "thu-duc"]
|
||||||
isVerified Boolean @default(false)
|
isVerified Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
@@ -170,10 +169,10 @@ enum Direction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Property {
|
model Property {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
propertyType PropertyType
|
propertyType PropertyType
|
||||||
title String
|
title String
|
||||||
description String @db.Text
|
description String @db.Text
|
||||||
address String
|
address String
|
||||||
ward String
|
ward String
|
||||||
district String
|
district String
|
||||||
@@ -193,8 +192,8 @@ model Property {
|
|||||||
nearbyPOIs Json?
|
nearbyPOIs Json?
|
||||||
metroDistanceM Float?
|
metroDistanceM Float?
|
||||||
projectName String?
|
projectName String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
listings Listing[]
|
listings Listing[]
|
||||||
valuations Valuation[]
|
valuations Valuation[]
|
||||||
@@ -204,7 +203,6 @@ model Property {
|
|||||||
@@index([propertyType])
|
@@index([propertyType])
|
||||||
@@index([district, city])
|
@@index([district, city])
|
||||||
@@index([location], type: Gist)
|
@@index([location], type: Gist)
|
||||||
|
|
||||||
// --- Compound indexes (query optimization) ---
|
// --- Compound indexes (query optimization) ---
|
||||||
@@index([district, propertyType])
|
@@index([district, propertyType])
|
||||||
@@index([district, city, propertyType])
|
@@index([district, city, propertyType])
|
||||||
@@ -215,7 +213,7 @@ model PropertyMedia {
|
|||||||
propertyId String
|
propertyId String
|
||||||
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
||||||
url String
|
url String
|
||||||
type String // "image" | "video"
|
type String // "image" | "video"
|
||||||
order Int @default(0)
|
order Int @default(0)
|
||||||
caption String?
|
caption String?
|
||||||
aiTags Json?
|
aiTags Json?
|
||||||
@@ -227,11 +225,11 @@ model PropertyMedia {
|
|||||||
model Listing {
|
model Listing {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
propertyId String
|
propertyId String
|
||||||
property Property @relation(fields: [propertyId], references: [id])
|
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
||||||
agentId String?
|
agentId String?
|
||||||
agent Agent? @relation(fields: [agentId], references: [id])
|
agent Agent? @relation(fields: [agentId], references: [id], onDelete: SetNull)
|
||||||
sellerId String
|
sellerId String
|
||||||
seller User @relation(fields: [sellerId], references: [id])
|
seller User @relation(fields: [sellerId], references: [id], onDelete: Restrict)
|
||||||
transactionType TransactionType
|
transactionType TransactionType
|
||||||
status ListingStatus @default(DRAFT)
|
status ListingStatus @default(DRAFT)
|
||||||
priceVND BigInt
|
priceVND BigInt
|
||||||
@@ -265,7 +263,6 @@ model Listing {
|
|||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
@@index([featuredUntil])
|
@@index([featuredUntil])
|
||||||
@@index([expiresAt])
|
@@index([expiresAt])
|
||||||
|
|
||||||
// --- Compound indexes (query optimization) ---
|
// --- Compound indexes (query optimization) ---
|
||||||
@@index([sellerId, status, publishedAt(sort: Desc)])
|
@@index([sellerId, status, publishedAt(sort: Desc)])
|
||||||
@@index([agentId, status])
|
@@index([agentId, status])
|
||||||
@@ -309,9 +306,9 @@ enum TransactionStatus {
|
|||||||
model Transaction {
|
model Transaction {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
listingId String
|
listingId String
|
||||||
listing Listing @relation(fields: [listingId], references: [id])
|
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
|
||||||
buyerId String
|
buyerId String
|
||||||
buyer User @relation("BuyerTransactions", fields: [buyerId], references: [id])
|
buyer User @relation("BuyerTransactions", fields: [buyerId], references: [id], onDelete: Restrict)
|
||||||
status TransactionStatus @default(INQUIRY)
|
status TransactionStatus @default(INQUIRY)
|
||||||
agreedPrice BigInt?
|
agreedPrice BigInt?
|
||||||
depositAmount BigInt?
|
depositAmount BigInt?
|
||||||
@@ -330,9 +327,9 @@ model Transaction {
|
|||||||
model Inquiry {
|
model Inquiry {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
listingId String
|
listingId String
|
||||||
listing Listing @relation(fields: [listingId], references: [id])
|
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
|
||||||
userId String
|
userId String
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
message String @db.Text
|
message String @db.Text
|
||||||
phone String?
|
phone String?
|
||||||
isRead Boolean @default(false)
|
isRead Boolean @default(false)
|
||||||
@@ -355,7 +352,7 @@ enum LeadStatus {
|
|||||||
model Lead {
|
model Lead {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
agentId String
|
agentId String
|
||||||
agent Agent @relation(fields: [agentId], references: [id])
|
agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
||||||
name String
|
name String
|
||||||
phone String
|
phone String
|
||||||
email String?
|
email String?
|
||||||
@@ -397,20 +394,20 @@ enum PaymentType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Payment {
|
model Payment {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id], onDelete: Restrict)
|
||||||
transactionId String?
|
transactionId String?
|
||||||
transaction Transaction? @relation(fields: [transactionId], references: [id])
|
transaction Transaction? @relation(fields: [transactionId], references: [id], onDelete: SetNull)
|
||||||
provider PaymentProvider
|
provider PaymentProvider
|
||||||
type PaymentType
|
type PaymentType
|
||||||
amountVND BigInt
|
amountVND BigInt
|
||||||
status PaymentStatus @default(PENDING)
|
status PaymentStatus @default(PENDING)
|
||||||
providerTxId String?
|
providerTxId String?
|
||||||
callbackData Json?
|
callbackData Json?
|
||||||
idempotencyKey String?
|
idempotencyKey String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
@@unique([userId, provider, idempotencyKey], name: "Payment_idempotency_unique")
|
@@unique([userId, provider, idempotencyKey], name: "Payment_idempotency_unique")
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@ -418,7 +415,6 @@ model Payment {
|
|||||||
@@index([status])
|
@@index([status])
|
||||||
@@index([providerTxId])
|
@@index([providerTxId])
|
||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
|
|
||||||
// --- Compound indexes (query optimization) ---
|
// --- Compound indexes (query optimization) ---
|
||||||
@@index([userId, status, createdAt(sort: Desc)])
|
@@index([userId, status, createdAt(sort: Desc)])
|
||||||
@@index([userId, type, createdAt(sort: Desc)])
|
@@index([userId, type, createdAt(sort: Desc)])
|
||||||
@@ -443,17 +439,17 @@ enum SubscriptionStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Plan {
|
model Plan {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
tier PlanTier @unique
|
tier PlanTier @unique
|
||||||
name String
|
name String
|
||||||
priceMonthlyVND BigInt
|
priceMonthlyVND BigInt
|
||||||
priceYearlyVND BigInt
|
priceYearlyVND BigInt
|
||||||
maxListings Int?
|
maxListings Int?
|
||||||
maxSavedSearches Int?
|
maxSavedSearches Int?
|
||||||
maxAnalyticsQueries Int?
|
maxAnalyticsQueries Int?
|
||||||
maxMediaUploads Int?
|
maxMediaUploads Int?
|
||||||
features Json
|
features Json
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
|
|
||||||
subscriptions Subscription[]
|
subscriptions Subscription[]
|
||||||
}
|
}
|
||||||
@@ -461,9 +457,9 @@ model Plan {
|
|||||||
model Subscription {
|
model Subscription {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String @unique
|
userId String @unique
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
planId String
|
planId String
|
||||||
plan Plan @relation(fields: [planId], references: [id])
|
plan Plan @relation(fields: [planId], references: [id], onDelete: Restrict)
|
||||||
status SubscriptionStatus @default(ACTIVE)
|
status SubscriptionStatus @default(ACTIVE)
|
||||||
currentPeriodStart DateTime
|
currentPeriodStart DateTime
|
||||||
currentPeriodEnd DateTime
|
currentPeriodEnd DateTime
|
||||||
@@ -480,7 +476,7 @@ model Subscription {
|
|||||||
model UsageRecord {
|
model UsageRecord {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
subscriptionId String
|
subscriptionId String
|
||||||
subscription Subscription @relation(fields: [subscriptionId], references: [id])
|
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
|
||||||
metric String
|
metric String
|
||||||
count Int
|
count Int
|
||||||
periodStart DateTime
|
periodStart DateTime
|
||||||
@@ -496,7 +492,7 @@ model UsageRecord {
|
|||||||
model Valuation {
|
model Valuation {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
propertyId String
|
propertyId String
|
||||||
property Property @relation(fields: [propertyId], references: [id])
|
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
||||||
estimatedPrice BigInt
|
estimatedPrice BigInt
|
||||||
confidence Float
|
confidence Float
|
||||||
pricePerM2 Float
|
pricePerM2 Float
|
||||||
@@ -547,18 +543,18 @@ enum NotificationStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model NotificationLog {
|
model NotificationLog {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
channel NotificationChannel
|
channel NotificationChannel
|
||||||
templateKey String
|
templateKey String
|
||||||
subject String?
|
subject String?
|
||||||
body String @db.Text
|
body String @db.Text
|
||||||
metadata Json?
|
metadata Json?
|
||||||
status NotificationStatus @default(PENDING)
|
status NotificationStatus @default(PENDING)
|
||||||
errorDetail String?
|
errorDetail String?
|
||||||
sentAt DateTime?
|
sentAt DateTime?
|
||||||
readAt DateTime?
|
readAt DateTime?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@index([channel, status])
|
@@index([channel, status])
|
||||||
@@ -581,6 +577,48 @@ model NotificationPreference {
|
|||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// ADMIN AUDIT LOG
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
enum AdminAction {
|
||||||
|
LISTING_APPROVED
|
||||||
|
LISTING_REJECTED
|
||||||
|
LISTING_BULK_APPROVED
|
||||||
|
LISTING_BULK_REJECTED
|
||||||
|
USER_BANNED
|
||||||
|
USER_UNBANNED
|
||||||
|
USER_STATUS_UPDATED
|
||||||
|
KYC_APPROVED
|
||||||
|
KYC_REJECTED
|
||||||
|
SUBSCRIPTION_ADJUSTED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuditTargetType {
|
||||||
|
USER
|
||||||
|
LISTING
|
||||||
|
SUBSCRIPTION
|
||||||
|
}
|
||||||
|
|
||||||
|
model AdminAuditLog {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
action AdminAction
|
||||||
|
actorId String
|
||||||
|
targetId String
|
||||||
|
targetType AuditTargetType
|
||||||
|
metadata Json?
|
||||||
|
ipAddress String?
|
||||||
|
userAgent String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@index([actorId])
|
||||||
|
@@index([targetId, targetType])
|
||||||
|
@@index([action])
|
||||||
|
@@index([createdAt])
|
||||||
|
@@index([actorId, createdAt(sort: Desc)])
|
||||||
|
@@index([action, createdAt(sort: Desc)])
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// REVIEWS
|
// REVIEWS
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -588,7 +626,7 @@ model NotificationPreference {
|
|||||||
model Review {
|
model Review {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
targetType String
|
targetType String
|
||||||
targetId String
|
targetId String
|
||||||
rating Int
|
rating Int
|
||||||
|
|||||||
Reference in New Issue
Block a user