Files
moyosapp_beta.0.0.3/prisma/schema.prisma

286 lines
8.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// prisma/schema.prisma
datasource db {
provider = "postgresql"
}
generator client {
provider = "prisma-client-js"
}
model Guest {
id String @id @default(cuid())
name String
inviteCode String @unique @db.VarChar(6)
email String?
phone String?
maxPax Int @default(1) // singles default
dietaryRestrictions String?
isAttending Boolean?
rsvpTimestamp DateTime?
confirmationCode String?
// Seating
tableId String?
table Table? @relation(fields: [tableId], references: [id])
// Profile
relationship String? @default("Guest")
relationshipTo String? // "groom" or "bride"
title String?
surname String?
notes String?
avatar String?
bio String?
socialOptIn Boolean? @default(false) // Opt-in to appear in Who's Who directory
// Social Media (optional, with privacy controls)
facebook String?
instagram String?
linkedin String?
facebookPublic Boolean @default(false)
instagramPublic Boolean @default(false)
linkedinPublic Boolean @default(false)
// Contact Info Privacy Controls
emailPublic Boolean @default(false)
phonePublic Boolean @default(false)
// Analytics / geo
lastActive DateTime?
ipCountryCode String? @db.VarChar(2)
ipCity String?
ipData Json?
// Relations
songRequests Song[]
guestbookEntries GuestbookEntry[]
galleryPhotos GalleryPhoto[]
tributes Tribute[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([inviteCode])
@@index([isAttending])
@@index([tableId])
@@index([rsvpTimestamp])
}
model Song {
id String @id @default(cuid())
title String
artist String?
votes Int @default(0)
image String?
album String?
duration String?
appleMusicId String?
previewUrl String? // 3060s preview
trackUrl String? // Full track URL (Spotify, Apple Music, etc.)
guestId String
guest Guest @relation(fields: [guestId], references: [id], onDelete: Cascade)
votesList SongVote[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([guestId])
@@index([votes])
}
model SongVote {
id String @id @default(cuid())
songId String
song Song @relation(fields: [songId], references: [id], onDelete: Cascade)
guestId String? // Nullable for device-based votes
deviceId String? // Device UUID for unauthenticated votes
ipAddress String? // IP address for additional tracking
createdAt DateTime @default(now())
// Note: Unique constraints on nullable fields are handled in application logic
// Prisma doesn't support unique constraints with nullable fields directly
@@index([songId])
@@index([guestId])
@@index([deviceId])
@@index([songId, guestId])
@@index([songId, deviceId])
}
model GuestbookEntry {
id String @id @default(cuid())
guestId String
guest Guest @relation(fields: [guestId], references: [id], onDelete: Cascade)
guestName String
message String
photo String?
audioUrl String?
videoUrl String?
durationSeconds Int?
status String @default("active")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([guestId])
@@index([createdAt])
}
model GalleryPhoto {
id String @id @default(cuid())
guestId String
guest Guest @relation(fields: [guestId], references: [id], onDelete: Cascade)
url String
thumbnailUrl String?
caption String?
likes Int @default(0)
comments PhotoComment[]
status String @default("active")
width Int?
height Int?
bytes Int?
exif Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([guestId])
@@index([createdAt])
}
model PhotoComment {
id String @id @default(cuid())
photoId String
photo GalleryPhoto @relation(fields: [photoId], references: [id], onDelete: Cascade)
guestName String
message String
createdAt DateTime @default(now())
@@index([photoId])
}
model Table {
id String @id @default(cuid())
name String @unique
capacity Int
guests Guest[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Admin {
id String @id @default(cuid())
username String @unique
password String
email String?
totpSecret String? // Base32 encoded TOTP secret for Google Authenticator
totpEnabled Boolean @default(false) // Whether TOTP is enabled for this admin
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([username])
}
model ContentViolation {
id String @id @default(cuid())
deviceId String?
ipAddress String?
violationType String // copy, paste, screenshot, rightclick, selection, devtools
count Int @default(1)
guestId String? // If violation by authenticated guest
createdAt DateTime @default(now())
@@index([deviceId])
@@index([ipAddress])
@@index([guestId])
@@index([createdAt])
}
model BlockedDevice {
id String @id @default(cuid())
deviceId String?
ipAddress String?
guestId String? // If blocking authenticated guest
reason String?
durationHours Int? @default(24) // Block duration in hours
blockedAt DateTime @default(now())
expiresAt DateTime?
isActive Boolean @default(true)
@@index([deviceId])
@@index([ipAddress])
@@index([guestId])
@@index([isActive])
@@index([blockedAt])
}
model Tribute {
id String @id @default(cuid())
guestId String? // Optional - can be null for anonymous tributes
guest Guest? @relation(fields: [guestId], references: [id], onDelete: SetNull)
guestName String // Store guest name even if guest is deleted
dedication String // The tribute/dedication message
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([guestId])
@@index([createdAt])
@@index([guestName])
}
model Reminder {
id String @id @default(cuid())
name String
type String // "email" | "sms" | "both"
trigger String // "days_before" | "days_after" | "specific_date" | "rsvp_deadline"
daysBefore Int?
daysAfter Int?
specificDate DateTime?
targetAudience String // "all_guests" | "confirmed" | "pending" | "declined" | "no_rsvp"
templateId String?
customMessage String?
enabled Boolean @default(true)
lastSent DateTime?
nextSend DateTime?
sentCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([enabled])
@@index([nextSend])
@@index([createdAt])
}
model ABTest {
id String @id @default(cuid())
name String
type String // "email" | "sms"
status String @default("draft") // "draft" | "running" | "paused" | "completed"
variantA Json // { subject?, message, recipients, sent, opened?, clicked?, responded? }
variantB Json // { subject?, message, recipients, sent, opened?, clicked?, responded? }
splitRatio Float @default(50.0) // Percentage for variant A (0-100)
startDate DateTime?
endDate DateTime?
winner String? // "A" | "B" | null
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@index([createdAt])
}
model CouplesPlaylist {
id String @id @default(cuid())
title String
artist String?
trackUrl String? // Full track URL (Spotify, Apple Music, etc.)
previewUrl String? // 3060s preview
image String? // Album art
album String?
duration String?
order Int @default(0) // Order in playlist
revealAt Int? // Itinerary item index (0-based) when song should be revealed. null = reveal after final item
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([order])
@@index([revealAt])
}