generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum Category { ELECTRONICS FURNITURE CLOTHING HOME_GARDEN SPORTS BOOKS GAMES VEHICLES OTHER } enum ListingCondition { NEW LIKE_NEW GENTLY_USED USED FAIR } enum ListingStatus { DRAFT PENDING_REVIEW ACTIVE SOLD DELETED } enum OfferStatus { PENDING ACCEPTED DECLINED COUNTERED CANCELLED EXPIRED } enum NotificationType { NEW_OFFER OFFER_ACCEPTED OFFER_DECLINED ITEM_SOLD NEW_MESSAGE ITEM_FAVORITED LISTING_APPROVED LISTING_REJECTED MODERATION_WARNING ACCOUNT_BANNED ACCOUNT_UNBANNED REPORT_RESOLVED BOOKING_REQUEST BOOKING_CONFIRMED BOOKING_REJECTED BOOKING_CANCELLED BOOKING_STARTED BOOKING_COMPLETED RENTAL_REVIEW PAYOUT_SENT PAYOUT_FAILED } enum PaymentStatus { PENDING COMPLETED FAILED REFUNDED } enum UserRole { USER MODERATOR ADMIN SUPER_ADMIN } enum ReportReason { SPAM INAPPROPRIATE SCAM COUNTERFEIT PROHIBITED_ITEM HARASSMENT OTHER } enum ReportStatus { OPEN REVIEWING RESOLVED DISMISSED } enum ReportTargetType { LISTING USER } enum SubscriptionTier { BASIC PRO BUSINESS } enum SubscriptionStatus { ACTIVE CANCELLED EXPIRED PAST_DUE } enum PaymentType { LISTING_FEE COMMISSION PROMOTION SUBSCRIPTION RENTAL_BOOKING RENTAL_COMMISSION RENTAL_DEPOSIT RENTAL_DEPOSIT_REFUND RENTAL_PAYOUT } enum ModerationAction { APPROVED REJECTED WARNING BAN UNBAN LISTING_DELETED LISTING_FEATURED } // ── Rental enums ────────────────────────────────────────────────── enum RentalCategory { APARTMENT HOUSE CAR MOTORCYCLE BICYCLE EBIKE } enum RentalPeriodType { DAILY MONTHLY } enum RentalListingStatus { DRAFT PENDING_REVIEW ACTIVE PAUSED DELETED } enum BookingStatus { PENDING CONFIRMED ACTIVE COMPLETED CANCELLED_BY_TENANT CANCELLED_BY_LANDLORD REJECTED EXPIRED } enum PayoutStatus { PENDING PROCESSING COMPLETED FAILED } enum CancellationPolicy { FLEXIBLE MODERATE STRICT } // ── Models ──────────────────────────────────────────────────────── model User { id String @id @default(cuid()) email String @unique passwordHash String fullName String nickname String? avatar String? phone String? location String? bio String? rating Float @default(0) ratingCount Int @default(0) role UserRole @default(USER) isBanned Boolean @default(false) banReason String? bannedAt DateTime? bannedBy String? showEmail Boolean @default(false) showPhone Boolean @default(true) showLocation Boolean @default(true) showOnline Boolean @default(true) showRating Boolean @default(true) twoFactorEnabled Boolean @default(false) isActive Boolean @default(true) notifNewOffer Boolean @default(true) notifMessages Boolean @default(true) notifItemSold Boolean @default(true) notifFavorites Boolean @default(true) notifEmail Boolean @default(true) marketingEmail Boolean @default(false) resetToken String? @unique resetTokenExpiry DateTime? isLandlord Boolean @default(false) landlordVerified Boolean @default(false) stripeAccountId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions Session[] listings Listing[] images ListingImage[] sentOffers Offer[] @relation("BuyerOffers") receivedOffers Offer[] @relation("SellerOffers") conversations1 Conversation[] @relation("User1Conversations") conversations2 Conversation[] @relation("User2Conversations") messages Message[] favorites Favorite[] notifications Notification[] payments Payment[] blockedUsers BlockedUser[] @relation("Blocker") blockedBy BlockedUser[] @relation("Blocked") reports Report[] subscription Subscription? promotedListings PromotedListing[] moderationLogs ModerationLog[] // Rental relations rentalListings RentalListing[] tenantBookings Booking[] @relation("TenantBookings") landlordBookings Booking[] @relation("LandlordBookings") payouts Payout[] rentalReviews RentalReview[] @relation("TenantReviews") landlordReviews RentalReview[] @relation("LandlordReviews") rentalFavorites RentalFavorite[] promotedRentals PromotedRental[] } model Session { id String @id @default(cuid()) userId String refreshToken String @unique userAgent String? ipAddress String? expiresAt DateTime createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) } model Listing { id String @id @default(cuid()) title String description String price Float obo Boolean @default(false) category Category condition ListingCondition status ListingStatus @default(DRAFT) location String viewCount Int @default(0) isFeatured Boolean @default(false) rejectionReason String? reviewedBy String? reviewedAt DateTime? sellerId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt seller User @relation(fields: [sellerId], references: [id], onDelete: Cascade) images ListingImage[] offers Offer[] conversations Conversation[] favorites Favorite[] payments Payment[] promotedListing PromotedListing? @@index([sellerId]) @@index([category]) @@index([status]) @@index([createdAt]) } model ListingImage { id String @id @default(cuid()) url String order Int @default(0) listingId String uploadedBy String listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade) user User @relation(fields: [uploadedBy], references: [id], onDelete: Cascade) @@index([listingId]) } model Offer { id String @id @default(cuid()) amount Float message String? status OfferStatus @default(PENDING) counterAmount Float? expiresAt DateTime? buyerId String sellerId String listingId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt buyer User @relation("BuyerOffers", fields: [buyerId], references: [id], onDelete: Cascade) seller User @relation("SellerOffers", fields: [sellerId], references: [id], onDelete: Cascade) listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade) @@index([buyerId]) @@index([sellerId]) @@index([listingId]) } model Conversation { id String @id @default(cuid()) user1Id String user2Id String listingId String? rentalListingId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user1 User @relation("User1Conversations", fields: [user1Id], references: [id], onDelete: Cascade) user2 User @relation("User2Conversations", fields: [user2Id], references: [id], onDelete: Cascade) listing Listing? @relation(fields: [listingId], references: [id], onDelete: SetNull) rentalListing RentalListing? @relation(fields: [rentalListingId], references: [id], onDelete: SetNull) messages Message[] @@unique([user1Id, user2Id, listingId]) @@index([user1Id]) @@index([user2Id]) } model Message { id String @id @default(cuid()) content String senderId String conversationId String isRead Boolean @default(false) offerAmount Float? createdAt DateTime @default(now()) sender User @relation(fields: [senderId], references: [id], onDelete: Cascade) conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) @@index([conversationId]) @@index([senderId]) } model Favorite { id String @id @default(cuid()) userId String listingId String 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 Notification { id String @id @default(cuid()) userId String type NotificationType 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]) @@index([createdAt]) } model Payment { id String @id @default(cuid()) userId String listingId String stripePaymentId String? @unique amount Float status PaymentStatus @default(PENDING) type PaymentType @default(LISTING_FEE) description String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade) @@index([userId]) @@index([listingId]) } model BlockedUser { id String @id @default(cuid()) blockerId String blockedId String createdAt DateTime @default(now()) blocker User @relation("Blocker", fields: [blockerId], references: [id], onDelete: Cascade) blocked User @relation("Blocked", fields: [blockedId], references: [id], onDelete: Cascade) @@unique([blockerId, blockedId]) } model Report { id String @id @default(cuid()) reporterId String targetType ReportTargetType targetId String reason ReportReason description String? status ReportStatus @default(OPEN) resolvedBy String? resolution String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt reporter User @relation(fields: [reporterId], references: [id], onDelete: Cascade) @@index([status]) @@index([targetType, targetId]) } model PlatformConfig { id String @id @default(cuid()) listingFee Float @default(5.00) commissionPercent Float @default(5.0) autoApprove Boolean @default(true) maxImagesPerListing Int @default(6) maxListingsFreeTier Int @default(5) proPrice Float @default(9.99) businessPrice Float @default(29.99) promotionDayPrice Float @default(2.99) blockedKeywords String[] @default([]) rentalCommissionPercent Float @default(10.0) rentalAutoApprove Boolean @default(false) maxRentalImagesPerListing Int @default(10) bookingExpiryHours Int @default(48) rentalPromotionDayPrice Float @default(3.99) updatedAt DateTime @updatedAt } model Subscription { id String @id @default(cuid()) userId String @unique tier SubscriptionTier @default(BASIC) status SubscriptionStatus @default(ACTIVE) stripeSubscriptionId String? @unique currentPeriodEnd DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model PromotedListing { id String @id @default(cuid()) listingId String @unique userId String startDate DateTime @default(now()) endDate DateTime amountPaid Float isActive 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([isActive, endDate]) } model ModerationLog { id String @id @default(cuid()) moderatorId String targetUserId String? targetListingId String? action ModerationAction reason String? details Json? createdAt DateTime @default(now()) moderator User @relation(fields: [moderatorId], references: [id], onDelete: Cascade) @@index([moderatorId]) @@index([createdAt]) } // ── Rental Models ───────────────────────────────────────────────── model RentalListing { id String @id @default(cuid()) title String description String category RentalCategory location String dailyPrice Float? monthlyPrice Float? depositAmount Float? details Json? amenities String[] @default([]) rules String[] @default([]) cancellationPolicy CancellationPolicy @default(FLEXIBLE) minDays Int? maxDays Int? minMonths Int? maxMonths Int? status RentalListingStatus @default(DRAFT) viewCount Int @default(0) isFeatured Boolean @default(false) isVerified Boolean @default(false) rejectionReason String? reviewedBy String? reviewedAt DateTime? landlordId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt landlord User @relation(fields: [landlordId], references: [id], onDelete: Cascade) images RentalImage[] availabilityBlocks AvailabilityBlock[] bookings Booking[] reviews RentalReview[] favorites RentalFavorite[] promotedRental PromotedRental? conversations Conversation[] @@index([landlordId]) @@index([category]) @@index([status]) @@index([createdAt]) } model RentalImage { id String @id @default(cuid()) url String order Int @default(0) rentalListingId String rentalListing RentalListing @relation(fields: [rentalListingId], references: [id], onDelete: Cascade) @@index([rentalListingId]) } model AvailabilityBlock { id String @id @default(cuid()) rentalListingId String startDate DateTime endDate DateTime isBlocked Boolean @default(true) reason String? createdAt DateTime @default(now()) rentalListing RentalListing @relation(fields: [rentalListingId], references: [id], onDelete: Cascade) @@index([rentalListingId]) @@index([startDate, endDate]) } model Booking { id String @id @default(cuid()) rentalListingId String tenantId String landlordId String periodType RentalPeriodType startDate DateTime endDate DateTime pricePerPeriod Float totalPeriods Int subtotal Float commissionRate Float @default(10.0) commissionAmount Float depositAmount Float @default(0) totalAmount Float status BookingStatus @default(PENDING) message String? rejectionReason String? cancellationReason String? stripePaymentIntentId String? expiresAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt rentalListing RentalListing @relation(fields: [rentalListingId], references: [id], onDelete: Cascade) tenant User @relation("TenantBookings", fields: [tenantId], references: [id], onDelete: Cascade) landlord User @relation("LandlordBookings", fields: [landlordId], references: [id], onDelete: Cascade) payments BookingPayment[] payout Payout? review RentalReview? @@index([rentalListingId]) @@index([tenantId]) @@index([landlordId]) @@index([status]) } model BookingPayment { id String @id @default(cuid()) bookingId String stripePaymentId String? @unique amount Float type PaymentType status PaymentStatus @default(PENDING) createdAt DateTime @default(now()) booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade) @@index([bookingId]) } model Payout { id String @id @default(cuid()) bookingId String @unique landlordId String grossAmount Float commissionAmount Float netAmount Float status PayoutStatus @default(PENDING) stripeTransferId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade) landlord User @relation(fields: [landlordId], references: [id], onDelete: Cascade) @@index([landlordId]) @@index([status]) } model RentalReview { id String @id @default(cuid()) bookingId String @unique rentalListingId String tenantId String landlordId String rating Int comment String? landlordResponse String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade) rentalListing RentalListing @relation(fields: [rentalListingId], references: [id], onDelete: Cascade) tenant User @relation("TenantReviews", fields: [tenantId], references: [id], onDelete: Cascade) landlord User @relation("LandlordReviews", fields: [landlordId], references: [id], onDelete: Cascade) @@index([rentalListingId]) @@index([landlordId]) } model RentalFavorite { id String @id @default(cuid()) userId String rentalListingId String createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) rentalListing RentalListing @relation(fields: [rentalListingId], references: [id], onDelete: Cascade) @@unique([userId, rentalListingId]) } model PromotedRental { id String @id @default(cuid()) rentalListingId String @unique userId String startDate DateTime @default(now()) endDate DateTime amountPaid Float isActive Boolean @default(true) createdAt DateTime @default(now()) rentalListing RentalListing @relation(fields: [rentalListingId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([isActive, endDate]) }