// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" previewFeatures = ["tracing", "relationJoins"] } datasource db { provider = "postgresql" url = env("STACK_DATABASE_CONNECTION_STRING") directUrl = env("STACK_DIRECT_DATABASE_CONNECTION_STRING") } model Project { // Note that the project with ID `internal` is handled as a special case. id String @id createdAt DateTime @default(now()) updatedAt DateTime @updatedAt displayName String description String? @default("") configId String @db.Uuid config ProjectConfig @relation(fields: [configId], references: [id], onDelete: Cascade) configOverride ProjectConfigOverride? isProductionMode Boolean users ProjectUser[] @relation("ProjectUsers") teams Team[] apiKeySets ApiKeySet[] authMethods AuthMethod[] contactChannels ContactChannel[] connectedAccounts ConnectedAccount[] neonProvisionedProject NeonProvisionedProject? } // Contains all the configuration for a project. // More specifically, "configuration" is what we call those settings that only depend on environment variables and overrides between different deployments. model ProjectConfig { id String @id @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt allowLocalhost Boolean signUpEnabled Boolean @default(true) createTeamOnSignUp Boolean clientTeamCreationEnabled Boolean clientUserDeletionEnabled Boolean @default(false) // TODO: remove this after moving everyone to project specific JWTs legacyGlobalJwtSigning Boolean @default(false) teamCreateDefaultSystemPermissions TeamSystemPermission[] teamMemberDefaultSystemPermissions TeamSystemPermission[] projects Project[] oauthProviderConfigs OAuthProviderConfig[] emailServiceConfig EmailServiceConfig? domains ProjectDomain[] permissions Permission[] authMethodConfigs AuthMethodConfig[] connectedAccountConfigs ConnectedAccountConfig[] } model ProjectDomain { projectConfigId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt domain String handlerPath String projectConfig ProjectConfig @relation(fields: [projectConfigId], references: [id], onDelete: Cascade) @@unique([projectConfigId, domain]) } // Environment-specific overrides for a configuration. // // This is a quick and dirty way to allow for environment-specific overrides of the configuration. // // For most cases, you should prefer to use environment variables. // // Note: Overrides (and environment variables) are currently unimplemented, so this model is empty. model ProjectConfigOverride { projectId String @id createdAt DateTime @default(now()) updatedAt DateTime @updatedAt project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) } model Team { projectId String teamId String @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt displayName String profileImageUrl String? clientMetadata Json? clientReadOnlyMetadata Json? serverMetadata Json? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) permissions Permission[] teamMembers TeamMember[] @@id([projectId, teamId]) } // This is used for fields that are boolean but only the true value is part of a unique constraint. // For example if you want to allow only one selected team per user, you can make an optional field with this type and add a unique constraint. // Only the true value is considered for the unique constraint, the null value is not. enum BooleanTrue { TRUE } model TeamMember { projectId String projectUserId String @db.Uuid teamId String @db.Uuid // This will override the displayName of the user in this team. displayName String? // This will override the profileImageUrl of the user in this team. profileImageUrl String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) team Team @relation(fields: [projectId, teamId], references: [projectId, teamId], onDelete: Cascade) isSelected BooleanTrue? directPermissions TeamMemberDirectPermission[] @@id([projectId, projectUserId, teamId]) @@unique([projectId, projectUserId, isSelected]) } model TeamMemberDirectPermission { id String @id @default(uuid()) @db.Uuid projectId String projectUserId String @db.Uuid teamId String @db.Uuid permissionDbId String? @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt teamMember TeamMember @relation(fields: [projectId, projectUserId, teamId], references: [projectId, projectUserId, teamId], onDelete: Cascade) // exactly one of [permissionId && permission] or [systemPermission] must be set permission Permission? @relation(fields: [permissionDbId], references: [dbId], onDelete: Cascade) systemPermission TeamSystemPermission? @@unique([projectId, projectUserId, teamId, permissionDbId]) @@unique([projectId, projectUserId, teamId, systemPermission]) } model Permission { // The ID of this permission, as is chosen by and exposed to the user. It is different from the database ID, which is randomly generated and only used internally. queryableId String // The database ID of this permission. This is never exposed to any client and is only used to make sure the database has an ID column. dbId String @id @default(uuid()) @db.Uuid // exactly one of [projectConfigId && projectConfig] or [projectId && teamId && team] must be set projectConfigId String? @db.Uuid projectId String? teamId String? @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt description String? // The scope of the permission. If projectConfigId is set, may be GLOBAL or TEAM; if teamId is set, must be TEAM. scope PermissionScope projectConfig ProjectConfig? @relation(fields: [projectConfigId], references: [id], onDelete: Cascade) team Team? @relation(fields: [projectId, teamId], references: [projectId, teamId], onDelete: Cascade) parentEdges PermissionEdge[] @relation("ChildPermission") childEdges PermissionEdge[] @relation("ParentPermission") teamMemberDirectPermission TeamMemberDirectPermission[] isDefaultTeamCreatorPermission Boolean @default(false) isDefaultTeamMemberPermission Boolean @default(false) @@unique([projectConfigId, queryableId]) @@unique([projectId, teamId, queryableId]) } enum PermissionScope { GLOBAL TEAM } enum TeamSystemPermission { UPDATE_TEAM DELETE_TEAM READ_MEMBERS REMOVE_MEMBERS INVITE_MEMBERS } model PermissionEdge { edgeId String @id @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // exactly one of [parentPermissionDbId && parentPermission] or [parentTeamSystemPermission] must be set parentPermissionDbId String? @db.Uuid parentPermission Permission? @relation("ParentPermission", fields: [parentPermissionDbId], references: [dbId], onDelete: Cascade) parentTeamSystemPermission TeamSystemPermission? childPermissionDbId String @db.Uuid childPermission Permission @relation("ChildPermission", fields: [childPermissionDbId], references: [dbId], onDelete: Cascade) } model ProjectUser { projectId String projectUserId String @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt profileImageUrl String? displayName String? serverMetadata Json? clientReadOnlyMetadata Json? clientMetadata Json? requiresTotpMfa Boolean @default(false) totpSecret Bytes? project Project @relation("ProjectUsers", fields: [projectId], references: [id], onDelete: Cascade) projectUserRefreshTokens ProjectUserRefreshToken[] projectUserAuthorizationCodes ProjectUserAuthorizationCode[] projectUserOAuthAccounts ProjectUserOAuthAccount[] teamMembers TeamMember[] contactChannels ContactChannel[] authMethods AuthMethod[] connectedAccounts ConnectedAccount[] // some backlinks for the unique constraints on some auth methods passwordAuthMethod PasswordAuthMethod[] passkeyAuthMethod PasskeyAuthMethod[] otpAuthMethod OtpAuthMethod[] oauthAuthMethod OAuthAuthMethod[] @@id([projectId, projectUserId]) // indices for sorting and filtering @@index([projectId, displayName(sort: Asc)], name: "ProjectUser_displayName_asc") @@index([projectId, displayName(sort: Desc)], name: "ProjectUser_displayName_desc") @@index([projectId, createdAt(sort: Asc)], name: "ProjectUser_createdAt_asc") @@index([projectId, createdAt(sort: Desc)], name: "ProjectUser_createdAt_desc") } // This should be renamed to "OAuthAccount" as it is not always bound to a user // When ever a user goes through the OAuth flow and gets an account ID from the OAuth provider, we store that here. model ProjectUserOAuthAccount { projectId String projectUserId String @db.Uuid projectConfigId String @db.Uuid oauthProviderConfigId String providerAccountId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // This is used for the user to distinguish between multiple accounts from the same provider. // we might want to add more user info here later email String? providerConfig OAuthProviderConfig @relation(fields: [projectConfigId, oauthProviderConfigId], references: [projectConfigId, id], onDelete: Cascade) // Before the OAuth account is connected to a use (for example, in the link oauth process), the projectUser is null. projectUser ProjectUser? @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) oauthTokens OAuthToken[] oauthAccessToken OAuthAccessToken[] // At lease one of the authMethod or connectedAccount should be set. connectedAccount ConnectedAccount? oauthAuthMethod OAuthAuthMethod? @@id([projectId, oauthProviderConfigId, providerAccountId]) } enum ContactChannelType { EMAIL // PHONE } model ContactChannel { projectId String projectUserId String @db.Uuid id String @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt type ContactChannelType isPrimary BooleanTrue? usedForAuth BooleanTrue? isVerified Boolean value String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) @@id([projectId, projectUserId, id]) // each user has at most one primary contact channel of each type @@unique([projectId, projectUserId, type, isPrimary]) // value must be unique per user per type @@unique([projectId, projectUserId, type, value]) // only one contact channel per project with the same value and type can be used for auth @@unique([projectId, type, value, usedForAuth]) } model ConnectedAccountConfig { projectConfigId String @db.Uuid id String @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt enabled Boolean @default(true) // this should never be null oauthProviderConfig OAuthProviderConfig? projectConfig ProjectConfig @relation(fields: [projectConfigId], references: [id], onDelete: Cascade) connectedAccounts ConnectedAccount[] @@id([projectConfigId, id]) } model ConnectedAccount { projectId String id String @default(uuid()) @db.Uuid projectConfigId String @db.Uuid connectedAccountConfigId String @db.Uuid projectUserId String @db.Uuid oauthProviderConfigId String providerAccountId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt oauthAccount ProjectUserOAuthAccount @relation(fields: [projectId, oauthProviderConfigId, providerAccountId], references: [projectId, oauthProviderConfigId, providerAccountId]) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) connectedAccountConfig ConnectedAccountConfig @relation(fields: [projectConfigId, connectedAccountConfigId], references: [projectConfigId, id], onDelete: Cascade) oauthProviderConfig OAuthProviderConfig @relation(fields: [projectConfigId, oauthProviderConfigId], references: [projectConfigId, id], onDelete: Cascade) @@id([projectId, id]) @@unique([projectId, oauthProviderConfigId, providerAccountId]) } model AuthMethodConfig { projectConfigId String @db.Uuid id String @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt enabled Boolean @default(true) // exactly one of xyzConfig should be set otpConfig OtpAuthMethodConfig? oauthProviderConfig OAuthProviderConfig? passwordConfig PasswordAuthMethodConfig? passkeyConfig PasskeyAuthMethodConfig? projectConfig ProjectConfig @relation(fields: [projectConfigId], references: [id], onDelete: Cascade) authMethods AuthMethod[] @@id([projectConfigId, id]) } model OtpAuthMethodConfig { projectConfigId String @db.Uuid authMethodConfigId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt contactChannelType ContactChannelType authMethodConfig AuthMethodConfig @relation(fields: [projectConfigId, authMethodConfigId], references: [projectConfigId, id], onDelete: Cascade) @@id([projectConfigId, authMethodConfigId]) } model PasswordAuthMethodConfig { projectConfigId String @db.Uuid authMethodConfigId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt authMethodConfig AuthMethodConfig @relation(fields: [projectConfigId, authMethodConfigId], references: [projectConfigId, id], onDelete: Cascade) @@id([projectConfigId, authMethodConfigId]) } model PasskeyAuthMethodConfig { projectConfigId String @db.Uuid authMethodConfigId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt authMethodConfig AuthMethodConfig @relation(fields: [projectConfigId, authMethodConfigId], references: [projectConfigId, id], onDelete: Cascade) @@id([projectConfigId, authMethodConfigId]) } // Both the connected account and auth methods can use this configuration. model OAuthProviderConfig { projectConfigId String @db.Uuid id String authMethodConfigId String? @db.Uuid connectedAccountConfigId String? @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Exactly one of the xyzOAuthConfig variables should be set. proxiedOAuthConfig ProxiedOAuthProviderConfig? standardOAuthConfig StandardOAuthProviderConfig? // At least one of authMethodConfig or connectedAccountConfig should be set. // The config relation will never be unset, but the enabled field might be set to false on authMethodConfig or connectedAccountConfig. authMethodConfig AuthMethodConfig? @relation(fields: [projectConfigId, authMethodConfigId], references: [projectConfigId, id], onDelete: Cascade) connectedAccountConfig ConnectedAccountConfig? @relation(fields: [projectConfigId, connectedAccountConfigId], references: [projectConfigId, id], onDelete: Cascade) projectConfig ProjectConfig @relation(fields: [projectConfigId], references: [id], onDelete: Cascade) projectUserOAuthAccounts ProjectUserOAuthAccount[] connectedAccounts ConnectedAccount[] oauthAuthMethod OAuthAuthMethod[] @@id([projectConfigId, id]) @@unique([projectConfigId, authMethodConfigId]) @@unique([projectConfigId, connectedAccountConfigId]) } model ProxiedOAuthProviderConfig { projectConfigId String @db.Uuid id String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt type ProxiedOAuthProviderType providerConfig OAuthProviderConfig @relation(fields: [projectConfigId, id], references: [projectConfigId, id], onDelete: Cascade) @@id([projectConfigId, id]) // each type of proxied OAuth provider can only be used once per project @@unique([projectConfigId, type]) } enum ProxiedOAuthProviderType { GITHUB GOOGLE MICROSOFT SPOTIFY } model StandardOAuthProviderConfig { projectConfigId String @db.Uuid id String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt type StandardOAuthProviderType clientId String clientSecret String // optional extra parameters for specific oauth providers // Facebook business integration requires a config_id facebookConfigId String? // Microsoft organizational directory (tenant) microsoftTenantId String? providerConfig OAuthProviderConfig @relation(fields: [projectConfigId, id], references: [projectConfigId, id], onDelete: Cascade) // each type of standard OAuth provider can only be used once per project @@id([projectConfigId, id]) } model AuthMethod { projectId String id String @default(uuid()) @db.Uuid projectUserId String @db.Uuid authMethodConfigId String @db.Uuid projectConfigId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // exactly one of the xyzAuthMethods should be set otpAuthMethod OtpAuthMethod? passwordAuthMethod PasswordAuthMethod? passkeyAuthMethod PasskeyAuthMethod? oauthAuthMethod OAuthAuthMethod? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) authMethodConfig AuthMethodConfig @relation(fields: [projectConfigId, authMethodConfigId], references: [projectConfigId, id], onDelete: Cascade) @@id([projectId, id]) } model OtpAuthMethod { projectId String authMethodId String @db.Uuid projectUserId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt authMethod AuthMethod @relation(fields: [projectId, authMethodId], references: [projectId, id], onDelete: Cascade) projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) @@id([projectId, authMethodId]) // a user can only have one OTP auth method @@unique([projectId, projectUserId]) } model PasswordAuthMethod { projectId String authMethodId String @db.Uuid projectUserId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt passwordHash String authMethod AuthMethod @relation(fields: [projectId, authMethodId], references: [projectId, id], onDelete: Cascade) projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) @@id([projectId, authMethodId]) // a user can only have one password auth method @@unique([projectId, projectUserId]) } model PasskeyAuthMethod { projectId String authMethodId String @db.Uuid projectUserId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt credentialId String publicKey String userHandle String transports String[] credentialDeviceType String counter Int authMethod AuthMethod @relation(fields: [projectId, authMethodId], references: [projectId, id], onDelete: Cascade) projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) @@id([projectId, authMethodId]) // a user can only have one password auth method @@unique([projectId, projectUserId]) } // This connects to projectUserOauthAccount, which might be shared between auth method and connected account. model OAuthAuthMethod { projectId String projectConfigId String @db.Uuid authMethodId String @db.Uuid oauthProviderConfigId String providerAccountId String projectUserId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt authMethod AuthMethod @relation(fields: [projectId, authMethodId], references: [projectId, id], onDelete: Cascade) oauthAccount ProjectUserOAuthAccount @relation(fields: [projectId, oauthProviderConfigId, providerAccountId], references: [projectId, oauthProviderConfigId, providerAccountId]) projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) oauthProviderConfig OAuthProviderConfig @relation(fields: [projectConfigId, oauthProviderConfigId], references: [projectConfigId, id], onDelete: Cascade) @@id([projectId, authMethodId]) @@unique([projectId, oauthProviderConfigId, providerAccountId]) } enum StandardOAuthProviderType { GITHUB FACEBOOK GOOGLE MICROSOFT SPOTIFY DISCORD GITLAB BITBUCKET LINKEDIN APPLE X } model OAuthToken { id String @id @default(uuid()) @db.Uuid projectId String oAuthProviderConfigId String providerAccountId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt projectUserOAuthAccount ProjectUserOAuthAccount @relation(fields: [projectId, oAuthProviderConfigId, providerAccountId], references: [projectId, oauthProviderConfigId, providerAccountId], onDelete: Cascade) refreshToken String scopes String[] } model OAuthAccessToken { id String @id @default(uuid()) @db.Uuid projectId String oAuthProviderConfigId String providerAccountId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt projectUserOAuthAccount ProjectUserOAuthAccount @relation(fields: [projectId, oAuthProviderConfigId, providerAccountId], references: [projectId, oauthProviderConfigId, providerAccountId], onDelete: Cascade) accessToken String scopes String[] expiresAt DateTime } model OAuthOuterInfo { id String @id @default(uuid()) @db.Uuid info Json innerState String @unique expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model ProjectUserRefreshToken { projectId String projectUserId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt refreshToken String @unique expiresAt DateTime? projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) @@id([projectId, refreshToken]) } model ProjectUserAuthorizationCode { projectId String projectUserId String @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt authorizationCode String @unique redirectUri String expiresAt DateTime codeChallenge String codeChallengeMethod String newUser Boolean afterCallbackRedirectUrl String? projectUser ProjectUser @relation(fields: [projectId, projectUserId], references: [projectId, projectUserId], onDelete: Cascade) @@id([projectId, authorizationCode]) } model VerificationCode { projectId String id String @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt type VerificationCodeType code String expiresAt DateTime usedAt DateTime? redirectUrl String? method Json @default("null") data Json attemptCount Int @default(0) @@id([projectId, id]) @@unique([projectId, code]) @@index([data(ops: JsonbPathOps)], type: Gin) } enum VerificationCodeType { ONE_TIME_PASSWORD PASSWORD_RESET CONTACT_CHANNEL_VERIFICATION TEAM_INVITATION MFA_ATTEMPT PASSKEY_REGISTRATION_CHALLENGE PASSKEY_AUTHENTICATION_CHALLENGE NEON_INTEGRATION_PROJECT_TRANSFER } //#region API keys model ApiKeySet { projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) id String @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt description String expiresAt DateTime manuallyRevokedAt DateTime? publishableClientKey String? @unique secretServerKey String? @unique superSecretAdminKey String? @unique @@id([projectId, id]) } model EmailServiceConfig { projectConfigId String @id @db.Uuid projectConfig ProjectConfig @relation(fields: [projectConfigId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt proxiedEmailServiceConfig ProxiedEmailServiceConfig? standardEmailServiceConfig StandardEmailServiceConfig? emailTemplates EmailTemplate[] } enum EmailTemplateType { EMAIL_VERIFICATION PASSWORD_RESET MAGIC_LINK TEAM_INVITATION } model EmailTemplate { projectConfigId String @db.Uuid emailServiceConfig EmailServiceConfig @relation(fields: [projectConfigId], references: [projectConfigId], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt content Json type EmailTemplateType subject String @@id([projectConfigId, type]) } model ProxiedEmailServiceConfig { projectConfigId String @id @db.Uuid emailServiceConfig EmailServiceConfig @relation(fields: [projectConfigId], references: [projectConfigId], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model StandardEmailServiceConfig { projectConfigId String @id @db.Uuid emailServiceConfig EmailServiceConfig @relation(fields: [projectConfigId], references: [projectConfigId], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt senderName String senderEmail String host String port Int username String password String } //#endregion //#region IdP model IdPAccountToCdfcResultMapping { idpId String id String idpAccountId String @db.Uuid @unique cdfcResult Json @@id([idpId, id]) } model ProjectWrapperCodes { idpId String id String @default(uuid()) @db.Uuid interactionUid String authorizationCode String @unique cdfcResult Json @@id([idpId, id]) } model IdPAdapterData { idpId String model String id String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt payload Json expiresAt DateTime @@id([idpId, model, id]) @@index([payload(ops: JsonbPathOps)], type: Gin) @@index([expiresAt]) } //#endregion //#region Neon integration model NeonProvisionedProject { projectId String @id project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt neonClientId String } //#endregion //#region Events model Event { id String @id @default(uuid()) @db.Uuid createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // if isWide == false, then eventEndedAt is always equal to eventStartedAt isWide Boolean eventStartedAt DateTime eventEndedAt DateTime // TODO: add event_type, and at least one of either system_event_type or event_type is always set systemEventTypeIds String[] data Json // ============================== BEGIN END USER PROPERTIES ============================== // Below are properties describing the end user that caused this event to be logged // This is different from a request IP. See: apps/backend/src/lib/end-users.tsx // Note that the IP may have been spoofed, unless isEndUserIpInfoGuessTrusted is true endUserIpInfoGuessId String? @db.Uuid endUserIpInfoGuess EventIpInfo? @relation("EventIpInfo", fields: [endUserIpInfoGuessId], references: [id]) // If true, then endUserIpInfoGuess is not spoofed (might still be behind VPNs/proxies). If false, then the values may be spoofed. isEndUserIpInfoGuessTrusted Boolean @default(false) // =============================== END END USER PROPERTIES =============================== @@index([data(ops: JsonbPathOps)], type: Gin) } // An IP address that was seen in an event. Use the location fields instead of refetching the location from the ip, as the real-world geoip data may have changed since the event was logged. model EventIpInfo { id String @id @default(uuid()) @db.Uuid ip String countryCode String? regionCode String? cityName String? latitude Float? longitude Float? tzIdentifier String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt events Event[] @relation("EventIpInfo") } //#endregion