Files
simple-mail-cleaner/backend/prisma/schema.prisma
2026-01-23 14:39:23 +01:00

364 lines
8.1 KiB
Plaintext

generator client {
provider = "prisma-client-js"
engineType = "binary"
}
datasource db {
provider = "postgresql"
}
enum MailProvider {
GMAIL
GMX
WEBDE
}
enum UserRole {
USER
ADMIN
}
enum JobStatus {
QUEUED
RUNNING
SUCCEEDED
FAILED
CANCELED
}
enum RuleActionType {
MOVE
DELETE
ARCHIVE
LABEL
MARK_READ
MARK_UNREAD
}
enum RuleConditionType {
HEADER
HEADER_MISSING
SUBJECT
FROM
LIST_UNSUBSCRIBE
LIST_ID
UNSUBSCRIBE_STATUS
SCORE
}
enum ExportStatus {
QUEUED
RUNNING
DONE
FAILED
}
model Tenant {
id String @id @default(cuid())
name String
isActive Boolean @default(true)
exportJobs ExportJob[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
users User[]
mailboxAccounts MailboxAccount[]
rules Rule[]
jobs CleanupJob[]
metric TenantMetric?
providerMetrics TenantProviderMetric[]
}
model TenantMetric {
id String @id @default(cuid())
tenantId String @unique
avgProcessingRate Float?
sampleCount Int @default(0)
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
}
model TenantProviderMetric {
id String @id @default(cuid())
tenantId String
provider MailProvider
avgListingRate Float?
avgProcessingRate Float?
avgUnsubscribeRate Float?
avgRoutingRate Float?
avgListingSecondsPerMessage Float?
avgProcessingSecondsPerMessage Float?
avgUnsubscribeSecondsPerMessage Float?
avgRoutingSecondsPerMessage Float?
listingSampleCount Int @default(0)
processingSampleCount Int @default(0)
unsubscribeSampleCount Int @default(0)
routingSampleCount Int @default(0)
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
@@unique([tenantId, provider])
@@index([tenantId])
}
model ExportJob {
id String @id @default(cuid())
tenantId String
status ExportStatus @default(QUEUED)
format String
scope String
progress Int @default(0)
filePath String?
error String?
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
@@index([tenantId])
}
model User {
id String @id @default(cuid())
tenantId String
email String @unique
password String
role UserRole @default(USER)
isActive Boolean @default(true)
passwordResetRequired Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
}
model MailboxAccount {
id String @id @default(cuid())
tenantId String
email String
provider MailProvider
isActive Boolean @default(true)
imapHost String
imapPort Int
imapTLS Boolean
smtpHost String?
smtpPort Int?
smtpTLS Boolean?
oauthToken String?
oauthRefreshToken String?
oauthAccessToken String?
oauthExpiresAt DateTime?
providerUserId String?
oauthLastCheckedAt DateTime?
oauthLastErrorCode String?
appPassword String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
folders MailboxFolder[]
jobs CleanupJob[]
candidates CleanupJobCandidate[]
@@index([tenantId])
}
model MailboxFolder {
id String @id @default(cuid())
mailboxAccountId String
name String
remoteId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])
mailItems MailItem[]
@@index([mailboxAccountId])
}
model MailItem {
id String @id @default(cuid())
folderId String
messageId String
subject String?
from String?
receivedAt DateTime?
listId String?
listUnsubscribe String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
folder MailboxFolder @relation(fields: [folderId], references: [id])
@@index([folderId])
@@index([messageId])
}
model Rule {
id String @id @default(cuid())
tenantId String
name String
enabled Boolean @default(true)
matchMode RuleMatchMode @default(ALL)
position Int @default(0)
stopOnMatch Boolean @default(false)
phase RulePhase @default(POST_UNSUBSCRIBE)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
conditions RuleCondition[]
actions RuleAction[]
@@index([tenantId])
@@index([tenantId, position])
}
enum RuleMatchMode {
ALL
ANY
}
enum RulePhase {
PRE_UNSUBSCRIBE
POST_UNSUBSCRIBE
}
model RuleCondition {
id String @id @default(cuid())
ruleId String
type RuleConditionType
value String
rule Rule @relation(fields: [ruleId], references: [id])
@@index([ruleId])
}
model RuleAction {
id String @id @default(cuid())
ruleId String
type RuleActionType
target String?
rule Rule @relation(fields: [ruleId], references: [id])
@@index([ruleId])
}
model CleanupJob {
id String @id @default(cuid())
tenantId String
mailboxAccountId String
status JobStatus @default(QUEUED)
dryRun Boolean @default(true)
unsubscribeEnabled Boolean @default(true)
routingEnabled Boolean @default(true)
checkpoint Json?
checkpointUpdatedAt DateTime?
processedMessages Int?
totalMessages Int?
listingSeconds Int?
processingSeconds Int?
unsubscribeSeconds Int?
routingSeconds Int?
unsubscribeAttempts Int?
actionAttempts Int?
startedAt DateTime?
finishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenant Tenant @relation(fields: [tenantId], references: [id])
mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])
unsubscribeAttemptItems UnsubscribeAttempt[]
events CleanupJobEvent[]
candidates CleanupJobCandidate[]
@@index([tenantId])
@@index([mailboxAccountId])
}
model CleanupJobCandidate {
id String @id @default(cuid())
jobId String
mailboxAccountId String
provider MailProvider
externalId String
subject String?
from String?
fromDomain String?
receivedAt DateTime?
listId String?
listUnsubscribe String?
listUnsubscribePost String?
score Int
signals Json
actions Json?
unsubscribeStatus String?
unsubscribeMessage String?
unsubscribeTarget String?
unsubscribeDetails Json?
reviewed Boolean @default(false)
createdAt DateTime @default(now())
job CleanupJob @relation(fields: [jobId], references: [id])
mailboxAccount MailboxAccount @relation(fields: [mailboxAccountId], references: [id])
@@unique([jobId, externalId])
@@index([jobId])
@@index([jobId, fromDomain])
}
model UnsubscribeAttempt {
id String @id @default(cuid())
jobId String
mailItemId String?
dedupeKey String?
method String
target String
status String
createdAt DateTime @default(now())
job CleanupJob @relation(fields: [jobId], references: [id])
@@index([jobId])
@@unique([jobId, dedupeKey])
}
model UnsubscribeHistory {
id String @id @default(cuid())
tenantId String
dedupeKey String
target String
status String
createdAt DateTime @default(now())
@@unique([tenantId, dedupeKey])
@@index([tenantId])
}
model CleanupJobEvent {
id String @id @default(cuid())
jobId String
level String
message String
progress Int?
createdAt DateTime @default(now())
job CleanupJob @relation(fields: [jobId], references: [id])
@@index([jobId])
}
model AppSetting {
id String @id @default(cuid())
key String @unique
value String
updatedAt DateTime @updatedAt
}