warmbox/prisma/schema.prisma

266 lines
7.2 KiB
Text

generator client {
provider = "prisma-client"
output = "../apps/api/src/generated/prisma"
}
datasource db {
provider = "sqlite"
}
enum InboxStatus {
active
paused
error
}
enum WarmupStatus {
sent
rescued_from_spam
engaged_no_reply
replied
complete
}
enum MailboxRole {
unassigned
primary
satellite
}
enum MailboxProvider {
google_workspace
gmail
yahoo
outlook
custom
}
enum MailboxAuthType {
password
microsoft_oauth
}
enum CampaignRecipe {
grow
flat
randomized
custom
}
enum CampaignStatus {
draft
active
paused
maintenance
completed
failed
}
model Workspace {
id String @id @default(cuid())
name String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
inboxes Inbox[]
campaigns WarmupCampaign[]
}
enum SendJobStatus {
pending
processing
completed
failed
}
model SendJob {
id String @id @default(cuid())
campaignId String
scheduledAt DateTime
status SendJobStatus @default(pending)
idempotencyKey String @unique
processedAt DateTime?
errorMessage String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
campaign WarmupCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
@@index([campaignId, status, scheduledAt])
}
model JobRunLog {
id String @id @default(cuid())
jobName String
ranAt DateTime @default(now())
success Boolean @default(true)
message String?
@@index([jobName, ranAt])
}
model Inbox {
id String @id @default(cuid())
workspaceId String
email String
smtpHost String
smtpPort Int
imapHost String
imapPort Int
username String
password String
authType MailboxAuthType @default(password)
oauthRefreshToken String?
role MailboxRole @default(unassigned)
provider MailboxProvider @default(custom)
status InboxStatus @default(active)
dailyLimit Int @default(40)
currentRamp Int @default(5)
industry String @default("SaaS")
senderName String?
companyName String?
lastError String?
errorAt DateTime?
spamFolderPath String?
lastSpamRescuedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
sentLogs WarmupLog[] @relation("Sender")
receivedLogs WarmupLog[] @relation("Receiver")
primaryCampaigns WarmupCampaign[] @relation("PrimaryMailbox")
satelliteCampaigns CampaignSatellite[]
@@unique([workspaceId, email])
@@index([status])
@@index([workspaceId, role])
}
model WarmupCampaign {
id String @id @default(cuid())
workspaceId String
primaryMailboxId String
name String
recipe CampaignRecipe @default(grow)
status CampaignStatus @default(draft)
durationDays Int @default(30)
minEmailsPerDay Int @default(1)
maxEmailsPerDay Int @default(40)
replyRatePercent Int @default(30)
maintenanceVolumePercent Int @default(20)
timezone String @default("America/Vancouver")
sendWindowStart String @default("08:00")
sendWindowEnd String @default("18:00")
customSchedule String?
startedAt DateTime?
endsAt DateTime?
currentDay Int @default(0)
pausedFromStatus CampaignStatus?
lastSendAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
primaryMailbox Inbox @relation("PrimaryMailbox", fields: [primaryMailboxId], references: [id], onDelete: Cascade)
satellites CampaignSatellite[]
dailySchedules DailySchedule[]
dailyStats DailyStat[]
sendJobs SendJob[]
warmupLogs WarmupLog[]
@@index([workspaceId, status])
@@index([primaryMailboxId, status])
}
model CampaignSatellite {
id String @id @default(cuid())
campaignId String
satelliteMailboxId String
weight Int @default(1)
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
campaign WarmupCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
satelliteMailbox Inbox @relation(fields: [satelliteMailboxId], references: [id], onDelete: Cascade)
@@unique([campaignId, satelliteMailboxId])
@@index([satelliteMailboxId])
}
model DailySchedule {
id String @id @default(cuid())
campaignId String
dayIndex Int
date DateTime?
plannedSends Int
plannedReplies Int
actualSends Int @default(0)
actualReplies Int @default(0)
spamDetected Int @default(0)
spamRescued Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
campaign WarmupCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
@@unique([campaignId, dayIndex])
@@index([campaignId])
}
model DailyStat {
id String @id @default(cuid())
campaignId String
date DateTime
dayIndex Int
plannedSends Int
plannedReplies Int
actualSends Int @default(0)
actualReplies Int @default(0)
spamDetected Int @default(0)
spamRescued Int @default(0)
replyRateActual Float?
inboxPlacementRate Float?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
campaign WarmupCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
@@unique([campaignId, date])
@@index([campaignId])
}
model WarmupLog {
id String @id @default(cuid())
campaignId String?
senderId String
receiverId String
messageId String?
inReplyTo String?
subject String
body String
status WarmupStatus @default(sent)
placement String?
rescuedFromSpam Boolean @default(false)
validationResult String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
campaign WarmupCampaign? @relation(fields: [campaignId], references: [id], onDelete: SetNull)
sender Inbox @relation("Sender", fields: [senderId], references: [id], onDelete: Cascade)
receiver Inbox @relation("Receiver", fields: [receiverId], references: [id], onDelete: Cascade)
@@index([senderId, status])
@@index([receiverId, status])
@@index([messageId])
@@index([campaignId])
}
model Setting {
id String @id @default(cuid())
key String @unique
value String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}