Files
formbricks-formbricks/packages/database/schema.prisma

612 lines
19 KiB
Plaintext

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
// generator dbml {
// provider = "prisma-dbml-generator"
// }
generator zod {
provider = "zod-prisma"
output = "./zod"
imports = "./zod-utils"
relationModel = "default"
}
generator json {
provider = "prisma-json-types-generator"
}
enum PipelineTriggers {
responseCreated
responseUpdated
responseFinished
}
enum WebhookSource {
user
zapier
make
n8n
}
model Webhook {
id String @id @default(cuid())
name String?
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
url String
source WebhookSource @default(user)
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
environmentId String
triggers PipelineTriggers[]
surveyIds String[]
@@index([environmentId])
}
model Attribute {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
attributeClass AttributeClass @relation(fields: [attributeClassId], references: [id], onDelete: Cascade)
attributeClassId String
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
personId String
value String
@@unique([personId, attributeClassId])
}
enum AttributeType {
code
noCode
automatic
}
model AttributeClass {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
name String
description String?
archived Boolean @default(false)
type AttributeType
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
environmentId String
attributes Attribute[]
attributeFilters SurveyAttributeFilter[]
@@unique([name, environmentId])
@@index([environmentId, createdAt])
@@index([environmentId, archived])
}
model Person {
id String @id @default(cuid())
userId String
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
environmentId String
responses Response[]
attributes Attribute[]
displays Display[]
actions Action[]
@@unique([environmentId, userId])
@@index([environmentId])
}
model Response {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
finished Boolean @default(false)
survey Survey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
surveyId String
person Person? @relation(fields: [personId], references: [id], onDelete: Cascade)
personId String?
notes ResponseNote[]
/// @zod.custom(imports.ZResponseData)
/// [ResponseData]
data Json @default("{}")
/// @zod.custom(imports.ZResponseTtc)
/// [ResponseTtc]
ttc Json @default("{}")
/// @zod.custom(imports.ZResponseMeta)
/// [ResponseMeta]
meta Json @default("{}")
tags TagsOnResponses[]
/// @zod.custom(imports.ZResponsePersonAttributes)
/// [ResponsePersonAttributes]
personAttributes Json?
// singleUseId, used to prevent multiple responses
singleUseId String?
@@unique([surveyId, singleUseId])
@@index([surveyId, createdAt]) // to determine monthly response count
@@index([surveyId])
}
model ResponseNote {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
response Response @relation(fields: [responseId], references: [id], onDelete: Cascade)
responseId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
text String
isResolved Boolean @default(false)
isEdited Boolean @default(false)
@@index([responseId])
}
model Tag {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
name String
responses TagsOnResponses[]
environmentId String
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
@@unique([environmentId, name])
@@index([environmentId])
}
model TagsOnResponses {
responseId String
response Response @relation(fields: [responseId], references: [id], onDelete: Cascade)
tagId String
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@id([responseId, tagId])
@@index([responseId])
}
enum SurveyStatus {
draft
inProgress
paused
completed
}
enum DisplayStatus {
seen
responded
}
model Display {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
survey Survey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
surveyId String
person Person? @relation(fields: [personId], references: [id], onDelete: Cascade)
personId String?
responseId String? @unique
status DisplayStatus?
@@index([surveyId])
@@index([personId])
}
model SurveyTrigger {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
survey Survey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
surveyId String
actionClass ActionClass @relation(fields: [actionClassId], references: [id], onDelete: Cascade)
actionClassId String
@@unique([surveyId, actionClassId])
@@index([surveyId])
}
enum SurveyAttributeFilterCondition {
equals
notEquals
}
model SurveyAttributeFilter {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
attributeClass AttributeClass @relation(fields: [attributeClassId], references: [id], onDelete: Cascade)
attributeClassId String
survey Survey @relation(fields: [surveyId], references: [id], onDelete: Cascade)
surveyId String
condition SurveyAttributeFilterCondition
value String
@@unique([surveyId, attributeClassId])
@@index([surveyId])
@@index([attributeClassId])
}
enum SurveyType {
email
link
mobile
web
}
enum displayOptions {
displayOnce
displayMultiple
respondMultiple
}
model Survey {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
name String
redirectUrl String?
type SurveyType @default(web)
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
environmentId String
creator User? @relation(fields: [createdBy], references: [id])
createdBy String?
status SurveyStatus @default(draft)
/// @zod.custom(imports.ZSurveyWelcomeCard)
/// [SurveyWelcomeCard]
welcomeCard Json @default("{\"enabled\": false}")
/// @zod.custom(imports.ZSurveyQuestions)
/// [SurveyQuestions]
questions Json @default("[]")
/// @zod.custom(imports.ZSurveyThankYouCard)
/// [SurveyThankYouCard]
thankYouCard Json @default("{\"enabled\": false}")
/// @zod.custom(imports.ZSurveyHiddenFields)
/// [SurveyHiddenFields]
hiddenFields Json @default("{\"enabled\": false}")
responses Response[]
displayOption displayOptions @default(displayOnce)
recontactDays Int?
triggers SurveyTrigger[]
/// @zod.custom(imports.ZSurveyInlineTriggers)
/// [SurveyInlineTriggers]
inlineTriggers Json?
attributeFilters SurveyAttributeFilter[]
displays Display[]
autoClose Int?
delay Int @default(0)
autoComplete Int?
closeOnDate DateTime?
/// @zod.custom(imports.ZSurveyClosedMessage)
/// [SurveyClosedMessage]
surveyClosedMessage Json?
segmentId String?
segment Segment? @relation(fields: [segmentId], references: [id])
/// @zod.custom(imports.ZSurveyProductOverwrites)
/// [SurveyProductOverwrites]
productOverwrites Json?
/// @zod.custom(imports.ZSurveyStyling)
/// [SurveyStyling]
styling Json?
/// @zod.custom(imports.ZSurveySingleUse)
/// [SurveySingleUse]
singleUse Json? @default("{\"enabled\": false, \"isEncrypted\": true}")
/// @zod.custom(imports.ZSurveyVerifyEmail)
/// [SurveyVerifyEmail]
verifyEmail Json?
pin String?
resultShareKey String? @unique
displayPercentage Int?
@@index([environmentId])
}
enum ActionType {
code
noCode
automatic
}
model ActionClass {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
name String
description String?
type ActionType
/// @zod.custom(imports.ZActionClassNoCodeConfig)
/// [ActionClassNoCodeConfig]
noCodeConfig Json?
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
environmentId String
surveys SurveyTrigger[]
actions Action[]
@@unique([name, environmentId])
}
model Action {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
actionClass ActionClass @relation(fields: [actionClassId], references: [id], onDelete: Cascade)
actionClassId String
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
personId String
/// @zod.custom(imports.ZActionProperties)
/// @zod.custom(imports.ZActionProperties)
/// [ActionProperties]
properties Json @default("{}")
@@index([personId, actionClassId, createdAt])
@@index([actionClassId, createdAt])
@@index([personId])
}
enum EnvironmentType {
production
development
}
enum IntegrationType {
googleSheets
notion
airtable
}
model Integration {
id String @id @default(cuid())
type IntegrationType
environmentId String
/// @zod.custom(imports.ZIntegrationConfig)
/// [IntegrationConfig]
config Json
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
@@unique([type, environmentId])
@@index([environmentId])
}
model Environment {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
type EnvironmentType
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
productId String
widgetSetupCompleted Boolean @default(false)
surveys Survey[]
people Person[]
actionClasses ActionClass[]
attributeClasses AttributeClass[]
apiKeys ApiKey[]
webhooks Webhook[]
tags Tag[]
segments Segment[]
integration Integration[]
@@index([productId])
}
enum WidgetPlacement {
bottomLeft
bottomRight
topLeft
topRight
center
}
model Product {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
name String
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
teamId String
environments Environment[]
brandColor String @default("#64748b")
highlightBorderColor String?
/// @zod.custom(imports.ZStyling)
/// [Styling]
styling Json? @default("{\"unifiedStyling\":true,\"allowStyleOverwrite\":true,\"brandColor\":{\"light\":\"#64748b\"}}")
recontactDays Int @default(7)
linkSurveyBranding Boolean @default(true) // Determines if the survey branding should be displayed in link surveys
inAppSurveyBranding Boolean @default(true) // Determines if the survey branding should be displayed in in-app surveys
placement WidgetPlacement @default(bottomRight)
clickOutsideClose Boolean @default(true)
darkOverlay Boolean @default(false)
@@unique([teamId, name])
@@index([teamId])
}
model Team {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
name String
memberships Membership[]
products Product[]
/// @zod.custom(imports.ZTeamBilling)
/// [TeamBilling]
billing Json @default("{\"stripeCustomerId\": null, \"features\": {\"inAppSurvey\": {\"status\": \"inactive\", \"unlimited\": false}, \"linkSurvey\": {\"status\": \"inactive\", \"unlimited\": false}, \"userTargeting\": {\"status\": \"inactive\", \"unlimited\": false}}}")
invites Invite[]
}
enum MembershipRole {
owner
admin
editor
developer
viewer
}
model Membership {
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
teamId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
accepted Boolean @default(false)
role MembershipRole
@@id([userId, teamId])
@@index([userId])
@@index([teamId])
}
model Invite {
id String @id @default(uuid())
email String
name String?
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
teamId String
creator User @relation("inviteCreatedBy", fields: [creatorId], references: [id])
creatorId String
acceptor User? @relation("inviteAcceptedBy", fields: [acceptorId], references: [id], onDelete: Cascade)
acceptorId String?
accepted Boolean @default(false)
createdAt DateTime @default(now())
expiresAt DateTime
role MembershipRole @default(admin)
@@index([email, teamId])
@@index([teamId])
}
model ApiKey {
id String @id @unique @default(cuid())
createdAt DateTime @default(now())
lastUsedAt DateTime?
label String?
hashedKey String @unique()
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
environmentId String
@@index([environmentId])
}
enum IdentityProvider {
email
github
google
azuread
openid
}
model Account {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
type String
provider String
providerAccountId String
access_token String? @db.Text
refresh_token String? @db.Text
expires_at Int?
ext_expires_in Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
@@unique([provider, providerAccountId])
@@index([userId])
}
enum Role {
project_manager
engineer
founder
marketing_specialist
other
}
enum Objective {
increase_conversion
improve_user_retention
increase_user_adoption
sharpen_marketing_messaging
support_sales
other
}
enum Intention {
survey_user_segments
survey_at_specific_point_in_user_journey
enrich_customer_profiles
collect_all_user_feedback_on_one_platform
other
}
model User {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
name String?
email String @unique
emailVerified DateTime? @map(name: "email_verified")
imageUrl String?
twoFactorSecret String?
twoFactorEnabled Boolean @default(false)
backupCodes String?
password String?
onboardingCompleted Boolean @default(false)
identityProvider IdentityProvider @default(email)
identityProviderAccountId String?
memberships Membership[]
accounts Account[]
responseNotes ResponseNote[]
groupId String?
invitesCreated Invite[] @relation("inviteCreatedBy")
invitesAccepted Invite[] @relation("inviteAcceptedBy")
role Role?
objective Objective?
/// @zod.custom(imports.ZUserNotificationSettings)
/// @zod.custom(imports.ZUserNotificationSettings)
/// [UserNotificationSettings]
notificationSettings Json @default("{}")
surveys Survey[]
@@index([email])
}
model ShortUrl {
id String @id // generate nanoId in service
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
url String @unique
}
model Segment {
id String @id @default(cuid())
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
title String
description String?
isPrivate Boolean @default(true)
/// @zod.custom(imports.ZSegmentFilters)
/// [SegmentFilter]
filters Json @default("[]")
environmentId String
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
surveys Survey[]
@@unique([environmentId, title])
@@index([environmentId])
}