335 lines
13 KiB
Plaintext
335 lines
13 KiB
Plaintext
model User {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
email String @unique
|
|
passwordHash String?
|
|
firstName String
|
|
lastName String
|
|
avatar String?
|
|
phone String?
|
|
defaultTenantId String?
|
|
verifyToken String?
|
|
verifyTokenCreatedAt DateTime?
|
|
locale String?
|
|
lightOrDarkMode String?
|
|
totpSecretEncrypted String?
|
|
totpPendingSecretEncrypted String?
|
|
totpEnabledAt DateTime?
|
|
totpPendingCreatedAt DateTime?
|
|
mfaRecoveryCodeHashes Json?
|
|
mfaRecoveryCodesGeneratedAt DateTime?
|
|
mfaRecoveryCodeLastUsedAt DateTime?
|
|
active Boolean @default(false)
|
|
// Phase 10 Task #68 — DSGVO bulk-ops self-service flags.
|
|
// `pendingDeletion` is set the moment the user confirms a delete request via
|
|
// the email-verification token; `scheduledDeletionAt` carries the 30-day
|
|
// grace deadline. `pseudonymized` is flipped permanently once the cascade
|
|
// job has rewritten PII to deterministic pseudonyms (Art. 17 alternative
|
|
// path for retention-bound rows such as AuditEvents). `deletionRequestId`
|
|
// is the idempotency token shared with the worker job + audit trail.
|
|
pendingDeletion Boolean @default(false)
|
|
scheduledDeletionAt DateTime?
|
|
deletionRequestedAt DateTime?
|
|
pseudonymized Boolean @default(false)
|
|
pseudonymizedAt DateTime?
|
|
deletionRequestId String? @unique
|
|
admin AdminUser? // Has access to the admin panel
|
|
createdApiKeys ApiKey[]
|
|
createdGroups Group[]
|
|
groups GroupUser[]
|
|
logs Log[]
|
|
createdRows Row[]
|
|
createdRowComments RowComment[]
|
|
createdRowCommentReactions RowCommentReaction[]
|
|
createdEntityViews EntityView[] @relation("createdByUser")
|
|
rowPermissions RowPermission[]
|
|
assignedTasks RowTask[] @relation("assignedToUser")
|
|
completedTasks RowTask[] @relation("completedByUser")
|
|
createdRowTasks RowTask[] @relation("createdByUser")
|
|
tenants TenantUser[]
|
|
invitation TenantUserInvitation? @relation("createdUser")
|
|
sentInvitations TenantUserInvitation[] @relation("fromUser")
|
|
acceptedInvitations TenantUserInvitation[] @relation("acceptedByUser")
|
|
revokedInvitations TenantUserInvitation[] @relation("revokedByUser")
|
|
roles UserRole[]
|
|
readEmails EmailRead[]
|
|
entityViews EntityView[]
|
|
onboardingSessions OnboardingSession[]
|
|
tenantIpAddresses TenantIpAddress[]
|
|
analyticsUniqueSessions AnalyticsUniqueVisitor[]
|
|
metricLogs MetricLog[]
|
|
formulaLogs FormulaLog[]
|
|
knowledgeBaseArticles KnowledgeBaseArticle[]
|
|
events Event[]
|
|
workflowsCreated Workflow[] @relation("createdByUser")
|
|
workflowsExecuted WorkflowExecution[] @relation("executedByUser")
|
|
workflowVariablesCreated WorkflowVariable[]
|
|
workflowCredentialsCreated WorkflowCredential[]
|
|
feedback Feedback[]
|
|
supportTickets SupportTicket[]
|
|
credits Credit[]
|
|
portals Portal[]
|
|
impersonationAuditsActor ImpersonationAudit[] @relation("ImpersonationAuditActor")
|
|
impersonationAuditsTarget ImpersonationAudit[] @relation("ImpersonationAuditTarget")
|
|
notificationDeliveries NotificationDelivery[]
|
|
userSessions UserSession[]
|
|
trustedDevices TrustedDevice[]
|
|
notificationPreferences UserNotificationPreference[]
|
|
deviceTokens DeviceToken[]
|
|
magicLinkTokens MagicLinkToken[]
|
|
|
|
appsInstalled App[] @relation("appInstalledByUser")
|
|
appSyncRuns AppSyncRun[] @relation("appSyncRunCreatedByUser")
|
|
|
|
sitesCreated Site[] @relation("sitesCreatedByUser")
|
|
|
|
// Phase 10 Task #68 — grace-period cron query scans pendingDeletion users
|
|
// whose deadline has elapsed. Index keeps the cron O(log n).
|
|
@@index([pendingDeletion, scheduledDeletionAt], name: "user_pending_deletion_due_idx")
|
|
}
|
|
|
|
model MagicLinkToken {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
userId String
|
|
tokenHash String @unique
|
|
expiresAt DateTime
|
|
consumedAt DateTime?
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId, expiresAt])
|
|
@@index([expiresAt, consumedAt])
|
|
}
|
|
|
|
// Has access to the admin panel
|
|
model AdminUser {
|
|
userId String @id @unique
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
// Phase 09 #82 — OIDC SSO link to external IdP (Authentik).
|
|
// ssoProvider / ssoSubjectId together identify the federated identity
|
|
// (typically `authentik` + the IdP-stable `sub` claim). Both nullable so
|
|
// legacy bootstrap admins predating SSO continue to function.
|
|
ssoProvider String?
|
|
ssoSubjectId String?
|
|
ssoGroups String[] @default([])
|
|
lastSsoLoginAt DateTime?
|
|
|
|
@@unique([ssoProvider, ssoSubjectId])
|
|
@@index([ssoSubjectId])
|
|
}
|
|
|
|
model Tenant {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
slug String @unique
|
|
name String
|
|
icon String?
|
|
logo String?
|
|
subscriptionId String?
|
|
active Boolean @default(false)
|
|
deactivatedReason String?
|
|
apiKeys ApiKey[]
|
|
apiKeyLogs ApiKeyLog[]
|
|
groups Group[]
|
|
logs Log[]
|
|
rows Row[]
|
|
rowPermissions RowPermission[]
|
|
subscription TenantSubscription?
|
|
productConfiguration TenantProductConfiguration?
|
|
featureOverrides TenantFeatureOverride[]
|
|
users TenantUser[]
|
|
roles Role[]
|
|
invitations TenantUserInvitation[]
|
|
userRoles UserRole[]
|
|
inboundAddresses TenantInboundAddress[]
|
|
events Event[]
|
|
fromRegistration Registration?
|
|
entityViews EntityView[]
|
|
emailSenders EmailSender[]
|
|
campaigns Campaign[]
|
|
outboundEmails OutboundEmail[]
|
|
onboardingSessions OnboardingSession[]
|
|
ipAddresses TenantIpAddress[]
|
|
tenantSettingsRow TenantSettingsRow?
|
|
metricLogs MetricLog[]
|
|
formulaLogs FormulaLog[]
|
|
knowledgeBases KnowledgeBase[]
|
|
types TenantType[]
|
|
entityProperties Property[]
|
|
entityGroupConfigurations EntityGroupConfiguration[]
|
|
entityTemplates EntityTemplate[]
|
|
workflows Workflow[]
|
|
workflowExecutions WorkflowExecution[]
|
|
workflowVariables WorkflowVariable[]
|
|
workflowCredentials WorkflowCredential[]
|
|
feedback Feedback[]
|
|
supportTickets SupportTicket[]
|
|
credits Credit[]
|
|
portals Portal[]
|
|
portalsUsers PortalUser[]
|
|
widgets Widget[]
|
|
impersonationAudits ImpersonationAudit[]
|
|
notificationDeliveries NotificationDelivery[]
|
|
notificationPreferences UserNotificationPreference[]
|
|
deviceTokens DeviceToken[]
|
|
authProviders WorkspaceAuthProvider[]
|
|
|
|
theme String?
|
|
themeConfig String?
|
|
|
|
apps App[]
|
|
dataConnections DataConnection[]
|
|
appSyncRuns AppSyncRun[]
|
|
|
|
sites Site[]
|
|
|
|
forms Form[]
|
|
|
|
@@index([slug])
|
|
}
|
|
|
|
model WorkspaceAuthProvider {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenantId String
|
|
kind String
|
|
name String
|
|
slug String
|
|
enabled Boolean @default(false)
|
|
issuer String?
|
|
authorizationUrl String?
|
|
tokenUrl String?
|
|
jwksUrl String?
|
|
clientId String?
|
|
entityId String?
|
|
ssoUrl String?
|
|
certificateFingerprint String?
|
|
metadataUpdatedAt DateTime?
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([tenantId, slug])
|
|
@@index([tenantId, kind, enabled])
|
|
@@index([tenantId, updatedAt])
|
|
}
|
|
|
|
model ImpersonationAudit {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
actorUserId String?
|
|
targetUserId String?
|
|
tenantId String?
|
|
action String
|
|
reason String?
|
|
requestPath String
|
|
metadata String?
|
|
actor User? @relation("ImpersonationAuditActor", fields: [actorUserId], references: [id], onDelete: SetNull)
|
|
target User? @relation("ImpersonationAuditTarget", fields: [targetUserId], references: [id], onDelete: SetNull)
|
|
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([actorUserId])
|
|
@@index([targetUserId])
|
|
@@index([tenantId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model TenantUser {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
tenantId String
|
|
userId String
|
|
type Int
|
|
joined Int
|
|
status Int
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model TenantUserInvitation {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
email String
|
|
firstName String
|
|
lastName String
|
|
type Int
|
|
pending Boolean
|
|
status String @default("pending")
|
|
tokenHash String?
|
|
tokenPrefix String?
|
|
expiresAt DateTime?
|
|
acceptedAt DateTime?
|
|
revokedAt DateTime?
|
|
createdUserId String? @unique
|
|
fromUserId String?
|
|
acceptedByUserId String?
|
|
revokedByUserId String?
|
|
fromUser User? @relation(name: "fromUser", fields: [fromUserId], references: [id])
|
|
user User? @relation(name: "createdUser", fields: [createdUserId], references: [id])
|
|
acceptedByUser User? @relation(name: "acceptedByUser", fields: [acceptedByUserId], references: [id])
|
|
revokedByUser User? @relation(name: "revokedByUser", fields: [revokedByUserId], references: [id])
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([tenantId, status, expiresAt])
|
|
@@index([tenantId, email, status])
|
|
@@index([tenantId, tokenPrefix])
|
|
@@index([acceptedByUserId])
|
|
@@index([revokedByUserId])
|
|
}
|
|
|
|
model Registration {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
email String @unique
|
|
firstName String
|
|
lastName String
|
|
slug String?
|
|
token String @unique
|
|
ipAddress String?
|
|
company String?
|
|
selectedSubscriptionPriceId String?
|
|
leadConversionLeadId String?
|
|
leadConversionToken String?
|
|
leadConversionPlanId String?
|
|
createdTenantId String? @unique
|
|
createdTenant Tenant? @relation(fields: [createdTenantId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model Blacklist {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
type String // email, domain, ip
|
|
value String
|
|
active Boolean @default(true)
|
|
registerAttempts Int @default(0)
|
|
}
|
|
|
|
model TenantIpAddress {
|
|
id String @id @default(cuid())
|
|
tenantId String
|
|
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
userId String?
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
apiKeyId String?
|
|
apiKey ApiKey? @relation(fields: [apiKeyId], references: [id], onDelete: Cascade)
|
|
ip String
|
|
fromUrl String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([tenantId, ip, userId, apiKeyId])
|
|
}
|
|
|
|
model TenantType {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
title String @unique
|
|
titlePlural String
|
|
description String?
|
|
isDefault Boolean @default(false)
|
|
tenants Tenant[]
|
|
subscriptionProducts SubscriptionProduct[]
|
|
}
|