// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client" output = "../src/generated/prisma" moduleFormat = "esm" generatedFileExtension = "ts" importFileExtension = "ts" } // generator json { // provider = "prisma-json-types-generator" // } datasource db { provider = "postgresql" url = env("DATABASE_URL") directUrl = env("DATABASE_URL_DIRECT") } model CodeMigration { id String @id @default(dbgenerated("gen_random_uuid()")) name String @unique createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("__code_migrations") } enum ProjectType { website app backend } model Chat { id String @id @default(dbgenerated("gen_random_uuid()")) messages Json projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("chats") } model Organization { id String @id @default(dbgenerated("gen_random_uuid()")) name String projects Project[] members Member[] createdByUserId String? createdBy User? @relation(name: "organizationCreatedBy", fields: [createdByUserId], references: [id], onDelete: SetNull) ProjectAccess ProjectAccess[] Client Client[] Dashboard Dashboard[] ShareOverview ShareOverview[] ShareDashboard ShareDashboard[] ShareReport ShareReport[] ShareWidget ShareWidget[] integrations Integration[] invites Invite[] timezone String? // Subscription subscriptionId String? subscriptionCustomerId String? subscriptionPriceId String? subscriptionProductId String? /// [IPrismaSubscriptionStatus] subscriptionStatus String? subscriptionStartsAt DateTime? subscriptionEndsAt DateTime? subscriptionCanceledAt DateTime? subscriptionCreatedByUserId String? subscriptionCreatedBy User? @relation(name: "subscriptionCreatedBy", fields: [subscriptionCreatedByUserId], references: [id]) subscriptionPeriodEventsCount Int @default(0) subscriptionPeriodEventsCountExceededAt DateTime? subscriptionPeriodEventsLimit Int @default(0) subscriptionInterval String? // When deleteAt > now(), the organization will be deleted deleteAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("organizations") } model User { id String @id @default(dbgenerated("gen_random_uuid()")) email String @unique firstName String? lastName String? createdOrganizations Organization[] @relation("organizationCreatedBy") subscriptions Organization[] @relation("subscriptionCreatedBy") membership Member[] sentInvites Member[] @relation("invitedBy") createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt deletedAt DateTime? ProjectAccess ProjectAccess[] sessions Session[] accounts Account[] invites Invite[] @@map("users") } model Account { id String @id @default(dbgenerated("gen_random_uuid()")) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) email String? provider String providerId String? accessToken String? refreshToken String? accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? password String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt resetPasswords ResetPassword[] @@map("accounts") } model Session { id String @id userId String expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt user User @relation(references: [id], fields: [userId], onDelete: Cascade) @@map("sessions") } model Member { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid role String email String // userId is nullable because we want to allow invites to be sent to emails that are not registered userId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) invitedById String? invitedBy User? @relation("invitedBy", fields: [invitedById], references: [id], onDelete: SetNull) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) meta Json? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("members") } model Invite { id String @id email String createdBy User @relation(fields: [createdById], references: [id], onDelete: Cascade) createdById String organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) projectAccess String[] expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt role String @@map("invites") } model Project { id String @id @default(dbgenerated("gen_random_uuid()")) name String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String eventsCount Int @default(0) types ProjectType[] @default([]) domain String? cors String[] @default([]) crossDomain Boolean @default(false) allowUnsafeRevenueTracking Boolean @default(false) /// [IPrismaProjectFilters] filters Json @default("[]") clients Client[] reports Report[] dashboards Dashboard[] share ShareOverview? shareDashboards ShareDashboard[] shareReports ShareReport[] shareWidgets ShareWidget[] meta EventMeta[] references Reference[] access ProjectAccess[] notificationRules NotificationRule[] notifications Notification[] imports Import[] // When deleteAt > now(), the project will be deleted deleteAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt Chat Chat[] @@map("projects") } enum AccessLevel { read write admin } model ProjectAccess { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) level AccessLevel createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("project_access") } model Salt { salt String @id createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("salts") } enum ClientType { read write root } model Client { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String secret String? type ClientType @default(write) projectId String? project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String ignoreCorsAndSecret Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("clients") } enum Interval { hour day month minute week } enum ChartType { linear bar histogram pie metric area map funnel retention conversion sankey } model Dashboard { id String @id @default(dbgenerated("gen_random_uuid()")) name String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) reports Report[] share ShareDashboard? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("dashboards") } enum Metric { sum average min max } model Report { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String interval Interval range String @default("30d") chartType ChartType lineType String @default("monotone") breakdowns Json events Json formula String? unit String? metric Metric @default(sum) projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) previous Boolean @default(false) criteria String? funnelGroup String? funnelWindow Float? /// [IReportOptions] options Json? dashboardId String dashboard Dashboard @relation(fields: [dashboardId], references: [id], onDelete: Cascade) layout ReportLayout? share ShareReport? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("reports") } model ReportLayout { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid reportId String @unique @db.Uuid report Report @relation(fields: [reportId], references: [id], onDelete: Cascade) // Grid position and size x Int @default(0) y Int @default(0) w Int @default(4) h Int @default(3) // Optional: store additional layout preferences minW Int? @default(2) minH Int? @default(2) maxW Int? maxH Int? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("report_layouts") } model ShareOverview { id String @unique projectId String @unique project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String public Boolean @default(false) password String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("shares") } model ShareDashboard { id String @unique dashboardId String @unique dashboard Dashboard @relation(fields: [dashboardId], references: [id], onDelete: Cascade) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) public Boolean @default(false) password String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("share_dashboards") } model ShareReport { id String @unique reportId String @unique @db.Uuid report Report @relation(fields: [reportId], references: [id], onDelete: Cascade) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) public Boolean @default(false) password String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("share_reports") } model ShareWidget { id String @unique projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String public Boolean @default(true) /// [IPrismaWidgetOptions] options Json createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("share_widgets") } model EventMeta { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String conversion Boolean? color String? icon String? projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@unique([name, projectId]) @@map("event_meta") } model Reference { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid title String description String? date DateTime @default(now()) projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("references") } enum IntegrationType { app mail custom } model NotificationRule { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) integrations Integration[] sendToApp Boolean @default(false) sendToEmail Boolean @default(false) /// [IPrismaNotificationRuleConfig] config Json template String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt notifications Notification[] @@map("notification_rules") } model Notification { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) title String message String isReadAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt sendToApp Boolean @default(false) sendToEmail Boolean @default(false) integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade) integrationId String? @db.Uuid notificationRuleId String? @db.Uuid notificationRule NotificationRule? @relation(fields: [notificationRuleId], references: [id], onDelete: Cascade) /// [IPrismaNotificationPayload] payload Json? @@map("notifications") } model Integration { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String /// [IPrismaIntegrationConfig] config Json organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) organizationId String notificationRules NotificationRule[] notifications Notification[] createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("integrations") } model ResetPassword { id String @id accountId String account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("reset_password") } enum ImportStatus { pending processing completed failed } model Import { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) jobId String? // BullMQ job ID status ImportStatus statusMessage String? // Human-readable current step like "Importing events (Feb 2025)", "Generating session IDs" errorMessage String? /// [IPrismaImportConfig] config Json totalEvents Int @default(0) processedEvents Int @default(0) currentStep String? currentBatch String? // String date 2020-01-01 createdAt DateTime @default(now()) completedAt DateTime? updatedAt DateTime @default(now()) @updatedAt @@map("imports") } enum InsightState { active suppressed closed } model ProjectInsight { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid projectId String moduleKey String // e.g. "referrers", "entry-pages" dimensionKey String // e.g. "referrer:instagram", "page:/pricing" windowKind String // "yesterday" | "rolling_7d" | "rolling_30d" state InsightState @default(active) title String summary String? displayName String @default("") /// [IPrismaProjectInsightPayload] payload Json @default("{}") // Rendered insight payload (typed) direction String? // "up" | "down" | "flat" impactScore Float @default(0) severityBand String? // "low" | "moderate" | "severe" version Int @default(1) threadId String @default(dbgenerated("gen_random_uuid()")) @db.Uuid windowStart DateTime? windowEnd DateTime? firstDetectedAt DateTime @default(now()) lastUpdatedAt DateTime @default(now()) @updatedAt lastSeenAt DateTime @default(now()) events InsightEvent[] @@unique([projectId, moduleKey, dimensionKey, windowKind, state]) @@index([projectId, impactScore(sort: Desc)]) @@index([projectId, moduleKey, windowKind, state]) @@map("project_insights") } model InsightEvent { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid insightId String @db.Uuid insight ProjectInsight @relation(fields: [insightId], references: [id], onDelete: Cascade) eventKind String // "created" | "updated" | "severity_up" | "direction_flip" | "closed" | etc changeFrom Json? changeTo Json? createdAt DateTime @default(now()) @@index([insightId, createdAt]) @@map("insight_events") }