From 4d275842b168a6b7ef5fe1a0a47ad6b891682557 Mon Sep 17 00:00:00 2001 From: Anurag Singh Date: Tue, 27 May 2025 08:54:34 +0530 Subject: [PATCH] Refactor Prisma schema: split into domain-specific files for better organization - Split schema.prisma into auth, organization, projects, notifications, and enums files - Improve maintainability --- server/prisma/schema.prisma | 399 ---------------------- server/prisma/schema/auth.prisma | 122 +++++++ server/prisma/schema/enums.prisma | 60 ++++ server/prisma/schema/notifications.prisma | 15 + server/prisma/schema/organization.prisma | 73 ++++ server/prisma/schema/projects.prisma | 110 ++++++ server/prisma/schema/schema.prisma | 18 + 7 files changed, 398 insertions(+), 399 deletions(-) delete mode 100644 server/prisma/schema.prisma create mode 100644 server/prisma/schema/auth.prisma create mode 100644 server/prisma/schema/enums.prisma create mode 100644 server/prisma/schema/notifications.prisma create mode 100644 server/prisma/schema/organization.prisma create mode 100644 server/prisma/schema/projects.prisma create mode 100644 server/prisma/schema/schema.prisma diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma deleted file mode 100644 index 217c8f1..0000000 --- a/server/prisma/schema.prisma +++ /dev/null @@ -1,399 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? -// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init - -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - // Direct URL is used for migrations - url = env("DATABASE_URL") - directUrl = env("DATABASE_DIRECT_URL") -} - -enum OrganizationRole { - OWNER - ADMIN - MEMBER -} - -enum InvitationStatus { - PENDING - ACCEPTED - DECLINED - EXPIRED -} - -model User { - id String @id - name String - email String - emailVerified Boolean - image String? - createdAt DateTime - updatedAt DateTime - sessions Session[] - accounts Account[] - - twoFactorEnabled Boolean? - role String? - banned Boolean? - banReason String? - banExpires DateTime? - twofactors TwoFactor[] - members Member[] - invitations Invitation[] - - finishedOnboarding Boolean? - OnboardingDetails OnboardingDetails? - ApiKey ApiKey[] - Feedback Feedback[] - Runs Runs[] - - @@unique([email]) - @@map("user") -} - -model OnboardingDetails { - id String @id - userId String @unique - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - location String? - background String? - company String? - howDidYouHearAboutUs String? - agreeToMarketing Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - @@map("onboarding_details") -} - -model Session { - id String @id - expiresAt DateTime - token String - createdAt DateTime - updatedAt DateTime - ipAddress String? - userAgent String? - userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - impersonatedBy String? - activeOrganizationId String? - - @@unique([token]) - @@map("session") -} - -model Account { - id String @id - accountId String - providerId String - userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - accessToken String? - refreshToken String? - idToken String? - accessTokenExpiresAt DateTime? - refreshTokenExpiresAt DateTime? - scope String? - password String? - createdAt DateTime - updatedAt DateTime - - @@map("account") -} - -model Verification { - id String @id - identifier String - value String - expiresAt DateTime - createdAt DateTime? - updatedAt DateTime? - - @@map("verification") -} - -model TwoFactor { - id String @id - secret String - backupCodes String - userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@map("twoFactor") -} - -model OrganizationSubscription { - id String @id @default(uuid()) - organizationId String @unique - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) - stripeCustomerId String - stripeSubscriptionId String - plan SubscriptionPlan - seats Int - usageLimits Json // flexible, so you can customize per org - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - @@map("organization_subscription") -} - -enum SubscriptionPlan { - FREE - PRO -} - -model Organization { - id String @id - name String - slug String - logo String? - createdAt DateTime - metadata String? - members Member[] - invitations Invitation[] - ApiKey ApiKey[] - Projects Projects[] - Runs Runs[] - Feedback Feedback[] - Notification Notification[] - OrganizationSubscription OrganizationSubscription? - - @@unique([slug]) - @@map("organization") -} - -model Member { - id String @id - organizationId String - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) - userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - role OrganizationRole - createdAt DateTime - - @@map("member") -} - -model Feedback { - id Int @id @default(autoincrement()) - organizationId String - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) - userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - createdAt DateTime @default(now()) - feedback String - feedbackSentiment String? - - @@map("feedback") -} - -model Invitation { - id String @id - organizationId String - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) - email String - role OrganizationRole - status InvitationStatus - expiresAt DateTime - inviterId String - user User @relation(fields: [inviterId], references: [id], onDelete: Cascade) - - @@map("invitation") -} - -model ApiKey { - id String @id - key String - name String - // user facing key - keyString String @default("*********") - organizationId String - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) - userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - createdAt DateTime - isHashed Boolean @default(true) - lastUsed DateTime? - expiresAt DateTime? - Runs Runs[] - - @@unique([key]) - @@map("api_key") -} - -model Projects { - id BigInt @id @default(autoincrement()) - name String @db.VarChar(255) - organizationId String - tags String[] @default([]) - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) - runs Runs[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - // This unique constraint ensures we can efficiently look up projects by name + orgId - @@unique([organizationId, name]) - @@map("projects") -} - -enum RunStatus { - RUNNING - COMPLETED - FAILED - TERMINATED - CANCELLED -} - -enum RunLogType { - METRIC - IMAGE - VIDEO - AUDIO - FILE - TEXT - ARTIFACT - HISTOGRAM - TABLE - DATA -} - -model RunLogs { - id BigInt @id @default(autoincrement()) - runId BigInt - logGroup String? - logName String - logType RunLogType - run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) - createdAt DateTime @default(now()) - - @@index([runId]) - @@map("run_logs") -} - -model Runs { - id BigInt @id @default(autoincrement()) - name String @db.VarChar(255) - organizationId String - tags String[] @default([]) - loggerSettings Json? - systemMetadata Json? - config Json? - projectId BigInt - status RunStatus @default(RUNNING) - statusUpdated DateTime? - statusMetadata Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - createdById String - creatorApiKeyId String - - creator User @relation(fields: [createdById], references: [id]) - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) - project Projects @relation(fields: [projectId], references: [id]) - logs RunLogs[] - triggers RunTriggers[] - Notification Notification[] - RunGraphNode RunGraphNode[] - RunGraphEdge RunGraphEdge[] - ApiKey ApiKey @relation(fields: [creatorApiKeyId], references: [id]) - - // Add compound index on projectId and organizationId to efficiently find runs for a project - @@index([projectId, organizationId]) - @@index([status]) - @@index([createdById]) - @@map("runs") -} - -enum RunGraphNodeType { - MODULE - UNKNOWN - CONTAINER - IO -} - -model RunGraphNode { - id BigInt @id @default(autoincrement()) - runId BigInt - run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) - name String - depth Int - type String - order Int? - label String? - nodeId String? - nodeType RunGraphNodeType? - instId String? - args Json? - kwargs Json? - params Json? - - @@index([runId]) - @@index([depth]) - @@index([name]) - @@index([nodeId]) - @@map("run_graph_nodes") -} - -model RunGraphEdge { - id BigInt @id @default(autoincrement()) - runId BigInt - run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) - sourceId String - targetId String - - @@index([runId]) - @@index([sourceId]) - @@index([targetId]) - @@map("run_graph_edges") -} - -enum RunTriggerType { - CANCEL // Trigger to cancel a run -} - -model RunTriggers { - id BigInt @id @default(autoincrement()) - runId BigInt - run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) - trigger String - createdAt DateTime @default(now()) - triggerType RunTriggerType - - @@index([runId]) - @@index([triggerType]) - @@map("run_triggers") -} - -enum NotificationType { - RUN_CANCELLED - RUN_FAILED - INFO - WARNING - ERROR - DEBUG -} - -model Notification { - id BigInt @id @default(autoincrement()) - runId BigInt - organizationId String - organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) - run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) - createdAt DateTime @default(now()) - type NotificationType - content String - read Boolean @default(false) - - @@index([runId]) - @@index([organizationId]) - @@map("notifications") -} diff --git a/server/prisma/schema/auth.prisma b/server/prisma/schema/auth.prisma new file mode 100644 index 0000000..cd28111 --- /dev/null +++ b/server/prisma/schema/auth.prisma @@ -0,0 +1,122 @@ +model User { + id String @id + name String + email String + emailVerified Boolean + image String? + createdAt DateTime + updatedAt DateTime + sessions Session[] + accounts Account[] + + twoFactorEnabled Boolean? + role String? + banned Boolean? + banReason String? + banExpires DateTime? + twofactors TwoFactor[] + members Member[] + invitations Invitation[] + + finishedOnboarding Boolean? + OnboardingDetails OnboardingDetails? + ApiKey ApiKey[] + Feedback Feedback[] + Runs Runs[] + + @@unique([email]) + @@map("user") +} + +model Account { + id String @id + accountId String + providerId String + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + accessToken String? + refreshToken String? + idToken String? + accessTokenExpiresAt DateTime? + refreshTokenExpiresAt DateTime? + scope String? + password String? + createdAt DateTime + updatedAt DateTime + + @@map("account") +} + +model Verification { + id String @id + identifier String + value String + expiresAt DateTime + createdAt DateTime? + updatedAt DateTime? + + @@map("verification") +} + +model TwoFactor { + id String @id + secret String + backupCodes String + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("twoFactor") +} + +model OnboardingDetails { + id String @id + userId String @unique + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + location String? + background String? + company String? + howDidYouHearAboutUs String? + agreeToMarketing Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("onboarding_details") +} + +model Session { + id String @id + expiresAt DateTime + token String + createdAt DateTime + updatedAt DateTime + ipAddress String? + userAgent String? + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + impersonatedBy String? + activeOrganizationId String? + + @@unique([token]) + @@map("session") +} + +model ApiKey { + id String @id + key String + name String + // user facing key + keyString String @default("*********") + organizationId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + createdAt DateTime + isHashed Boolean @default(true) + lastUsed DateTime? + expiresAt DateTime? + Runs Runs[] + + @@unique([key]) + @@map("api_key") +} diff --git a/server/prisma/schema/enums.prisma b/server/prisma/schema/enums.prisma new file mode 100644 index 0000000..51a70fd --- /dev/null +++ b/server/prisma/schema/enums.prisma @@ -0,0 +1,60 @@ +enum InvitationStatus { + PENDING + ACCEPTED + DECLINED + EXPIRED +} + +enum NotificationType { + RUN_CANCELLED + RUN_FAILED + INFO + WARNING + ERROR + DEBUG +} + +enum OrganizationRole { + OWNER + ADMIN + MEMBER +} + +enum RunStatus { + RUNNING + COMPLETED + FAILED + TERMINATED + CANCELLED +} + +enum RunLogType { + METRIC + IMAGE + VIDEO + AUDIO + FILE + TEXT + ARTIFACT + HISTOGRAM + TABLE + DATA +} + +enum RunGraphNodeType { + MODULE + UNKNOWN + CONTAINER + IO +} + +enum RunTriggerType { + CANCEL // Trigger to cancel a run +} + +enum SubscriptionPlan { + FREE + PRO +} + + diff --git a/server/prisma/schema/notifications.prisma b/server/prisma/schema/notifications.prisma new file mode 100644 index 0000000..7ba71b4 --- /dev/null +++ b/server/prisma/schema/notifications.prisma @@ -0,0 +1,15 @@ +model Notification { + id BigInt @id @default(autoincrement()) + runId BigInt + organizationId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) + createdAt DateTime @default(now()) + type NotificationType + content String + read Boolean @default(false) + + @@index([runId]) + @@index([organizationId]) + @@map("notifications") +} diff --git a/server/prisma/schema/organization.prisma b/server/prisma/schema/organization.prisma new file mode 100644 index 0000000..346d844 --- /dev/null +++ b/server/prisma/schema/organization.prisma @@ -0,0 +1,73 @@ +model OrganizationSubscription { + id String @id @default(uuid()) + organizationId String @unique + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + stripeCustomerId String + stripeSubscriptionId String + plan SubscriptionPlan + seats Int + usageLimits Json // flexible, so you can customize per org + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("organization_subscription") +} + +model Organization { + id String @id + name String + slug String + logo String? + createdAt DateTime + metadata String? + members Member[] + invitations Invitation[] + ApiKey ApiKey[] + Projects Projects[] + Runs Runs[] + Feedback Feedback[] + Notification Notification[] + OrganizationSubscription OrganizationSubscription? + + @@unique([slug]) + @@map("organization") +} + +model Member { + id String @id + organizationId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + role OrganizationRole + createdAt DateTime + + @@map("member") +} + +model Feedback { + id Int @id @default(autoincrement()) + organizationId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + createdAt DateTime @default(now()) + feedback String + feedbackSentiment String? + + @@map("feedback") +} + +model Invitation { + id String @id + organizationId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + email String + role OrganizationRole + status InvitationStatus + expiresAt DateTime + inviterId String + user User @relation(fields: [inviterId], references: [id], onDelete: Cascade) + + @@map("invitation") +} diff --git a/server/prisma/schema/projects.prisma b/server/prisma/schema/projects.prisma new file mode 100644 index 0000000..a43f8f5 --- /dev/null +++ b/server/prisma/schema/projects.prisma @@ -0,0 +1,110 @@ +model Projects { + id BigInt @id @default(autoincrement()) + name String @db.VarChar(255) + organizationId String + tags String[] @default([]) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) + runs Runs[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // This unique constraint ensures we can efficiently look up projects by name + orgId + @@unique([organizationId, name]) + @@map("projects") +} + +model RunLogs { + id BigInt @id @default(autoincrement()) + runId BigInt + logGroup String? + logName String + logType RunLogType + run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) + createdAt DateTime @default(now()) + + @@index([runId]) + @@map("run_logs") +} + +model Runs { + id BigInt @id @default(autoincrement()) + name String @db.VarChar(255) + organizationId String + tags String[] @default([]) + loggerSettings Json? + systemMetadata Json? + config Json? + projectId BigInt + status RunStatus @default(RUNNING) + statusUpdated DateTime? + statusMetadata Json? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + createdById String + creatorApiKeyId String + + creator User @relation(fields: [createdById], references: [id]) + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) + project Projects @relation(fields: [projectId], references: [id]) + logs RunLogs[] + triggers RunTriggers[] + Notification Notification[] + RunGraphNode RunGraphNode[] + RunGraphEdge RunGraphEdge[] + ApiKey ApiKey @relation(fields: [creatorApiKeyId], references: [id]) + + // Add compound index on projectId and organizationId to efficiently find runs for a project + @@index([projectId, organizationId]) + @@index([status]) + @@index([createdById]) + @@map("runs") +} + +model RunGraphNode { + id BigInt @id @default(autoincrement()) + runId BigInt + run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) + name String + depth Int + type String + order Int? + label String? + nodeId String? + nodeType RunGraphNodeType? + instId String? + args Json? + kwargs Json? + params Json? + + @@index([runId]) + @@index([depth]) + @@index([name]) + @@index([nodeId]) + @@map("run_graph_nodes") +} + +model RunGraphEdge { + id BigInt @id @default(autoincrement()) + runId BigInt + run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) + sourceId String + targetId String + + @@index([runId]) + @@index([sourceId]) + @@index([targetId]) + @@map("run_graph_edges") +} + +model RunTriggers { + id BigInt @id @default(autoincrement()) + runId BigInt + run Runs @relation(fields: [runId], references: [id], onDelete: Cascade) + trigger String + createdAt DateTime @default(now()) + triggerType RunTriggerType + + @@index([runId]) + @@index([triggerType]) + @@map("run_triggers") +} diff --git a/server/prisma/schema/schema.prisma b/server/prisma/schema/schema.prisma new file mode 100644 index 0000000..755e290 --- /dev/null +++ b/server/prisma/schema/schema.prisma @@ -0,0 +1,18 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + // Direct URL is used for migrations + url = env("DATABASE_URL") + directUrl = env("DATABASE_DIRECT_URL") +} + +