// 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" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum ConnectionSyncStatus { SYNC_NEEDED IN_SYNC_QUEUE SYNCING SYNCED SYNCED_WITH_WARNINGS FAILED } enum StripeSubscriptionStatus { ACTIVE INACTIVE } enum ChatVisibility { PRIVATE PUBLIC } /// @note: The @map annotation is required to maintain backwards compatibility /// with the existing database. /// @note: In the generated client, these mapped values will be in pascalCase. /// This behaviour will change in prisma v7. See: https://github.com/prisma/prisma/issues/8446#issuecomment-3356119713 enum CodeHostType { github gitlab gitea gerrit bitbucketServer @map("bitbucket-server") bitbucketCloud @map("bitbucket-cloud") genericGitHost @map("generic-git-host") azuredevops } model Repo { id Int @id @default(autoincrement()) name String /// Full repo name, including the vcs hostname (ex. github.com/sourcebot-dev/sourcebot) displayName String? /// Display name of the repo for UI (ex. sourcebot-dev/sourcebot) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt isFork Boolean isArchived Boolean isPublic Boolean @default(false) metadata Json /// For schema see repoMetadataSchema in packages/shared/src/types.ts cloneUrl String webUrl String? connections RepoToConnection[] imageUrl String? permittedUsers UserToRepoPermission[] permissionSyncJobs RepoPermissionSyncJob[] permissionSyncedAt DateTime? /// When the permissions were last synced successfully. jobs RepoIndexingJob[] indexedAt DateTime? /// When the repo was last indexed successfully. indexedCommitHash String? /// The commit hash of the last indexed commit (on HEAD). external_id String /// The id of the repo in the external service external_codeHostType CodeHostType /// The type of the external service (e.g., github, gitlab, etc.) external_codeHostUrl String /// The base url of the external service (e.g., https://github.com) org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int searchContexts SearchContext[] @@unique([external_id, external_codeHostUrl, orgId]) @@index([orgId]) } enum RepoIndexingJobStatus { PENDING IN_PROGRESS COMPLETED FAILED } enum RepoIndexingJobType { INDEX CLEANUP } model RepoIndexingJob { id String @id @default(cuid()) type RepoIndexingJobType status RepoIndexingJobStatus @default(PENDING) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt completedAt DateTime? metadata Json? /// For schema see repoIndexingJobMetadataSchema in packages/shared/src/types.ts errorMessage String? repo Repo @relation(fields: [repoId], references: [id], onDelete: Cascade) repoId Int } enum RepoPermissionSyncJobStatus { PENDING IN_PROGRESS COMPLETED FAILED } model RepoPermissionSyncJob { id String @id @default(cuid()) status RepoPermissionSyncJobStatus @default(PENDING) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt completedAt DateTime? errorMessage String? repo Repo @relation(fields: [repoId], references: [id], onDelete: Cascade) repoId Int } model SearchContext { id Int @id @default(autoincrement()) name String description String? repos Repo[] org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int @@unique([name, orgId]) } /// Matches the union of `type` fields in the schema. /// @see: schemas/v3/connection.type.ts enum ConnectionType { github gitlab gitea gerrit bitbucket azuredevops git } model Connection { id Int @id @default(autoincrement()) name String config Json isDeclarative Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt repos RepoToConnection[] // The type of connection (e.g., github, gitlab, etc.) connectionType ConnectionType syncJobs ConnectionSyncJob[] /// When the connection was last synced successfully. syncedAt DateTime? // The organization that owns this connection org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int @@unique([name, orgId]) } enum ConnectionSyncJobStatus { PENDING IN_PROGRESS COMPLETED FAILED } model ConnectionSyncJob { id String @id @default(cuid()) status ConnectionSyncJobStatus @default(PENDING) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt completedAt DateTime? warningMessages String[] errorMessage String? connection Connection @relation(fields: [connectionId], references: [id], onDelete: Cascade) connectionId Int } model RepoToConnection { addedAt DateTime @default(now()) connection Connection @relation(fields: [connectionId], references: [id], onDelete: Cascade) connectionId Int repo Repo @relation(fields: [repoId], references: [id], onDelete: Cascade) repoId Int @@id([connectionId, repoId]) @@index([repoId, connectionId]) } model Invite { /// The globally unique invite id id String @id @default(cuid()) /// Time of invite creation createdAt DateTime @default(now()) /// The email of the recipient of the invite recipientEmail String /// The user that created the invite host User @relation(fields: [hostUserId], references: [id], onDelete: Cascade) hostUserId String /// The organization the invite is for org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int @@unique([recipientEmail, orgId]) } model AccountRequest { id String @id @default(cuid()) createdAt DateTime @default(now()) requestedBy User @relation(fields: [requestedById], references: [id], onDelete: Cascade) requestedById String @unique org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int @@unique([requestedById, orgId]) } model Org { id Int @id @default(autoincrement()) name String domain String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt members UserToOrg[] connections Connection[] repos Repo[] apiKeys ApiKey[] isOnboarded Boolean @default(false) imageUrl String? metadata Json? // For schema see orgMetadataSchema in packages/web/src/types.ts memberApprovalRequired Boolean @default(true) stripeCustomerId String? stripeSubscriptionStatus StripeSubscriptionStatus? stripeLastUpdatedAt DateTime? /// List of pending invites to this organization invites Invite[] /// The invite id for this organization inviteLinkEnabled Boolean @default(false) inviteLinkId String? audits Audit[] accountRequests AccountRequest[] searchContexts SearchContext[] chats Chat[] } enum OrgRole { OWNER MEMBER GUEST } model UserToOrg { joinedAt DateTime @default(now()) /// The linked organization org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int /// The linked user user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId String role OrgRole @default(MEMBER) @@id([orgId, userId]) } model ApiKey { name String hash String @id @unique createdAt DateTime @default(now()) lastUsedAt DateTime? org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int createdBy User @relation(fields: [createdById], references: [id], onDelete: Cascade) createdById String } model Audit { id String @id @default(cuid()) timestamp DateTime @default(now()) action String actorId String actorType String targetId String targetType String sourcebotVersion String metadata Json? org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int @@index([actorId, actorType, targetId, targetType, orgId]) // Fast path for analytics queries – orgId is first because we assume most deployments are single tenant @@index([orgId, timestamp, action, actorId], map: "idx_audit_core_actions_full") // Fast path for analytics queries for a specific user @@index([actorId, timestamp], map: "idx_audit_actor_time_full") } // @see : https://authjs.dev/concepts/database-models#user model User { id String @id @default(cuid()) name String? email String? @unique hashedPassword String? emailVerified DateTime? image String? accounts Account[] orgs UserToOrg[] accountRequest AccountRequest? accessibleRepos UserToRepoPermission[] /// List of pending invites that the user has created invites Invite[] apiKeys ApiKey[] chats Chat[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt permissionSyncJobs UserPermissionSyncJob[] permissionSyncedAt DateTime? } enum UserPermissionSyncJobStatus { PENDING IN_PROGRESS COMPLETED FAILED } model UserPermissionSyncJob { id String @id @default(cuid()) status UserPermissionSyncJobStatus @default(PENDING) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt completedAt DateTime? errorMessage String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId String } model UserToRepoPermission { createdAt DateTime @default(now()) repo Repo @relation(fields: [repoId], references: [id], onDelete: Cascade) repoId Int user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId String @@id([repoId, userId]) } // @see : https://authjs.dev/concepts/database-models#account model Account { id String @id @default(cuid()) userId String type String provider String providerAccountId String refresh_token String? access_token String? expires_at Int? token_type String? scope String? id_token String? session_state String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) } // @see : https://authjs.dev/concepts/database-models#verificationtoken model VerificationToken { identifier String token String expires DateTime @@unique([identifier, token]) } model Chat { id String @id @default(cuid()) name String? createdBy User @relation(fields: [createdById], references: [id], onDelete: Cascade) createdById String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) orgId Int visibility ChatVisibility @default(PRIVATE) isReadonly Boolean @default(false) messages Json // This is a JSON array of `Message` types from @ai-sdk/ui-utils. }