generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-3.0.x", "linux-arm64-openssl-3.0.x"] previewFeatures = ["metrics", "tracing", "relationJoins", "nativeDistinct"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(uuid()) @db.VarChar name String email String @unique emailVerified DateTime? @map("email_verified") // image field is for the next-auth avatarUrl String? @map("avatar_url") @db.VarChar createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) /// Not available if user signed up through OAuth providers password String? @db.VarChar accounts Account[] sessions Session[] features UserFeatures[] customer UserStripeCustomer? subscription UserSubscription? invoices UserInvoice[] workspacePermissions WorkspaceUserPermission[] pagePermissions WorkspacePageUserPermission[] @@map("users") } model Workspace { id String @id @default(uuid()) @db.VarChar public Boolean createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) pages WorkspacePage[] permissions WorkspaceUserPermission[] pagePermissions WorkspacePageUserPermission[] features WorkspaceFeatures[] @@map("workspaces") } // Table for workspace page meta data // NOTE: // We won't make sure every page has a corresponding record in this table. // Only the ones that have ever changed will have records here, // and for others we will make sure it's has a default value return in our bussiness logic. model WorkspacePage { workspaceId String @map("workspace_id") @db.VarChar(36) pageId String @map("page_id") @db.VarChar(36) public Boolean @default(false) // Page/Edgeless mode Int @default(0) @db.SmallInt workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) @@id([workspaceId, pageId]) @@map("workspace_pages") } // @deprecated, use WorkspaceUserPermission model DeprecatedUserWorkspacePermission { id String @id @default(uuid()) @db.VarChar workspaceId String @map("workspace_id") @db.VarChar subPageId String? @map("sub_page_id") @db.VarChar userId String? @map("entity_id") @db.VarChar /// Read/Write/Admin/Owner type Int @db.SmallInt /// Whether the permission invitation is accepted by the user accepted Boolean @default(false) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) @@unique([workspaceId, subPageId, userId]) @@map("user_workspace_permissions") } model WorkspaceUserPermission { id String @id @default(uuid()) @db.VarChar(36) workspaceId String @map("workspace_id") @db.VarChar(36) userId String @map("user_id") @db.VarChar(36) // Read/Write type Int @db.SmallInt /// Whether the permission invitation is accepted by the user accepted Boolean @default(false) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) user User @relation(fields: [userId], references: [id], onDelete: Cascade) workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) @@unique([workspaceId, userId]) @@map("workspace_user_permissions") } model WorkspacePageUserPermission { id String @id @default(uuid()) @db.VarChar(36) workspaceId String @map("workspace_id") @db.VarChar(36) pageId String @map("page_id") @db.VarChar(36) userId String @map("user_id") @db.VarChar(36) // Read/Write type Int @db.SmallInt /// Whether the permission invitation is accepted by the user accepted Boolean @default(false) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) user User @relation(fields: [userId], references: [id], onDelete: Cascade) workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) @@unique([workspaceId, pageId, userId]) @@map("workspace_page_user_permissions") } // feature gates is a way to enable/disable features for a user // for example: // - early access is a feature that allow some users to access the insider version // - pro plan is a quota that allow some users access to more resources after they pay model UserFeatures { id Int @id @default(autoincrement()) userId String @map("user_id") @db.VarChar(36) featureId Int @map("feature_id") @db.Integer // we will record the reason why the feature is enabled/disabled // for example: // - pro_plan_v1: "user buy the pro plan" reason String @db.VarChar // record the quota enabled time createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) // record the quota expired time, pay plan is a subscription, so it will expired expiredAt DateTime? @map("expired_at") @db.Timestamptz(6) // whether the feature is activated // for example: // - if we switch the user to another plan, we will set the old plan to deactivated, but dont delete it activated Boolean @default(false) feature Features @relation(fields: [featureId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_features") } // feature gates is a way to enable/disable features for a workspace // for example: // - copilet is a feature that allow some users in a workspace to access the copilet feature model WorkspaceFeatures { id Int @id @default(autoincrement()) workspaceId String @map("workspace_id") @db.VarChar(36) featureId Int @map("feature_id") @db.Integer // we will record the reason why the feature is enabled/disabled // for example: // - copilet_v1: "owner buy the copilet feature package" reason String @db.VarChar // record the feature enabled time createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) // record the quota expired time, pay plan is a subscription, so it will expired expiredAt DateTime? @map("expired_at") @db.Timestamptz(6) // whether the feature is activated // for example: // - if owner unsubscribe a feature package, we will set the feature to deactivated, but dont delete it activated Boolean @default(false) feature Features @relation(fields: [featureId], references: [id], onDelete: Cascade) workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) @@map("workspace_features") } model Features { id Int @id @default(autoincrement()) feature String @db.VarChar version Int @default(0) @db.Integer // 0: feature, 1: quota type Int @db.Integer // configs, define by feature conntroller configs Json @db.Json createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) UserFeatureGates UserFeatures[] WorkspaceFeatures WorkspaceFeatures[] @@unique([feature, version]) @@map("features") } model Account { id String @id @default(cuid()) userId String @map("user_id") type String provider String providerAccountId String @map("provider_account_id") refresh_token String? @db.Text access_token String? @db.Text expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) @@map("accounts") } model Session { id String @id @default(cuid()) sessionToken String @unique @map("session_token") userId String @map("user_id") expires DateTime user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("sessions") } model VerificationToken { identifier String token String @unique expires DateTime @@unique([identifier, token]) @@map("verificationtokens") } // deprecated, use [ObjectStorage] model Blob { id Int @id @default(autoincrement()) @db.Integer hash String @db.VarChar workspaceId String @map("workspace_id") @db.VarChar blob Bytes @db.ByteA length BigInt createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) // not for keeping, but for snapshot history deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6) @@unique([workspaceId, hash]) @@map("blobs") } // deprecated, use [ObjectStorage] model OptimizedBlob { id Int @id @default(autoincrement()) @db.Integer hash String @db.VarChar workspaceId String @map("workspace_id") @db.VarChar params String @db.VarChar blob Bytes @db.ByteA length BigInt createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) // not for keeping, but for snapshot history deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6) @@unique([workspaceId, hash, params]) @@map("optimized_blobs") } // the latest snapshot of each doc that we've seen // Snapshot + Updates are the latest state of the doc model Snapshot { workspaceId String @map("workspace_id") @db.VarChar id String @default(uuid()) @map("guid") @db.VarChar blob Bytes @db.ByteA seq Int @default(0) @db.Integer state Bytes? @db.ByteA createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) // the `updated_at` field will not record the time of record changed, // but the created time of last seen update that has been merged into snapshot. updatedAt DateTime @map("updated_at") @db.Timestamptz(6) @@id([id, workspaceId]) @@map("snapshots") } model Update { workspaceId String @map("workspace_id") @db.VarChar id String @map("guid") @db.VarChar seq Int @db.Integer blob Bytes @db.ByteA createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) @@id([workspaceId, id, seq]) @@map("updates") } model SnapshotHistory { workspaceId String @map("workspace_id") @db.VarChar(36) id String @map("guid") @db.VarChar(36) timestamp DateTime @db.Timestamptz(6) blob Bytes @db.ByteA state Bytes? @db.ByteA expiredAt DateTime @map("expired_at") @db.Timestamptz(6) @@id([workspaceId, id, timestamp]) @@map("snapshot_histories") } model NewFeaturesWaitingList { id String @id @default(uuid()) @db.VarChar email String @unique type Int @db.SmallInt createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) @@map("new_features_waiting_list") } model UserStripeCustomer { userId String @id @map("user_id") @db.VarChar stripeCustomerId String @unique @map("stripe_customer_id") @db.VarChar createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_stripe_customers") } model UserSubscription { id Int @id @default(autoincrement()) @db.Integer userId String @unique @map("user_id") @db.VarChar(36) plan String @db.VarChar(20) // yearly/monthly recurring String @db.VarChar(20) // subscription.id stripeSubscriptionId String @unique @map("stripe_subscription_id") // subscription.status, active/past_due/canceled/unpaid... status String @db.VarChar(20) // subscription.current_period_start start DateTime @map("start") @db.Timestamptz(6) // subscription.current_period_end end DateTime @map("end") @db.Timestamptz(6) // subscription.billing_cycle_anchor nextBillAt DateTime? @map("next_bill_at") @db.Timestamptz(6) // subscription.canceled_at canceledAt DateTime? @map("canceled_at") @db.Timestamptz(6) // subscription.trial_start trialStart DateTime? @map("trial_start") @db.Timestamptz(6) // subscription.trial_end trialEnd DateTime? @map("trial_end") @db.Timestamptz(6) stripeScheduleId String? @map("stripe_schedule_id") @db.VarChar createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_subscriptions") } model UserInvoice { id Int @id @default(autoincrement()) @db.Integer userId String @map("user_id") @db.VarChar(36) stripeInvoiceId String @unique @map("stripe_invoice_id") currency String @db.VarChar(3) // CNY 12.50 stored as 1250 amount Int @db.Integer status String @db.VarChar(20) plan String @db.VarChar(20) recurring String @db.VarChar(20) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6) // billing reason reason String @db.VarChar lastPaymentError String? @map("last_payment_error") @db.Text // stripe hosted invoice link link String? @db.Text user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_invoices") } model DataMigration { id String @id @default(uuid()) @db.VarChar(36) name String @db.VarChar startedAt DateTime @default(now()) @map("started_at") @db.Timestamptz(6) finishedAt DateTime? @map("finished_at") @db.Timestamptz(6) @@map("_data_migrations") }