Files
storybid/packages/server/prisma/schema.prisma
T
2026-05-02 19:46:42 -05:00

339 lines
12 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ── Organization ──────────────────────────────────────────────────────────────
model Organization {
id String @id @default(cuid())
name String
slug String @unique
logoUrl String?
primaryColor String?
stripeAccountId String?
publicUrl String?
localHostname String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
events AuctionEvent[]
bidders Bidder[]
staffUsers StaffUser[]
}
// ── Staff Users ───────────────────────────────────────────────────────────────
model StaffUser {
id String @id @default(cuid())
organizationId String
email String @unique
name String
role String // admin | event_manager | auctioneer | spotter | checkin_staff
passwordHash String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation(fields: [organizationId], references: [id])
auditLogs AuditLog[]
}
// ── Events ────────────────────────────────────────────────────────────────────
model AuctionEvent {
id String @id @default(cuid())
organizationId String
name String
slug String
description String?
venueAddress String?
startAt DateTime
endAt DateTime
status String @default("draft") // draft | published | active | closed | archived
timezone String @default("America/New_York")
bannerImageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation(fields: [organizationId], references: [id])
auctions Auction[]
bidders BidderEventEnrollment[]
invoices Invoice[]
donations Donation[]
paddleRaiseCampaigns PaddleRaiseCampaign[]
auditLogs AuditLog[]
@@unique([organizationId, slug])
}
// ── Auctions ──────────────────────────────────────────────────────────────────
model Auction {
id String @id @default(cuid())
eventId String
type String // live | silent
name String
status String @default("draft") // draft | active | paused | closed
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event AuctionEvent @relation(fields: [eventId], references: [id])
items AuctionItem[]
silentWindows SilentAuctionWindow[]
}
// ── Auction Items ─────────────────────────────────────────────────────────────
model AuctionItem {
id String @id @default(cuid())
auctionId String
lotNumber String
title String
description String?
donorName String?
category String?
fairMarketValue Decimal?
openingBid Decimal @default(0)
reservePrice Decimal?
currentHighBid Decimal?
currentHighBidderId String?
bidIncrement Decimal @default(10)
state String @default("preview") // preview | active | going_once | going_twice | sold | passed | closed
pickupNotes String?
sortOrder Int @default(0)
silentWindowId String?
softCloseEnabled Boolean @default(false)
softCloseExtendMinutes Int @default(2)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
auction Auction @relation(fields: [auctionId], references: [id])
silentWindow SilentAuctionWindow? @relation(fields: [silentWindowId], references: [id])
currentHighBidder Bidder? @relation("CurrentHighBids", fields: [currentHighBidderId], references: [id])
media ItemMedia[]
bids Bid[]
@@unique([auctionId, lotNumber])
}
model ItemMedia {
id String @id @default(cuid())
itemId String
mediaType String // image | video | document | embed
url String
thumbnailUrl String?
caption String?
sortOrder Int @default(0)
createdAt DateTime @default(now())
item AuctionItem @relation(fields: [itemId], references: [id], onDelete: Cascade)
}
model SilentAuctionWindow {
id String @id @default(cuid())
auctionId String
name String
opensAt DateTime
closesAt DateTime
softCloseEnabled Boolean @default(false)
softCloseExtendMinutes Int @default(2)
status String @default("pending") // pending | open | closed
auction Auction @relation(fields: [auctionId], references: [id])
items AuctionItem[]
}
// ── Bidders ───────────────────────────────────────────────────────────────────
model Bidder {
id String @id @default(cuid())
organizationId String
email String?
phone String?
firstName String
lastName String
paymentMethodOnFile Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation(fields: [organizationId], references: [id])
authMethods BidderAuthMethod[]
eventEnrollments BidderEventEnrollment[]
bids Bid[]
currentHighBids AuctionItem[] @relation("CurrentHighBids")
invoices Invoice[]
donations Donation[]
deviceSessions DeviceSession[]
notifications Notification[]
}
model BidderAuthMethod {
id String @id @default(cuid())
bidderId String
type String // email_magic_link | sms_otp
identifier String
verifiedAt DateTime?
createdAt DateTime @default(now())
bidder Bidder @relation(fields: [bidderId], references: [id], onDelete: Cascade)
@@unique([type, identifier])
}
model BidderEventEnrollment {
id String @id @default(cuid())
bidderId String
eventId String
paddleNumber String?
tableAssignment String?
notes String?
checkInStatus String @default("pending") // pending | checked_in
checkInAt DateTime?
createdAt DateTime @default(now())
bidder Bidder @relation(fields: [bidderId], references: [id])
event AuctionEvent @relation(fields: [eventId], references: [id])
@@unique([bidderId, eventId])
@@unique([eventId, paddleNumber])
}
// ── Bids ──────────────────────────────────────────────────────────────────────
model Bid {
id String @id @default(cuid())
itemId String
bidderId String
amount Decimal
clientCreatedAt DateTime
serverReceivedAt DateTime @default(now())
originMode String // public | local_dns | local_ip | offline_queue
syncStatus String @default("synced") // synced | pending | conflict | rejected
deviceId String
clientSeq Int
isWinning Boolean @default(false)
createdAt DateTime @default(now())
item AuctionItem @relation(fields: [itemId], references: [id])
bidder Bidder @relation(fields: [bidderId], references: [id])
@@index([itemId, createdAt])
@@index([bidderId])
}
// ── Paddle Raise & Donations ──────────────────────────────────────────────────
model PaddleRaiseCampaign {
id String @id @default(cuid())
eventId String
name String
goal Decimal?
totalRaised Decimal @default(0)
tiers Json @default("[]") // number[]
isActive Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
event AuctionEvent @relation(fields: [eventId], references: [id])
donations Donation[]
}
model Donation {
id String @id @default(cuid())
eventId String
bidderId String?
campaignId String?
amount Decimal
anonymous Boolean @default(false)
stripePaymentIntentId String?
createdAt DateTime @default(now())
event AuctionEvent @relation(fields: [eventId], references: [id])
bidder Bidder? @relation(fields: [bidderId], references: [id])
campaign PaddleRaiseCampaign? @relation(fields: [campaignId], references: [id])
}
// ── Invoices & Payments ───────────────────────────────────────────────────────
model Invoice {
id String @id @default(cuid())
bidderId String
eventId String
stripeInvoiceId String?
totalAmount Decimal @default(0)
paidAmount Decimal @default(0)
status String @default("draft") // draft | open | paid | partially_paid | void
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bidder Bidder @relation(fields: [bidderId], references: [id])
event AuctionEvent @relation(fields: [eventId], references: [id])
payments Payment[]
}
model Payment {
id String @id @default(cuid())
invoiceId String
stripePaymentIntentId String?
amount Decimal
currency String @default("usd")
status String // pending | succeeded | failed | refunded
createdAt DateTime @default(now())
invoice Invoice @relation(fields: [invoiceId], references: [id])
}
// ── Device Sessions ───────────────────────────────────────────────────────────
model DeviceSession {
id String @id @default(cuid())
bidderId String
deviceId String @unique
userAgent String?
lastSeenAt DateTime @default(now())
createdAt DateTime @default(now())
bidder Bidder @relation(fields: [bidderId], references: [id])
}
// ── Audit Log ─────────────────────────────────────────────────────────────────
model AuditLog {
id String @id @default(cuid())
eventId String?
staffUserId String?
action String
entityType String
entityId String
payload Json?
originMode String? // mirrors bid origin when relevant
ipAddress String?
createdAt DateTime @default(now())
event AuctionEvent? @relation(fields: [eventId], references: [id])
staffUser StaffUser? @relation(fields: [staffUserId], references: [id])
@@index([eventId, createdAt])
@@index([entityType, entityId])
}
// ── Notifications ─────────────────────────────────────────────────────────────
model Notification {
id String @id @default(cuid())
bidderId String
type String // outbid | item_closed | checkout_ready | otp | receipt
channel String // in_app | push | email | sms
payload Json
sentAt DateTime?
readAt DateTime?
createdAt DateTime @default(now())
bidder Bidder @relation(fields: [bidderId], references: [id])
}