571 lines
17 KiB
Plaintext
571 lines
17 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum TenantStatus {
|
|
ACTIVE
|
|
SUSPENDED
|
|
TRIAL
|
|
INACTIVE
|
|
}
|
|
|
|
enum UserStatus {
|
|
ACTIVE
|
|
INVITED
|
|
DISABLED
|
|
}
|
|
|
|
enum RoleCode {
|
|
SUPER_ADMIN
|
|
ADMIN_CLIENT
|
|
AGENT
|
|
}
|
|
|
|
enum ChannelStatus {
|
|
CONNECTED
|
|
DISCONNECTED
|
|
PENDING
|
|
ERROR
|
|
}
|
|
|
|
enum ConversationStatus {
|
|
OPEN
|
|
PENDING
|
|
RESOLVED
|
|
ARCHIVED
|
|
SPAM
|
|
}
|
|
|
|
enum ConversationPriority {
|
|
LOW
|
|
NORMAL
|
|
HIGH
|
|
URGENT
|
|
}
|
|
|
|
enum MessageDirection {
|
|
INBOUND
|
|
OUTBOUND
|
|
}
|
|
|
|
enum AuthTokenType {
|
|
PASSWORD_RESET
|
|
INVITE_ACCEPTANCE
|
|
}
|
|
|
|
enum MessageType {
|
|
TEXT
|
|
IMAGE
|
|
VIDEO
|
|
AUDIO
|
|
DOCUMENT
|
|
TEMPLATE
|
|
INTERACTIVE
|
|
}
|
|
|
|
enum DeliveryStatus {
|
|
QUEUED
|
|
SENT
|
|
DELIVERED
|
|
READ
|
|
FAILED
|
|
}
|
|
|
|
enum OptInStatus {
|
|
UNKNOWN
|
|
OPTED_IN
|
|
OPTED_OUT
|
|
}
|
|
|
|
enum TemplateApprovalStatus {
|
|
DRAFT
|
|
PENDING
|
|
APPROVED
|
|
REJECTED
|
|
DISABLED
|
|
}
|
|
|
|
enum CampaignStatus {
|
|
DRAFT
|
|
SCHEDULED
|
|
PROCESSING
|
|
COMPLETED
|
|
PARTIAL_FAILED
|
|
FAILED
|
|
CANCELED
|
|
}
|
|
|
|
enum CampaignAudienceType {
|
|
SEGMENT
|
|
IMPORT
|
|
MANUAL
|
|
}
|
|
|
|
enum CampaignType {
|
|
BROADCAST
|
|
BULK_FOLLOWUP
|
|
}
|
|
|
|
enum PaymentStatus {
|
|
UNPAID
|
|
PAID
|
|
OVERDUE
|
|
VOID
|
|
}
|
|
|
|
model SubscriptionPlan {
|
|
id String @id @default(cuid())
|
|
name String
|
|
code String @unique
|
|
priceMonthly Int
|
|
messageQuota Int
|
|
seatQuota Int
|
|
broadcastQuota Int
|
|
featuresJson String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenants Tenant[]
|
|
invoices BillingInvoice[]
|
|
}
|
|
|
|
model Tenant {
|
|
id String @id @default(cuid())
|
|
name String
|
|
slug String @unique
|
|
companyName String
|
|
timezone String
|
|
status TenantStatus @default(TRIAL)
|
|
planId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
plan SubscriptionPlan @relation(fields: [planId], references: [id])
|
|
users User[]
|
|
roles Role[]
|
|
channels Channel[]
|
|
contacts Contact[]
|
|
tags Tag[]
|
|
conversations Conversation[]
|
|
messages Message[]
|
|
notes ConversationNote[]
|
|
activities ConversationActivity[]
|
|
segments ContactSegment[]
|
|
templates MessageTemplate[]
|
|
campaigns BroadcastCampaign[]
|
|
auditLogs AuditLog[]
|
|
webhookEvents WebhookEvent[]
|
|
usageMetrics UsageMetric[]
|
|
billingInvoices BillingInvoice[]
|
|
}
|
|
|
|
model Role {
|
|
id String @id @default(cuid())
|
|
tenantId String?
|
|
name String
|
|
code RoleCode
|
|
permissionsJson String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant? @relation(fields: [tenantId], references: [id])
|
|
users User[]
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
fullName String
|
|
email String @unique
|
|
passwordHash String
|
|
roleId String
|
|
status UserStatus @default(INVITED)
|
|
avatarUrl String?
|
|
lastLoginAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
role Role @relation(fields: [roleId], references: [id])
|
|
assignedConversations Conversation[]
|
|
notes ConversationNote[]
|
|
sentMessages Message[]
|
|
activities ConversationActivity[] @relation("ActorActivities")
|
|
campaigns BroadcastCampaign[]
|
|
auditLogs AuditLog[]
|
|
}
|
|
|
|
model AuthToken {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
tenantId String
|
|
tokenType AuthTokenType
|
|
tokenHash String @unique
|
|
expiresAt DateTime
|
|
consumedAt DateTime?
|
|
createdByUser String?
|
|
createdAt DateTime @default(now())
|
|
metadataJson String?
|
|
|
|
@@index([userId, tokenType])
|
|
@@index([tenantId, tokenType, expiresAt])
|
|
}
|
|
|
|
model Channel {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
channelName String
|
|
provider String
|
|
wabaId String?
|
|
phoneNumberId String?
|
|
displayPhoneNumber String?
|
|
status ChannelStatus @default(PENDING)
|
|
webhookStatus String?
|
|
lastSyncAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
contacts Contact[]
|
|
conversations Conversation[]
|
|
messages Message[]
|
|
templates MessageTemplate[]
|
|
campaigns BroadcastCampaign[]
|
|
webhookEvents WebhookEvent[]
|
|
}
|
|
|
|
model Contact {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
channelId String?
|
|
externalRef String?
|
|
fullName String
|
|
phoneNumber String
|
|
email String?
|
|
avatarUrl String?
|
|
countryCode String?
|
|
optInStatus OptInStatus @default(UNKNOWN)
|
|
lastInteractionAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
channel Channel? @relation(fields: [channelId], references: [id])
|
|
conversations Conversation[]
|
|
messages Message[]
|
|
contactTags ContactTag[]
|
|
segmentMembers SegmentMember[]
|
|
campaignRecipients CampaignRecipient[]
|
|
|
|
@@unique([tenantId, phoneNumber])
|
|
}
|
|
|
|
model Tag {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
name String
|
|
color String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
contactTags ContactTag[]
|
|
conversationTags ConversationTag[]
|
|
|
|
@@unique([tenantId, name])
|
|
}
|
|
|
|
model ContactTag {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
contactId String
|
|
tagId String
|
|
createdAt DateTime @default(now())
|
|
contact Contact @relation(fields: [contactId], references: [id])
|
|
tag Tag @relation(fields: [tagId], references: [id])
|
|
|
|
@@unique([contactId, tagId])
|
|
}
|
|
|
|
model Conversation {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
channelId String
|
|
contactId String
|
|
subject String?
|
|
status ConversationStatus @default(OPEN)
|
|
priority ConversationPriority @default(NORMAL)
|
|
assignedUserId String?
|
|
firstMessageAt DateTime?
|
|
lastMessageAt DateTime?
|
|
lastInboundAt DateTime?
|
|
lastOutboundAt DateTime?
|
|
resolvedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
channel Channel @relation(fields: [channelId], references: [id])
|
|
contact Contact @relation(fields: [contactId], references: [id])
|
|
assignedUser User? @relation(fields: [assignedUserId], references: [id])
|
|
messages Message[]
|
|
notes ConversationNote[]
|
|
activities ConversationActivity[]
|
|
conversationTags ConversationTag[]
|
|
|
|
@@index([tenantId, status])
|
|
@@index([tenantId, assignedUserId])
|
|
@@index([tenantId, lastMessageAt])
|
|
}
|
|
|
|
model Message {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
conversationId String
|
|
channelId String
|
|
contactId String?
|
|
direction MessageDirection
|
|
type MessageType @default(TEXT)
|
|
providerMessageId String? @unique
|
|
replyToMessageId String?
|
|
contentText String?
|
|
mediaUrl String?
|
|
mimeType String?
|
|
deliveryStatus DeliveryStatus @default(QUEUED)
|
|
failedReason String?
|
|
sentByUserId String?
|
|
sentAt DateTime?
|
|
deliveredAt DateTime?
|
|
readAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
conversation Conversation @relation(fields: [conversationId], references: [id])
|
|
channel Channel @relation(fields: [channelId], references: [id])
|
|
contact Contact? @relation(fields: [contactId], references: [id])
|
|
sentByUser User? @relation(fields: [sentByUserId], references: [id])
|
|
}
|
|
|
|
model ConversationNote {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
conversationId String
|
|
userId String
|
|
content String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
conversation Conversation @relation(fields: [conversationId], references: [id])
|
|
user User @relation(fields: [userId], references: [id])
|
|
}
|
|
|
|
model ConversationTag {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
conversationId String
|
|
tagId String
|
|
createdAt DateTime @default(now())
|
|
conversation Conversation @relation(fields: [conversationId], references: [id])
|
|
tag Tag @relation(fields: [tagId], references: [id])
|
|
|
|
@@unique([conversationId, tagId])
|
|
}
|
|
|
|
model ConversationActivity {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
conversationId String
|
|
actorUserId String?
|
|
activityType String
|
|
metadataJson String?
|
|
createdAt DateTime @default(now())
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
conversation Conversation @relation(fields: [conversationId], references: [id])
|
|
actorUser User? @relation("ActorActivities", fields: [actorUserId], references: [id])
|
|
}
|
|
|
|
model ContactSegment {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
name String
|
|
description String?
|
|
rulesJson String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
members SegmentMember[]
|
|
campaigns BroadcastCampaign[]
|
|
}
|
|
|
|
model SegmentMember {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
segmentId String
|
|
contactId String
|
|
createdAt DateTime @default(now())
|
|
segment ContactSegment @relation(fields: [segmentId], references: [id])
|
|
contact Contact @relation(fields: [contactId], references: [id])
|
|
|
|
@@unique([segmentId, contactId])
|
|
}
|
|
|
|
model MessageTemplate {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
channelId String
|
|
name String
|
|
category String
|
|
languageCode String
|
|
templateType String?
|
|
bodyText String
|
|
headerType String?
|
|
footerText String?
|
|
buttonsJson String?
|
|
providerTemplateId String?
|
|
approvalStatus TemplateApprovalStatus @default(DRAFT)
|
|
rejectedReason String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
channel Channel @relation(fields: [channelId], references: [id])
|
|
campaigns BroadcastCampaign[]
|
|
}
|
|
|
|
model BroadcastCampaign {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
channelId String
|
|
templateId String
|
|
createdByUserId String
|
|
name String
|
|
campaignType CampaignType @default(BROADCAST)
|
|
audienceType CampaignAudienceType
|
|
segmentId String?
|
|
scheduledAt DateTime?
|
|
startedAt DateTime?
|
|
finishedAt DateTime?
|
|
status CampaignStatus @default(DRAFT)
|
|
totalRecipients Int @default(0)
|
|
totalSent Int @default(0)
|
|
totalDelivered Int @default(0)
|
|
totalRead Int @default(0)
|
|
totalFailed Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
channel Channel @relation(fields: [channelId], references: [id])
|
|
template MessageTemplate @relation(fields: [templateId], references: [id])
|
|
createdByUser User @relation(fields: [createdByUserId], references: [id])
|
|
segment ContactSegment? @relation(fields: [segmentId], references: [id])
|
|
recipients CampaignRecipient[]
|
|
|
|
@@index([tenantId, status])
|
|
@@index([status, scheduledAt])
|
|
}
|
|
|
|
model CampaignRecipient {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
campaignId String
|
|
contactId String
|
|
phoneNumber String
|
|
sendStatus DeliveryStatus @default(QUEUED)
|
|
sendAttempts Int @default(0)
|
|
maxSendAttempts Int @default(3)
|
|
lastAttemptAt DateTime?
|
|
nextRetryAt DateTime?
|
|
failureReason String?
|
|
providerMessageId String?
|
|
sentAt DateTime?
|
|
deliveredAt DateTime?
|
|
readAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
campaign BroadcastCampaign @relation(fields: [campaignId], references: [id])
|
|
contact Contact @relation(fields: [contactId], references: [id])
|
|
|
|
@@index([campaignId, sendStatus, nextRetryAt])
|
|
@@index([campaignId, sendStatus])
|
|
}
|
|
|
|
model BackgroundJobState {
|
|
id String @id @default(cuid())
|
|
jobName String @unique
|
|
lockedBy String
|
|
lockedUntil DateTime?
|
|
runs Int @default(0)
|
|
consecutiveFailures Int @default(0)
|
|
lastRunStartedAt DateTime?
|
|
lastRunCompletedAt DateTime?
|
|
lastRunStatus String?
|
|
lastRunSummaryJson String?
|
|
lastError String?
|
|
lastFailureAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model AuditLog {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
actorUserId String?
|
|
entityType String
|
|
entityId String
|
|
action String
|
|
metadataJson String?
|
|
ipAddress String?
|
|
userAgent String?
|
|
createdAt DateTime @default(now())
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
actorUser User? @relation(fields: [actorUserId], references: [id])
|
|
}
|
|
|
|
model WebhookEvent {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
channelId String?
|
|
eventType String
|
|
providerEventId String?
|
|
eventHash String?
|
|
payloadJson String
|
|
processStatus String
|
|
failedReason String?
|
|
createdAt DateTime @default(now())
|
|
processedAt DateTime?
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
channel Channel? @relation(fields: [channelId], references: [id])
|
|
|
|
@@index([tenantId, channelId, eventHash])
|
|
}
|
|
|
|
model UsageMetric {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
metricDate DateTime
|
|
inboundMessages Int @default(0)
|
|
outboundMessages Int @default(0)
|
|
activeContacts Int @default(0)
|
|
activeAgents Int @default(0)
|
|
broadcastSent Int @default(0)
|
|
storageUsedMb Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
|
|
@@unique([tenantId, metricDate])
|
|
}
|
|
|
|
model BillingInvoice {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
planId String
|
|
invoiceNumber String @unique
|
|
periodStart DateTime
|
|
periodEnd DateTime
|
|
subtotal Int
|
|
taxAmount Int
|
|
totalAmount Int
|
|
paymentStatus PaymentStatus @default(UNPAID)
|
|
dueDate DateTime
|
|
paidAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenant Tenant @relation(fields: [tenantId], references: [id])
|
|
plan SubscriptionPlan @relation(fields: [planId], references: [id])
|
|
}
|